/** * $Id$ * * ***** BEGIN GPL 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. * * 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) 2005 Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ #include #include #include #include "DNA_ID.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "BKE_blender.h" #include "BKE_colortools.h" #include "BKE_node.h" #include "BKE_material.h" #include "BKE_texture.h" #include "BKE_utildefines.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "MEM_guardedalloc.h" #include "RE_shader_ext.h" /* <- ShadeInput Shaderesult TexResult */ /* ********* exec data struct, remains internal *********** */ typedef struct ShaderCallData { ShadeInput *shi; /* from render pipe */ ShadeResult *shr; /* from render pipe */ } ShaderCallData; /* **************** call to switch lamploop for material node ************ */ static void (*node_shader_lamp_loop)(ShadeInput *, ShadeResult *); void set_node_shader_lamp_loop(void (*lamp_loop_func)(ShadeInput *, ShadeResult *)) { node_shader_lamp_loop= lamp_loop_func; } /* ****** */ static void nodestack_get_vec(float *in, short type_in, bNodeStack *ns) { float *from= ns->vec; if(type_in==SOCK_VALUE) { if(ns->sockettype==SOCK_VALUE) *in= *from; else *in= 0.333333f*(from[0]+from[1]+from[2]); } else if(type_in==SOCK_VECTOR) { if(ns->sockettype==SOCK_VALUE) { in[0]= from[0]; in[1]= from[0]; in[2]= from[0]; } else { VECCOPY(in, from); } } else { /* type_in==SOCK_RGBA */ if(ns->sockettype==SOCK_RGBA) { QUATCOPY(in, from); } else if(ns->sockettype==SOCK_VALUE) { in[0]= from[0]; in[1]= from[0]; in[2]= from[0]; in[3]= 1.0f; } else { VECCOPY(in, from); in[3]= 1.0f; } } } /* ******************************************************** */ /* ********* Shader Node type definitions ***************** */ /* ******************************************************** */ /* SocketType syntax: socket type, max connections (0 is no limit), name, 4 values for default, 2 values for range */ /* Verification rule: If name changes, a saved socket and its links will be removed! Type changes are OK */ /* **************** OUTPUT ******************** */ static bNodeSocketType sh_node_output_in[]= { { SOCK_RGBA, 1, "Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 1, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_output(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { if(data) { ShadeInput *shi= ((ShaderCallData *)data)->shi; float col[4]; /* stack order input sockets: col, alpha, normal */ nodestack_get_vec(col, SOCK_VECTOR, in[0]); nodestack_get_vec(col+3, SOCK_VALUE, in[1]); if(shi->do_preview) { nodeAddToPreview(node, col, shi->xs, shi->ys); node->lasty= shi->ys; } if(node->flag & NODE_DO_OUTPUT) { ShadeResult *shr= ((ShaderCallData *)data)->shr; QUATCOPY(shr->combined, col); shr->alpha= col[3]; // VECCOPY(shr->nor, in[3]->vec); } } } static bNodeType sh_node_output= { /* type code */ SH_NODE_OUTPUT, /* name */ "Output", /* width+range */ 80, 60, 200, /* class+opts */ NODE_CLASS_OUTPUT, NODE_PREVIEW, /* input sock */ sh_node_output_in, /* output sock */ NULL, /* storage */ "", /* execfunc */ node_shader_exec_output }; /* **************** GEOMETRY ******************** */ /* output socket defines */ #define GEOM_OUT_GLOB 0 #define GEOM_OUT_LOCAL 1 #define GEOM_OUT_VIEW 2 #define GEOM_OUT_ORCO 3 #define GEOM_OUT_UV 4 #define GEOM_OUT_NORMAL 5 #define GEOM_OUT_VCOL 6 /* output socket type definition */ static bNodeSocketType sh_node_geom_out[]= { { SOCK_VECTOR, 0, "Global", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, /* btw; uses no limit */ { SOCK_VECTOR, 0, "Local", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "View", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "Orco", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "UV", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_RGBA, 0, "Vertex Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; /* node execute callback */ static void node_shader_exec_geom(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { if(data) { ShadeInput *shi= ((ShaderCallData *)data)->shi; NodeGeometry *ngeo= (NodeGeometry*)node->storage; ShadeInputUV *suv= &shi->uv[0]; static float defaultvcol[4] = {1.0f, 1.0f, 1.0f, 1.0f}; int i; if(ngeo->uvname[0]) { /* find uv layer by name */ for(i = 0; i < shi->totuv; i++) { if(strcmp(shi->uv[i].name, ngeo->uvname)==0) { suv= &shi->uv[i]; break; } } } /* out: global, local, view, orco, uv, normal, vertex color */ VECCOPY(out[GEOM_OUT_GLOB]->vec, shi->gl); VECCOPY(out[GEOM_OUT_LOCAL]->vec, shi->co); VECCOPY(out[GEOM_OUT_VIEW]->vec, shi->view); VECCOPY(out[GEOM_OUT_ORCO]->vec, shi->lo); VECCOPY(out[GEOM_OUT_UV]->vec, suv->uv); VECCOPY(out[GEOM_OUT_NORMAL]->vec, shi->vno); if (shi->totcol) { /* find vertex color layer by name */ ShadeInputCol *scol= &shi->col[0]; if(ngeo->colname[0]) { for(i = 0; i < shi->totcol; i++) { if(strcmp(shi->col[i].name, ngeo->colname)==0) { scol= &shi->col[i]; break; } } } VECCOPY(out[GEOM_OUT_VCOL]->vec, scol->col); out[GEOM_OUT_VCOL]->vec[3]= 1.0f; } else memcpy(out[GEOM_OUT_VCOL]->vec, defaultvcol, sizeof(defaultvcol)); if(shi->osatex) { out[GEOM_OUT_GLOB]->data= shi->dxgl; out[GEOM_OUT_GLOB]->datatype= NS_OSA_VECTORS; out[GEOM_OUT_LOCAL]->data= shi->dxco; out[GEOM_OUT_LOCAL]->datatype= NS_OSA_VECTORS; out[GEOM_OUT_VIEW]->data= &shi->dxview; out[GEOM_OUT_VIEW]->datatype= NS_OSA_VALUES; out[GEOM_OUT_ORCO]->data= shi->dxlo; out[GEOM_OUT_ORCO]->datatype= NS_OSA_VECTORS; out[GEOM_OUT_UV]->data= suv->dxuv; out[GEOM_OUT_UV]->datatype= NS_OSA_VECTORS; out[GEOM_OUT_NORMAL]->data= shi->dxno; out[GEOM_OUT_NORMAL]->datatype= NS_OSA_VECTORS; } } } /* node type definition */ static bNodeType sh_node_geom= { /* type code */ SH_NODE_GEOMETRY, /* name */ "Geometry", /* width+range */ 120, 80, 160, /* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS, /* input sock */ NULL, /* output sock */ sh_node_geom_out, /* storage */ "NodeGeometry", /* execfunc */ node_shader_exec_geom }; /* **************** MATERIAL ******************** */ /* input socket defines */ #define MAT_IN_COLOR 0 #define MAT_IN_SPEC 1 #define MAT_IN_REFL 2 #define MAT_IN_NORMAL 3 static bNodeSocketType sh_node_material_in[]= { { SOCK_RGBA, 1, "Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_RGBA, 1, "Spec", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 1, "Refl", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VECTOR, 1, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; /* output socket defines */ #define MAT_OUT_COLOR 0 #define MAT_OUT_ALPHA 1 #define MAT_OUT_NORMAL 2 static bNodeSocketType sh_node_material_out[]= { { SOCK_RGBA, 0, "Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 0, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_material(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { if(data && node->id) { ShadeResult shrnode; ShadeInput *shi; ShaderCallData *shcd= data; float col[4]; shi= shcd->shi; shi->mat= (Material *)node->id; /* copy all relevant material vars, note, keep this synced with render_types.h */ memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); shi->har= shi->mat->har; /* write values */ if(in[MAT_IN_COLOR]->hasinput) nodestack_get_vec(&shi->r, SOCK_VECTOR, in[MAT_IN_COLOR]); if(in[MAT_IN_SPEC]->hasinput) nodestack_get_vec(&shi->specr, SOCK_VECTOR, in[MAT_IN_SPEC]); if(in[MAT_IN_REFL]->hasinput) nodestack_get_vec(&shi->refl, SOCK_VALUE, in[MAT_IN_REFL]); /* retrieve normal */ if(in[MAT_IN_NORMAL]->hasinput) { nodestack_get_vec(shi->vn, SOCK_VECTOR, in[MAT_IN_NORMAL]); Normalise(shi->vn); } else VECCOPY(shi->vn, shi->vno); /* custom option to flip normal */ if(node->custom1 & SH_NODE_MAT_NEG) { shi->vn[0]= -shi->vn[0]; shi->vn[1]= -shi->vn[1]; shi->vn[2]= -shi->vn[2]; } node_shader_lamp_loop(shi, &shrnode); /* clears shrnode */ /* write to outputs */ if(node->custom1 & SH_NODE_MAT_DIFF) { VECCOPY(col, shrnode.combined); if(!(node->custom1 & SH_NODE_MAT_SPEC)) { VecSubf(col, col, shrnode.spec); } } else if(node->custom1 & SH_NODE_MAT_SPEC) { VECCOPY(col, shrnode.spec); } else col[0]= col[1]= col[2]= 0.0f; col[3]= shrnode.alpha; if(shi->do_preview) nodeAddToPreview(node, col, shi->xs, shi->ys); VECCOPY(out[MAT_OUT_COLOR]->vec, col); out[MAT_OUT_ALPHA]->vec[0]= shrnode.alpha; if(node->custom1 & SH_NODE_MAT_NEG) { shi->vn[0]= -shi->vn[0]; shi->vn[1]= -shi->vn[1]; shi->vn[2]= -shi->vn[2]; } VECCOPY(out[MAT_OUT_NORMAL]->vec, shi->vn); /* copy passes, now just active node */ if(node->flag & NODE_ACTIVE_ID) *(shcd->shr)= shrnode; } } static bNodeType sh_node_material= { /* type code */ SH_NODE_MATERIAL, /* name */ "Material", /* width+range */ 120, 80, 240, /* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS|NODE_PREVIEW, /* input sock */ sh_node_material_in, /* output sock */ sh_node_material_out, /* storage */ "", /* execfunc */ node_shader_exec_material }; /* **************** TEXTURE ******************** */ static bNodeSocketType sh_node_texture_in[]= { { SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, /* no limit */ { -1, 0, "" } }; static bNodeSocketType sh_node_texture_out[]= { { SOCK_VALUE, 0, "Value", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_RGBA , 0, "Color", 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_texture(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { if(data && node->id) { ShadeInput *shi= ((ShaderCallData *)data)->shi; TexResult texres; float vec[3], nor[3]={0.0f, 0.0f, 0.0f}; int retval; /* out: value, color, normal */ /* we should find out if a normal as output is needed, for now we do all */ texres.nor= nor; if(in[0]->hasinput) { nodestack_get_vec(vec, SOCK_VECTOR, in[0]); if(in[0]->datatype==NS_OSA_VECTORS) { float *fp= in[0]->data; retval= multitex_ext((Tex *)node->id, vec, fp, fp+3, shi->osatex, &texres); } else if(in[0]->datatype==NS_OSA_VALUES) { float *fp= in[0]->data; float dxt[3], dyt[3]; dxt[0]= fp[0]; dxt[1]= dxt[2]= 0.0f; dyt[0]= fp[1]; dyt[1]= dyt[2]= 0.0f; retval= multitex_ext((Tex *)node->id, vec, dxt, dyt, shi->osatex, &texres); } else retval= multitex_ext((Tex *)node->id, vec, NULL, NULL, 0, &texres); } else { /* only for previewrender, so we see stuff */ VECCOPY(vec, shi->lo); retval= multitex_ext((Tex *)node->id, vec, NULL, NULL, 0, &texres); } /* stupid exception */ if( ((Tex *)node->id)->type==TEX_STUCCI) { texres.tin= 0.5f + 0.7f*texres.nor[0]; CLAMP(texres.tin, 0.0f, 1.0f); } /* intensity and color need some handling */ if(texres.talpha) out[0]->vec[0]= texres.ta; else out[0]->vec[0]= texres.tin; if((retval & TEX_RGB)==0) { out[1]->vec[0]= out[0]->vec[0]; out[1]->vec[1]= out[0]->vec[0]; out[1]->vec[2]= out[0]->vec[0]; out[1]->vec[3]= 1.0f; } else { out[1]->vec[0]= texres.tr; out[1]->vec[1]= texres.tg; out[1]->vec[2]= texres.tb; out[1]->vec[3]= 1.0f; } VECCOPY(out[2]->vec, nor); if(shi->do_preview) nodeAddToPreview(node, out[1]->vec, shi->xs, shi->ys); } } static bNodeType sh_node_texture= { /* type code */ SH_NODE_TEXTURE, /* name */ "Texture", /* width+range */ 120, 80, 240, /* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS|NODE_PREVIEW, /* input sock */ sh_node_texture_in, /* output sock */ sh_node_texture_out, /* storage */ "", /* execfunc */ node_shader_exec_texture }; /* **************** MAPPING ******************** */ static bNodeSocketType sh_node_mapping_in[]= { { SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_mapping_out[]= { { SOCK_VECTOR, 0, "Vector", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; /* do the regular mapping options for blender textures */ static void node_shader_exec_mapping(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { TexMapping *texmap= node->storage; float *vec= out[0]->vec; /* stack order input: vector */ /* stack order output: vector */ nodestack_get_vec(vec, SOCK_VECTOR, in[0]); Mat4MulVecfl(texmap->mat, vec); if(texmap->flag & TEXMAP_CLIP_MIN) { if(vec[0]min[0]) vec[0]= texmap->min[0]; if(vec[1]min[1]) vec[1]= texmap->min[1]; if(vec[2]min[2]) vec[2]= texmap->min[2]; } if(texmap->flag & TEXMAP_CLIP_MAX) { if(vec[0]>texmap->max[0]) vec[0]= texmap->max[0]; if(vec[1]>texmap->max[1]) vec[1]= texmap->max[1]; if(vec[2]>texmap->max[2]) vec[2]= texmap->max[2]; } } static bNodeType sh_node_mapping= { /* type code */ SH_NODE_MAPPING, /* name */ "Mapping", /* width+range */ 240, 160, 320, /* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS, /* input sock */ sh_node_mapping_in, /* output sock */ sh_node_mapping_out, /* storage */ "TexMapping", /* execfunc */ node_shader_exec_mapping }; /* **************** CAMERA INFO ******************** */ static bNodeSocketType sh_node_camera_out[]= { { SOCK_VECTOR, 0, "View Vector", 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f}, /* None of these actually */ { SOCK_VALUE, 0, "View Z Depth", 0.f, 0.0f, 0.0f, 0.0f, 0.0f, 99999999999.0f}, /* have any limits on their */ { SOCK_VALUE, 0, "View Distance", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 99999999999.0f}, /* values. */ { -1, 0, "" } }; static void node_shader_exec_camera(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { if(data) { ShadeInput *shi= ((ShaderCallData *)data)->shi; /* Data we need for shading. */ VECCOPY(out[0]->vec, shi->co); /* get view vector */ out[1]->vec[0]= fabs(shi->co[2]); /* get view z-depth */ out[2]->vec[0]= Normalise(out[0]->vec); /* get view distance */ } } static bNodeType sh_node_camera= { /* type code */ SH_NODE_CAMERA, /* name */ "Camera Data", /* width+range */ 95, 95, 120, /* class+opts */ NODE_CLASS_INPUT, 0, /* input sock */ NULL, /* output sock */ sh_node_camera_out, /* storage */ "node_camera", /* execfunc */ node_shader_exec_camera }; /* **************** SCALAR MATH ******************** */ static bNodeSocketType sh_node_math_in[]= { { SOCK_VALUE, 1, "Value", 0.5f, 0.5f, 0.5f, 1.0f, -100.0f, 100.0f}, { SOCK_VALUE, 1, "Value", 0.5f, 0.5f, 0.5f, 1.0f, -100.0f, 100.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_math_out[]= { { SOCK_VALUE, 0, "Value", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_math(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { switch(node->custom1){ case 0: /* Add */ out[0]->vec[0]= in[0]->vec[0] + in[1]->vec[0]; break; case 1: /* Subtract */ out[0]->vec[0]= in[0]->vec[0] - in[1]->vec[0]; break; case 2: /* Multiply */ out[0]->vec[0]= in[0]->vec[0] * in[1]->vec[0]; break; case 3: /* Divide */ { if(in[1]->vec[0]==0) /* We don't want to divide by zero. */ out[0]->vec[0]= 0.0; else out[0]->vec[0]= in[0]->vec[0] / in[1]->vec[0]; } break; case 4: /* Sine */ { if(in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= sin(in[0]->vec[0]); else out[0]->vec[0]= sin(in[1]->vec[0]); } break; case 5: /* Cosine */ { if(in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= cos(in[0]->vec[0]); else out[0]->vec[0]= cos(in[1]->vec[0]); } break; case 6: /* Tangent */ { if(in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= tan(in[0]->vec[0]); else out[0]->vec[0]= tan(in[1]->vec[0]); } break; case 7: /* Arc-Sine */ { if(in[0]->hasinput || !in[1]->hasinput) { /* This one only takes one input, so we've got to choose. */ /* Can't do the impossible... */ if( in[0]->vec[0] <= 1 && in[0]->vec[0] >= -1 ) out[0]->vec[0]= asin(in[0]->vec[0]); else out[0]->vec[0]= 0.0; } else { /* Can't do the impossible... */ if( in[1]->vec[0] <= 1 && in[1]->vec[0] >= -1 ) out[0]->vec[0]= asin(in[1]->vec[0]); else out[0]->vec[0]= 0.0; } } break; case 8: /* Arc-Cosine */ { if(in[0]->hasinput || !in[1]->hasinput) { /* This one only takes one input, so we've got to choose. */ /* Can't do the impossible... */ if( in[0]->vec[0] <= 1 && in[0]->vec[0] >= -1 ) out[0]->vec[0]= acos(in[0]->vec[0]); else out[0]->vec[0]= 0.0; } else { /* Can't do the impossible... */ if( in[1]->vec[0] <= 1 && in[1]->vec[0] >= -1 ) out[0]->vec[0]= acos(in[1]->vec[0]); else out[0]->vec[0]= 0.0; } } break; case 9: /* Arc-Tangent */ { if(in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= atan(in[0]->vec[0]); else out[0]->vec[0]= atan(in[1]->vec[0]); } break; case 10: /* Power */ { /* Don't want any imaginary numbers... */ if( in[0]->vec[0] >= 0 ) out[0]->vec[0]= pow(in[0]->vec[0], in[1]->vec[0]); else out[0]->vec[0]= 0.0; } break; case 11: /* Logarithm */ { /* Don't want any imaginary numbers... */ if( in[0]->vec[0] > 0 && in[1]->vec[0] > 0 ) out[0]->vec[0]= log(in[0]->vec[0]) / log(in[1]->vec[0]); else out[0]->vec[0]= 0.0; } break; case 12: /* Minimum */ { if( in[0]->vec[0] < in[1]->vec[0] ) out[0]->vec[0]= in[0]->vec[0]; else out[0]->vec[0]= in[1]->vec[0]; } break; case 13: /* Maximum */ { if( in[0]->vec[0] > in[1]->vec[0] ) out[0]->vec[0]= in[0]->vec[0]; else out[0]->vec[0]= in[1]->vec[0]; } break; case 14: /* Round */ { if(in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= (int)(in[0]->vec[0] + 0.5f); else out[0]->vec[0]= (int)(in[1]->vec[0] + 0.5f); } break; } } static bNodeType sh_node_math= { /* type code */ SH_NODE_MATH, /* name */ "Math", /* width+range */ 120, 110, 160, /* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS, /* input sock */ sh_node_math_in, /* output sock */ sh_node_math_out, /* storage */ "node_math", /* execfunc */ node_shader_exec_math }; /* **************** VALUE SQUEEZE ******************** */ static bNodeSocketType sh_node_squeeze_in[]= { { SOCK_VALUE, 1, "Value", 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f}, { SOCK_VALUE, 1, "Width", 1.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f}, { SOCK_VALUE, 1, "Center", 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_squeeze_out[]= { { SOCK_VALUE, 0, "Value", 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_squeeze(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { float vec[3]; nodestack_get_vec(vec, SOCK_VALUE, in[0]); nodestack_get_vec(vec+1, SOCK_VALUE, in[1]); nodestack_get_vec(vec+2, SOCK_VALUE, in[2]); out[0]->vec[0] = 1.0f / (1.0f + pow(2.71828183,-((vec[0]-vec[2])*vec[1]))) ; } static bNodeType sh_node_squeeze= { /* type code */ SH_NODE_SQUEEZE, /* name */ "Squeeze Value", /* width+range */ 120, 110, 160, /* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS, /* input sock */ sh_node_squeeze_in, /* output sock */ sh_node_squeeze_out, /* storage */ "node_squeeze", /* execfunc */ node_shader_exec_squeeze }; /* **************** VECTOR MATH ******************** */ static bNodeSocketType sh_node_vect_math_in[]= { { SOCK_VECTOR, 1, "Vector", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { SOCK_VECTOR, 1, "Vector", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_vect_math_out[]= { { SOCK_VECTOR, 0, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 0, "Value", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_vect_math(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { float vec1[3], vec2[3]; nodestack_get_vec(vec1, SOCK_VECTOR, in[0]); nodestack_get_vec(vec2, SOCK_VECTOR, in[1]); if(node->custom1 == 0) { /* Add */ out[0]->vec[0]= vec1[0] + vec2[0]; out[0]->vec[1]= vec1[1] + vec2[1]; out[0]->vec[2]= vec1[2] + vec2[2]; out[1]->vec[0]= (fabs(out[0]->vec[0]) + fabs(out[0]->vec[0]) + fabs(out[0]->vec[0])) / 3; } else if(node->custom1 == 1) { /* Subtract */ out[0]->vec[0]= vec1[0] - vec2[0]; out[0]->vec[1]= vec1[1] - vec2[1]; out[0]->vec[2]= vec1[2] - vec2[2]; out[1]->vec[0]= (fabs(out[0]->vec[0]) + fabs(out[0]->vec[0]) + fabs(out[0]->vec[0])) / 3; } else if(node->custom1 == 2) { /* Average */ out[0]->vec[0]= vec1[0] + vec2[0]; out[0]->vec[1]= vec1[1] + vec2[1]; out[0]->vec[2]= vec1[2] + vec2[2]; out[1]->vec[0] = Normalise( out[0]->vec ); } else if(node->custom1 == 3) { /* Dot product */ out[1]->vec[0]= (vec1[0] * vec2[0]) + (vec1[1] * vec2[1]) + (vec1[2] * vec2[2]); } else if(node->custom1 == 4) { /* Cross product */ out[0]->vec[0]= (vec1[1] * vec2[2]) - (vec1[2] * vec2[1]); out[0]->vec[1]= (vec1[2] * vec2[0]) - (vec1[0] * vec2[2]); out[0]->vec[2]= (vec1[0] * vec2[1]) - (vec1[1] * vec2[0]); out[1]->vec[0] = Normalise( out[0]->vec ); } else if(node->custom1 == 5) { /* Normalize */ if(in[0]->hasinput || !in[1]->hasinput) { /* This one only takes one input, so we've got to choose. */ out[0]->vec[0]= vec1[0]; out[0]->vec[1]= vec1[1]; out[0]->vec[2]= vec1[2]; } else { out[0]->vec[0]= vec2[0]; out[0]->vec[1]= vec2[1]; out[0]->vec[2]= vec2[2]; } out[1]->vec[0] = Normalise( out[0]->vec ); } } static bNodeType sh_node_vect_math= { /* type code */ SH_NODE_VECT_MATH, /* name */ "Vector Math", /* width+range */ 80, 75, 140, /* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS, /* input sock */ sh_node_vect_math_in, /* output sock */ sh_node_vect_math_out, /* storage */ "node_vect_math", /* execfunc */ node_shader_exec_vect_math }; /* **************** NORMAL ******************** */ static bNodeSocketType sh_node_normal_in[]= { { SOCK_VECTOR, 1, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_normal_out[]= { { SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VALUE, 0, "Dot", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; /* generates normal, does dot product */ static void node_shader_exec_normal(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { bNodeSocket *sock= node->outputs.first; float vec[3]; /* stack order input: normal */ /* stack order output: normal, value */ nodestack_get_vec(vec, SOCK_VECTOR, in[0]); VECCOPY(out[0]->vec, sock->ns.vec); /* render normals point inside... the widget points outside */ out[1]->vec[0]= -INPR(out[0]->vec, vec); } static bNodeType sh_node_normal= { /* type code */ SH_NODE_NORMAL, /* name */ "Normal", /* width+range */ 100, 60, 200, /* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS, /* input sock */ sh_node_normal_in, /* output sock */ sh_node_normal_out, /* storage */ "", /* execfunc */ node_shader_exec_normal }; /* **************** CURVE VEC ******************** */ static bNodeSocketType sh_node_curve_vec_in[]= { { SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_curve_vec_out[]= { { SOCK_VECTOR, 0, "Vector", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_curve_vec(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { float vec[3]; /* stack order input: vec */ /* stack order output: vec */ nodestack_get_vec(vec, SOCK_VECTOR, in[0]); curvemapping_evaluate3F(node->storage, out[0]->vec, vec); } static bNodeType sh_node_curve_vec= { /* type code */ SH_NODE_CURVE_VEC, /* name */ "Vector Curves", /* width+range */ 200, 140, 320, /* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS, /* input sock */ sh_node_curve_vec_in, /* output sock */ sh_node_curve_vec_out, /* storage */ "CurveMapping", /* execfunc */ node_shader_exec_curve_vec }; /* **************** CURVE RGB ******************** */ static bNodeSocketType sh_node_curve_rgb_in[]= { { SOCK_RGBA, 1, "Color", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_curve_rgb_out[]= { { SOCK_RGBA, 0, "Color", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_curve_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { float vec[3]; /* stack order input: vec */ /* stack order output: vec */ nodestack_get_vec(vec, SOCK_VECTOR, in[0]); curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec); } static bNodeType sh_node_curve_rgb= { /* type code */ SH_NODE_CURVE_RGB, /* name */ "RGB Curves", /* width+range */ 200, 140, 320, /* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS, /* input sock */ sh_node_curve_rgb_in, /* output sock */ sh_node_curve_rgb_out, /* storage */ "CurveMapping", /* execfunc */ node_shader_exec_curve_rgb }; /* **************** VALUE ******************** */ static bNodeSocketType sh_node_value_out[]= { { SOCK_VALUE, 0, "Value", 0.5f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f}, { -1, 0, "" } }; static void node_shader_exec_value(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { bNodeSocket *sock= node->outputs.first; out[0]->vec[0]= sock->ns.vec[0]; } static bNodeType sh_node_value= { /* type code */ SH_NODE_VALUE, /* name */ "Value", /* width+range */ 80, 50, 120, /* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS, /* input sock */ NULL, /* output sock */ sh_node_value_out, /* storage */ "", /* execfunc */ node_shader_exec_value }; /* **************** RGB ******************** */ static bNodeSocketType sh_node_rgb_out[]= { { SOCK_RGBA, 0, "Color", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { bNodeSocket *sock= node->outputs.first; VECCOPY(out[0]->vec, sock->ns.vec); } static bNodeType sh_node_rgb= { /* type code */ SH_NODE_RGB, /* name */ "RGB", /* width+range */ 100, 60, 140, /* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS, /* input sock */ NULL, /* output sock */ sh_node_rgb_out, /* storage */ "", /* execfunc */ node_shader_exec_rgb }; /* **************** MIX RGB ******************** */ static bNodeSocketType sh_node_mix_rgb_in[]= { { SOCK_VALUE, 1, "Fac", 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_RGBA, 1, "Color1", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { SOCK_RGBA, 1, "Color2", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_mix_rgb_out[]= { { SOCK_RGBA, 0, "Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_mix_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { /* stack order in: fac, col1, col2 */ /* stack order out: col */ float col[3]; float fac; float vec[3]; nodestack_get_vec(&fac, SOCK_VALUE, in[0]); CLAMP(fac, 0.0f, 1.0f); nodestack_get_vec(col, SOCK_VECTOR, in[1]); nodestack_get_vec(vec, SOCK_VECTOR, in[2]); ramp_blend(node->custom1, col, col+1, col+2, fac, vec); VECCOPY(out[0]->vec, col); } static bNodeType sh_node_mix_rgb= { /* type code */ SH_NODE_MIX_RGB, /* name */ "Mix", /* width+range */ 100, 60, 150, /* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS, /* input sock */ sh_node_mix_rgb_in, /* output sock */ sh_node_mix_rgb_out, /* storage */ "", /* execfunc */ node_shader_exec_mix_rgb }; /* **************** VALTORGB ******************** */ static bNodeSocketType sh_node_valtorgb_in[]= { { SOCK_VALUE, 1, "Fac", 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_valtorgb_out[]= { { SOCK_RGBA, 0, "Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 0, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_valtorgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { /* stack order in: fac */ /* stack order out: col, alpha */ if(node->storage) { float fac; nodestack_get_vec(&fac, SOCK_VALUE, in[0]); do_colorband(node->storage, fac, out[0]->vec); out[1]->vec[0]= out[0]->vec[3]; } } static bNodeType sh_node_valtorgb= { /* type code */ SH_NODE_VALTORGB, /* name */ "ColorRamp", /* width+range */ 240, 200, 300, /* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS, /* input sock */ sh_node_valtorgb_in, /* output sock */ sh_node_valtorgb_out, /* storage */ "ColorBand", /* execfunc */ node_shader_exec_valtorgb }; /* **************** RGBTOBW ******************** */ static bNodeSocketType sh_node_rgbtobw_in[]= { { SOCK_RGBA, 1, "Color", 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static bNodeSocketType sh_node_rgbtobw_out[]= { { SOCK_VALUE, 0, "Val", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { -1, 0, "" } }; static void node_shader_exec_rgbtobw(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { /* stack order out: bw */ /* stack order in: col */ out[0]->vec[0]= in[0]->vec[0]*0.35f + in[0]->vec[1]*0.45f + in[0]->vec[2]*0.2f; } static bNodeType sh_node_rgbtobw= { /* type code */ SH_NODE_RGBTOBW, /* name */ "RGB to BW", /* width+range */ 80, 40, 120, /* class+opts */ NODE_CLASS_CONVERTOR, 0, /* input sock */ sh_node_rgbtobw_in, /* output sock */ sh_node_rgbtobw_out, /* storage */ "", /* execfunc */ node_shader_exec_rgbtobw }; /* ****************** types array for all shaders ****************** */ bNodeType *node_all_shaders[]= { &node_group_typeinfo, &sh_node_output, &sh_node_material, &sh_node_camera, &sh_node_value, &sh_node_rgb, &sh_node_mix_rgb, &sh_node_valtorgb, &sh_node_rgbtobw, &sh_node_texture, &sh_node_normal, &sh_node_geom, &sh_node_mapping, &sh_node_curve_vec, &sh_node_curve_rgb, &sh_node_math, &sh_node_vect_math, &sh_node_squeeze, NULL }; /* ******************* execute and parse ************ */ void ntreeShaderExecTree(bNodeTree *ntree, ShadeInput *shi, ShadeResult *shr) { ShaderCallData scd; /* convert caller data to struct */ scd.shi= shi; scd.shr= shr; /* each material node has own local shaderesult, with optional copying */ memset(shr, 0, sizeof(ShadeResult)); ntreeExecTree(ntree, &scd, shi->thread); /* threads */ /* better not allow negative for now */ if(shr->combined[0]<0.0f) shr->combined[0]= 0.0f; if(shr->combined[1]<0.0f) shr->combined[1]= 0.0f; if(shr->combined[2]<0.0f) shr->combined[2]= 0.0f; } /* go over all used Geometry and Texture nodes, and return a texco flag */ /* no group inside needed, this function is called for groups too */ void ntreeShaderGetTexcoMode(bNodeTree *ntree, int r_mode, short *texco, int *mode) { bNode *node; bNodeSocket *sock; int a; ntreeSocketUseFlags(ntree); for(node= ntree->nodes.first; node; node= node->next) { if(node->type==SH_NODE_TEXTURE) { if((r_mode & R_OSA) && node->id) { Tex *tex= (Tex *)node->id; if ELEM3(tex->type, TEX_IMAGE, TEX_PLUGIN, TEX_ENVMAP) *texco |= TEXCO_OSA|NEED_UV; } /* usability exception... without input we still give the node orcos */ sock= node->inputs.first; if(sock==NULL || sock->link==NULL) *texco |= TEXCO_ORCO|NEED_UV; } else if(node->type==SH_NODE_GEOMETRY) { /* note; sockets always exist for the given type! */ for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) { if(sock->flag & SOCK_IN_USE) { switch(a) { case GEOM_OUT_GLOB: *texco |= TEXCO_GLOB|NEED_UV; break; case GEOM_OUT_VIEW: *texco |= TEXCO_VIEW|NEED_UV; break; case GEOM_OUT_ORCO: *texco |= TEXCO_ORCO|NEED_UV; break; case GEOM_OUT_UV: *texco |= TEXCO_UV|NEED_UV; break; case GEOM_OUT_NORMAL: *texco |= TEXCO_NORM|NEED_UV; break; case GEOM_OUT_VCOL: *texco |= NEED_UV; *mode |= MA_VERTEXCOL; break; } } } } } } /* nodes that use ID data get synced with local data */ void nodeShaderSynchronizeID(bNode *node, int copyto) { if(node->id==NULL) return; if(node->type==SH_NODE_MATERIAL) { bNodeSocket *sock; Material *ma= (Material *)node->id; int a; /* hrmf, case in loop isnt super fast, but we dont edit 100s of material at same time either! */ for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) { if(!(sock->flag & SOCK_HIDDEN)) { if(copyto) { switch(a) { case MAT_IN_COLOR: VECCOPY(&ma->r, sock->ns.vec); break; case MAT_IN_SPEC: VECCOPY(&ma->specr, sock->ns.vec); break; case MAT_IN_REFL: ma->ref= sock->ns.vec[0]; break; } } else { switch(a) { case MAT_IN_COLOR: VECCOPY(sock->ns.vec, &ma->r); break; case MAT_IN_SPEC: VECCOPY(sock->ns.vec, &ma->specr); break; case MAT_IN_REFL: sock->ns.vec[0]= ma->ref; break; } } } } } }