1722 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1722 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | 
						|
 *
 | 
						|
 * The Original Code is Copyright (C) 2005 Blender Foundation.
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * The Original Code is: all of this file.
 | 
						|
 *
 | 
						|
 * Contributor(s): Brecht Van Lommel.
 | 
						|
 *
 | 
						|
 * ***** END GPL LICENSE BLOCK *****
 | 
						|
 */
 | 
						|
 | 
						|
/** \file blender/gpu/intern/gpu_codegen.c
 | 
						|
 *  \ingroup gpu
 | 
						|
 *
 | 
						|
 * Convert material node-trees to GLSL.
 | 
						|
 */
 | 
						|
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
#include "DNA_customdata_types.h"
 | 
						|
#include "DNA_image_types.h"
 | 
						|
#include "DNA_material_types.h"
 | 
						|
 | 
						|
#include "BLI_blenlib.h"
 | 
						|
#include "BLI_utildefines.h"
 | 
						|
#include "BLI_dynstr.h"
 | 
						|
#include "BLI_ghash.h"
 | 
						|
 | 
						|
#include "GPU_extensions.h"
 | 
						|
#include "GPU_glew.h"
 | 
						|
#include "GPU_material.h"
 | 
						|
#include "GPU_shader.h"
 | 
						|
#include "GPU_texture.h"
 | 
						|
 | 
						|
#include "BLI_sys_types.h" /* for intptr_t support */
 | 
						|
 | 
						|
#include "gpu_codegen.h"
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <stdarg.h>
 | 
						|
 | 
						|
extern char datatoc_gpu_shader_material_glsl[];
 | 
						|
extern char datatoc_gpu_shader_vertex_glsl[];
 | 
						|
extern char datatoc_gpu_shader_vertex_world_glsl[];
 | 
						|
extern char datatoc_gpu_shader_geometry_glsl[];
 | 
						|
 | 
						|
static char *glsl_material_library = NULL;
 | 
						|
 | 
						|
 | 
						|
/* type definitions and constants */
 | 
						|
 | 
						|
enum {
 | 
						|
	MAX_FUNCTION_NAME = 64
 | 
						|
};
 | 
						|
enum {
 | 
						|
	MAX_PARAMETER = 32
 | 
						|
};
 | 
						|
 | 
						|
typedef enum {
 | 
						|
	FUNCTION_QUAL_IN,
 | 
						|
	FUNCTION_QUAL_OUT,
 | 
						|
	FUNCTION_QUAL_INOUT
 | 
						|
} GPUFunctionQual;
 | 
						|
 | 
						|
typedef struct GPUFunction {
 | 
						|
	char name[MAX_FUNCTION_NAME];
 | 
						|
	GPUType paramtype[MAX_PARAMETER];
 | 
						|
	GPUFunctionQual paramqual[MAX_PARAMETER];
 | 
						|
	int totparam;
 | 
						|
} GPUFunction;
 | 
						|
 | 
						|
/* Indices match the GPUType enum */
 | 
						|
static const char *GPU_DATATYPE_STR[17] = {"", "float", "vec2", "vec3", "vec4",
 | 
						|
	NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4"};
 | 
						|
 | 
						|
/* GLSL code parsing for finding function definitions.
 | 
						|
 * These are stored in a hash for lookup when creating a material. */
 | 
						|
 | 
						|
static GHash *FUNCTION_HASH = NULL;
 | 
						|
#if 0
 | 
						|
static char *FUNCTION_PROTOTYPES = NULL;
 | 
						|
static GPUShader *FUNCTION_LIB = NULL;
 | 
						|
#endif
 | 
						|
 | 
						|
static int gpu_str_prefix(const char *str, const char *prefix)
 | 
						|
{
 | 
						|
	while (*str && *prefix) {
 | 
						|
		if (*str != *prefix)
 | 
						|
			return 0;
 | 
						|
 | 
						|
		str++;
 | 
						|
		prefix++;
 | 
						|
	}
 | 
						|
	
 | 
						|
	return (*prefix == '\0');
 | 
						|
}
 | 
						|
 | 
						|
static char *gpu_str_skip_token(char *str, char *token, int max)
 | 
						|
{
 | 
						|
	int len = 0;
 | 
						|
 | 
						|
	/* skip a variable/function name */
 | 
						|
	while (*str) {
 | 
						|
		if (ELEM(*str, ' ', '(', ')', ',', '\t', '\n', '\r'))
 | 
						|
			break;
 | 
						|
		else {
 | 
						|
			if (token && len < max - 1) {
 | 
						|
				*token = *str;
 | 
						|
				token++;
 | 
						|
				len++;
 | 
						|
			}
 | 
						|
			str++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (token)
 | 
						|
		*token = '\0';
 | 
						|
 | 
						|
	/* skip the next special characters:
 | 
						|
	 * note the missing ')' */
 | 
						|
	while (*str) {
 | 
						|
		if (ELEM(*str, ' ', '(', ',', '\t', '\n', '\r'))
 | 
						|
			str++;
 | 
						|
		else
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	return str;
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_parse_functions_string(GHash *hash, char *code)
 | 
						|
{
 | 
						|
	GPUFunction *function;
 | 
						|
	GPUType type;
 | 
						|
	GPUFunctionQual qual;
 | 
						|
	int i;
 | 
						|
 | 
						|
	while ((code = strstr(code, "void "))) {
 | 
						|
		function = MEM_callocN(sizeof(GPUFunction), "GPUFunction");
 | 
						|
 | 
						|
		code = gpu_str_skip_token(code, NULL, 0);
 | 
						|
		code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME);
 | 
						|
 | 
						|
		/* get parameters */
 | 
						|
		while (*code && *code != ')') {
 | 
						|
			/* test if it's an input or output */
 | 
						|
			qual = FUNCTION_QUAL_IN;
 | 
						|
			if (gpu_str_prefix(code, "out "))
 | 
						|
				qual = FUNCTION_QUAL_OUT;
 | 
						|
			if (gpu_str_prefix(code, "inout "))
 | 
						|
				qual = FUNCTION_QUAL_INOUT;
 | 
						|
			if ((qual != FUNCTION_QUAL_IN) || gpu_str_prefix(code, "in "))
 | 
						|
				code = gpu_str_skip_token(code, NULL, 0);
 | 
						|
 | 
						|
			/* test for type */
 | 
						|
			type = GPU_NONE;
 | 
						|
			for (i = 1; i <= 16; i++) {
 | 
						|
				if (GPU_DATATYPE_STR[i] && gpu_str_prefix(code, GPU_DATATYPE_STR[i])) {
 | 
						|
					type = i;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (!type && gpu_str_prefix(code, "samplerCube")) {
 | 
						|
				type = GPU_TEXCUBE;
 | 
						|
			}
 | 
						|
			if (!type && gpu_str_prefix(code, "sampler2DShadow")) {
 | 
						|
				type = GPU_SHADOW2D;
 | 
						|
			}
 | 
						|
			if (!type && gpu_str_prefix(code, "sampler2D")) {
 | 
						|
				type = GPU_TEX2D;
 | 
						|
			}
 | 
						|
 | 
						|
			if (type) {
 | 
						|
				/* add parameter */
 | 
						|
				code = gpu_str_skip_token(code, NULL, 0);
 | 
						|
				code = gpu_str_skip_token(code, NULL, 0);
 | 
						|
				function->paramqual[function->totparam] = qual;
 | 
						|
				function->paramtype[function->totparam] = type;
 | 
						|
				function->totparam++;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				fprintf(stderr, "GPU invalid function parameter in %s.\n", function->name);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (function->name[0] == '\0' || function->totparam == 0) {
 | 
						|
			fprintf(stderr, "GPU functions parse error.\n");
 | 
						|
			MEM_freeN(function);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		BLI_ghash_insert(hash, function->name, function);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
static char *gpu_generate_function_prototyps(GHash *hash)
 | 
						|
{
 | 
						|
	DynStr *ds = BLI_dynstr_new();
 | 
						|
	GHashIterator *ghi;
 | 
						|
	GPUFunction *function;
 | 
						|
	char *name, *prototypes;
 | 
						|
	int a;
 | 
						|
	
 | 
						|
	/* automatically generate function prototypes to add to the top of the
 | 
						|
	 * generated code, to avoid have to add the actual code & recompile all */
 | 
						|
	ghi = BLI_ghashIterator_new(hash);
 | 
						|
 | 
						|
	for (; !BLI_ghashIterator_done(ghi); BLI_ghashIterator_step(ghi)) {
 | 
						|
		name = BLI_ghashIterator_getValue(ghi);
 | 
						|
		function = BLI_ghashIterator_getValue(ghi);
 | 
						|
 | 
						|
		BLI_dynstr_appendf(ds, "void %s(", name);
 | 
						|
		for (a = 0; a < function->totparam; a++) {
 | 
						|
			if (function->paramqual[a] == FUNCTION_QUAL_OUT)
 | 
						|
				BLI_dynstr_append(ds, "out ");
 | 
						|
			else if (function->paramqual[a] == FUNCTION_QUAL_INOUT)
 | 
						|
				BLI_dynstr_append(ds, "inout ");
 | 
						|
 | 
						|
			if (function->paramtype[a] == GPU_TEX2D)
 | 
						|
				BLI_dynstr_append(ds, "sampler2D");
 | 
						|
			else if (function->paramtype[a] == GPU_SHADOW2D)
 | 
						|
				BLI_dynstr_append(ds, "sampler2DShadow");
 | 
						|
			else
 | 
						|
				BLI_dynstr_append(ds, GPU_DATATYPE_STR[function->paramtype[a]]);
 | 
						|
#  if 0
 | 
						|
			BLI_dynstr_appendf(ds, " param%d", a);
 | 
						|
#  endif
 | 
						|
 | 
						|
			if (a != function->totparam - 1)
 | 
						|
				BLI_dynstr_append(ds, ", ");
 | 
						|
		}
 | 
						|
		BLI_dynstr_append(ds, ");\n");
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "\n");
 | 
						|
 | 
						|
	prototypes = BLI_dynstr_get_cstring(ds);
 | 
						|
	BLI_dynstr_free(ds);
 | 
						|
 | 
						|
	return prototypes;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static GPUFunction *gpu_lookup_function(const char *name)
 | 
						|
{
 | 
						|
	if (!FUNCTION_HASH) {
 | 
						|
		FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh");
 | 
						|
		gpu_parse_functions_string(FUNCTION_HASH, glsl_material_library);
 | 
						|
	}
 | 
						|
 | 
						|
	return BLI_ghash_lookup(FUNCTION_HASH, (const void *)name);
 | 
						|
}
 | 
						|
 | 
						|
void gpu_codegen_init(void)
 | 
						|
{
 | 
						|
	GPU_code_generate_glsl_lib();
 | 
						|
}
 | 
						|
 | 
						|
void gpu_codegen_exit(void)
 | 
						|
{
 | 
						|
	extern Material defmaterial; /* render module abuse... */
 | 
						|
 | 
						|
	if (defmaterial.gpumaterial.first)
 | 
						|
		GPU_material_free(&defmaterial.gpumaterial);
 | 
						|
 | 
						|
	if (FUNCTION_HASH) {
 | 
						|
		BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN);
 | 
						|
		FUNCTION_HASH = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPU_shader_free_builtin_shaders();
 | 
						|
 | 
						|
	if (glsl_material_library) {
 | 
						|
		MEM_freeN(glsl_material_library);
 | 
						|
		glsl_material_library = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (FUNCTION_PROTOTYPES) {
 | 
						|
		MEM_freeN(FUNCTION_PROTOTYPES);
 | 
						|
		FUNCTION_PROTOTYPES = NULL;
 | 
						|
	}
 | 
						|
	if (FUNCTION_LIB) {
 | 
						|
		GPU_shader_free(FUNCTION_LIB);
 | 
						|
		FUNCTION_LIB = NULL;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* GLSL code generation */
 | 
						|
 | 
						|
static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id)
 | 
						|
{
 | 
						|
	char name[1024];
 | 
						|
 | 
						|
	BLI_snprintf(name, sizeof(name), "%s%d", tmp, id);
 | 
						|
 | 
						|
	if (from == to) {
 | 
						|
		BLI_dynstr_append(ds, name);
 | 
						|
	}
 | 
						|
	else if (to == GPU_FLOAT) {
 | 
						|
		if (from == GPU_VEC4)
 | 
						|
			BLI_dynstr_appendf(ds, "convert_rgba_to_float(%s)", name);
 | 
						|
		else if (from == GPU_VEC3)
 | 
						|
			BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name);
 | 
						|
		else if (from == GPU_VEC2)
 | 
						|
			BLI_dynstr_appendf(ds, "%s.r", name);
 | 
						|
	}
 | 
						|
	else if (to == GPU_VEC2) {
 | 
						|
		if (from == GPU_VEC4)
 | 
						|
			BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name);
 | 
						|
		else if (from == GPU_VEC3)
 | 
						|
			BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name);
 | 
						|
		else if (from == GPU_FLOAT)
 | 
						|
			BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name);
 | 
						|
	}
 | 
						|
	else if (to == GPU_VEC3) {
 | 
						|
		if (from == GPU_VEC4)
 | 
						|
			BLI_dynstr_appendf(ds, "%s.rgb", name);
 | 
						|
		else if (from == GPU_VEC2)
 | 
						|
			BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name);
 | 
						|
		else if (from == GPU_FLOAT)
 | 
						|
			BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		if (from == GPU_VEC3)
 | 
						|
			BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name);
 | 
						|
		else if (from == GPU_VEC2)
 | 
						|
			BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name);
 | 
						|
		else if (from == GPU_FLOAT)
 | 
						|
			BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void codegen_print_datatype(DynStr *ds, const GPUType type, float *data)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	BLI_dynstr_appendf(ds, "%s(", GPU_DATATYPE_STR[type]);
 | 
						|
 | 
						|
	for (i = 0; i < type; i++) {
 | 
						|
		BLI_dynstr_appendf(ds, "%f", data[i]);
 | 
						|
		if (i == type - 1)
 | 
						|
			BLI_dynstr_append(ds, ")");
 | 
						|
		else
 | 
						|
			BLI_dynstr_append(ds, ", ");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int codegen_input_has_texture(GPUInput *input)
 | 
						|
{
 | 
						|
	if (input->link)
 | 
						|
		return 0;
 | 
						|
	else if (input->ima || input->prv)
 | 
						|
		return 1;
 | 
						|
	else
 | 
						|
		return input->tex != NULL;
 | 
						|
}
 | 
						|
 | 
						|
const char *GPU_builtin_name(GPUBuiltin builtin)
 | 
						|
{
 | 
						|
	if (builtin == GPU_VIEW_MATRIX)
 | 
						|
		return "unfviewmat";
 | 
						|
	else if (builtin == GPU_OBJECT_MATRIX)
 | 
						|
		return "unfobmat";
 | 
						|
	else if (builtin == GPU_INVERSE_VIEW_MATRIX)
 | 
						|
		return "unfinvviewmat";
 | 
						|
	else if (builtin == GPU_INVERSE_OBJECT_MATRIX)
 | 
						|
		return "unfinvobmat";
 | 
						|
	else if (builtin == GPU_LOC_TO_VIEW_MATRIX)
 | 
						|
		return "unflocaltoviewmat";
 | 
						|
	else if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX)
 | 
						|
		return "unfinvlocaltoviewmat";
 | 
						|
	else if (builtin == GPU_VIEW_POSITION)
 | 
						|
		return "varposition";
 | 
						|
	else if (builtin == GPU_VIEW_NORMAL)
 | 
						|
		return "varnormal";
 | 
						|
	else if (builtin == GPU_OBCOLOR)
 | 
						|
		return "unfobcolor";
 | 
						|
	else if (builtin == GPU_AUTO_BUMPSCALE)
 | 
						|
		return "unfobautobumpscale";
 | 
						|
	else if (builtin == GPU_CAMERA_TEXCO_FACTORS)
 | 
						|
		return "unfcameratexfactors";
 | 
						|
	else
 | 
						|
		return "";
 | 
						|
}
 | 
						|
 | 
						|
/* assign only one texid per buffer to avoid sampling the same texture twice */
 | 
						|
static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key)
 | 
						|
{
 | 
						|
	if (BLI_ghash_haskey(bindhash, key)) {
 | 
						|
		/* Reuse existing texid */
 | 
						|
		input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, key));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/* Allocate new texid */
 | 
						|
		input->texid = *texid;
 | 
						|
		(*texid)++;
 | 
						|
		input->bindtex = true;
 | 
						|
		BLI_ghash_insert(bindhash, key, SET_INT_IN_POINTER(input->texid));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void codegen_set_unique_ids(ListBase *nodes)
 | 
						|
{
 | 
						|
	GHash *bindhash, *definehash;
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	GPUOutput *output;
 | 
						|
	int id = 1, texid = 0;
 | 
						|
 | 
						|
	bindhash = BLI_ghash_ptr_new("codegen_set_unique_ids1 gh");
 | 
						|
	definehash = BLI_ghash_ptr_new("codegen_set_unique_ids2 gh");
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			/* set id for unique names of uniform variables */
 | 
						|
			input->id = id++;
 | 
						|
			input->bindtex = false;
 | 
						|
			input->definetex = false;
 | 
						|
 | 
						|
			/* set texid used for settings texture slot with multitexture */
 | 
						|
			if (codegen_input_has_texture(input) &&
 | 
						|
			    ((input->source == GPU_SOURCE_TEX) || (input->source == GPU_SOURCE_TEX_PIXEL)))
 | 
						|
			{
 | 
						|
				/* assign only one texid per buffer to avoid sampling
 | 
						|
				 * the same texture twice */
 | 
						|
				if (input->link) {
 | 
						|
					/* input is texture from buffer */
 | 
						|
					codegen_set_texid(bindhash, input, &texid, input->link);
 | 
						|
				}
 | 
						|
				else if (input->ima) {
 | 
						|
					/* input is texture from image */
 | 
						|
					codegen_set_texid(bindhash, input, &texid, input->ima);
 | 
						|
				}
 | 
						|
				else if (input->prv) {
 | 
						|
					/* input is texture from preview render */
 | 
						|
					codegen_set_texid(bindhash, input, &texid, input->prv);
 | 
						|
				}
 | 
						|
				else if (input->tex) {
 | 
						|
					/* input is user created texture, check tex pointer */
 | 
						|
					codegen_set_texid(bindhash, input, &texid, input->tex);
 | 
						|
				}
 | 
						|
 | 
						|
				/* make sure this pixel is defined exactly once */
 | 
						|
				if (input->source == GPU_SOURCE_TEX_PIXEL) {
 | 
						|
					if (input->ima) {
 | 
						|
						if (!BLI_ghash_haskey(definehash, input->ima)) {
 | 
						|
							input->definetex = true;
 | 
						|
							BLI_ghash_insert(definehash, input->ima, SET_INT_IN_POINTER(input->texid));
 | 
						|
						}
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						if (!BLI_ghash_haskey(definehash, input->link)) {
 | 
						|
							input->definetex = true;
 | 
						|
							BLI_ghash_insert(definehash, input->link, SET_INT_IN_POINTER(input->texid));
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		for (output = node->outputs.first; output; output = output->next)
 | 
						|
			/* set id for unique names of tmp variables storing output */
 | 
						|
			output->id = id++;
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_ghash_free(bindhash, NULL, NULL);
 | 
						|
	BLI_ghash_free(definehash, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	const char *name;
 | 
						|
	int builtins = 0;
 | 
						|
 | 
						|
	/* print uniforms */
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			if ((input->source == GPU_SOURCE_TEX) || (input->source == GPU_SOURCE_TEX_PIXEL)) {
 | 
						|
				/* create exactly one sampler for each texture */
 | 
						|
				if (codegen_input_has_texture(input) && input->bindtex) {
 | 
						|
					BLI_dynstr_appendf(ds, "uniform %s samp%d;\n",
 | 
						|
						(input->textype == GPU_TEX2D) ? "sampler2D" :
 | 
						|
						(input->textype == GPU_TEXCUBE) ? "samplerCube" : "sampler2DShadow",
 | 
						|
						input->texid);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_BUILTIN) {
 | 
						|
				/* only define each builtin uniform/varying once */
 | 
						|
				if (!(builtins & input->builtin)) {
 | 
						|
					builtins |= input->builtin;
 | 
						|
					name = GPU_builtin_name(input->builtin);
 | 
						|
 | 
						|
					if (gpu_str_prefix(name, "unf")) {
 | 
						|
						BLI_dynstr_appendf(ds, "uniform %s %s;\n",
 | 
						|
							GPU_DATATYPE_STR[input->type], name);
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						BLI_dynstr_appendf(ds, "%s %s %s;\n",
 | 
						|
							GLEW_VERSION_3_0 ? "in" : "varying",
 | 
						|
							GPU_DATATYPE_STR[input->type], name);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_VEC_UNIFORM) {
 | 
						|
				if (input->dynamicvec) {
 | 
						|
					/* only create uniforms for dynamic vectors */
 | 
						|
					BLI_dynstr_appendf(ds, "uniform %s unf%d;\n",
 | 
						|
						GPU_DATATYPE_STR[input->type], input->id);
 | 
						|
				}
 | 
						|
				else {
 | 
						|
					/* for others use const so the compiler can do folding */
 | 
						|
					BLI_dynstr_appendf(ds, "const %s cons%d = ",
 | 
						|
						GPU_DATATYPE_STR[input->type], input->id);
 | 
						|
					codegen_print_datatype(ds, input->type, input->vec);
 | 
						|
					BLI_dynstr_append(ds, ";\n");
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
				bool skip_opensubdiv = input->attribtype == CD_TANGENT;
 | 
						|
				if (skip_opensubdiv) {
 | 
						|
					BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
 | 
						|
				}
 | 
						|
#endif
 | 
						|
				BLI_dynstr_appendf(ds, "%s %s var%d;\n",
 | 
						|
					GLEW_VERSION_3_0 ? "in" : "varying",
 | 
						|
					GPU_DATATYPE_STR[input->type], input->attribid);
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
				if (skip_opensubdiv) {
 | 
						|
					BLI_dynstr_appendf(ds, "#endif\n");
 | 
						|
				}
 | 
						|
#endif
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "\n");
 | 
						|
 | 
						|
	return builtins;
 | 
						|
}
 | 
						|
 | 
						|
static void codegen_declare_tmps(DynStr *ds, ListBase *nodes)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	GPUOutput *output;
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		/* load pixels from textures */
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			if (input->source == GPU_SOURCE_TEX_PIXEL) {
 | 
						|
				if (codegen_input_has_texture(input) && input->definetex) {
 | 
						|
					BLI_dynstr_appendf(ds, "\tvec4 tex%d = texture2D(", input->texid);
 | 
						|
					BLI_dynstr_appendf(ds, "samp%d, gl_TexCoord[%d].st);\n",
 | 
						|
					                   input->texid, input->texid);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* declare temporary variables for node output storage */
 | 
						|
		for (output = node->outputs.first; output; output = output->next) {
 | 
						|
			BLI_dynstr_appendf(ds, "\t%s tmp%d;\n",
 | 
						|
			                   GPU_DATATYPE_STR[output->type], output->id);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "\n");
 | 
						|
}
 | 
						|
 | 
						|
static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *finaloutput)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	GPUOutput *output;
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		BLI_dynstr_appendf(ds, "\t%s(", node->name);
 | 
						|
		
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			if (input->source == GPU_SOURCE_TEX) {
 | 
						|
				BLI_dynstr_appendf(ds, "samp%d", input->texid);
 | 
						|
				if (input->link)
 | 
						|
					BLI_dynstr_appendf(ds, ", gl_TexCoord[%d].st", input->texid);
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_TEX_PIXEL) {
 | 
						|
				codegen_convert_datatype(ds, input->link->output->type, input->type,
 | 
						|
					"tmp", input->link->output->id);
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_BUILTIN) {
 | 
						|
				if (input->builtin == GPU_VIEW_NORMAL)
 | 
						|
					BLI_dynstr_append(ds, "facingnormal");
 | 
						|
				else
 | 
						|
					BLI_dynstr_append(ds, GPU_builtin_name(input->builtin));
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_VEC_UNIFORM) {
 | 
						|
				if (input->dynamicvec)
 | 
						|
					BLI_dynstr_appendf(ds, "unf%d", input->id);
 | 
						|
				else
 | 
						|
					BLI_dynstr_appendf(ds, "cons%d", input->id);
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_ATTRIB) {
 | 
						|
				BLI_dynstr_appendf(ds, "var%d", input->attribid);
 | 
						|
			}
 | 
						|
			else if (input->source == GPU_SOURCE_OPENGL_BUILTIN) {
 | 
						|
				if (input->oglbuiltin == GPU_MATCAP_NORMAL)
 | 
						|
					BLI_dynstr_append(ds, "gl_SecondaryColor");
 | 
						|
				else if (input->oglbuiltin == GPU_COLOR)
 | 
						|
					BLI_dynstr_append(ds, "gl_Color");
 | 
						|
			}
 | 
						|
 | 
						|
			BLI_dynstr_append(ds, ", ");
 | 
						|
		}
 | 
						|
 | 
						|
		for (output = node->outputs.first; output; output = output->next) {
 | 
						|
			BLI_dynstr_appendf(ds, "tmp%d", output->id);
 | 
						|
			if (output->next)
 | 
						|
				BLI_dynstr_append(ds, ", ");
 | 
						|
		}
 | 
						|
 | 
						|
		BLI_dynstr_append(ds, ");\n");
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "\n\tgl_FragColor = ");
 | 
						|
	codegen_convert_datatype(ds, finaloutput->type, GPU_VEC4, "tmp", finaloutput->id);
 | 
						|
	BLI_dynstr_append(ds, ";\n");
 | 
						|
}
 | 
						|
 | 
						|
static char *code_generate_fragment(ListBase *nodes, GPUOutput *output)
 | 
						|
{
 | 
						|
	DynStr *ds = BLI_dynstr_new();
 | 
						|
	char *code;
 | 
						|
	int builtins;
 | 
						|
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#if 0
 | 
						|
	BLI_dynstr_append(ds, FUNCTION_PROTOTYPES);
 | 
						|
#endif
 | 
						|
 | 
						|
	codegen_set_unique_ids(nodes);
 | 
						|
	builtins = codegen_print_uniforms_functions(ds, nodes);
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (G.debug & G_DEBUG)
 | 
						|
		BLI_dynstr_appendf(ds, "/* %s */\n", name);
 | 
						|
#endif
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "void main()\n{\n");
 | 
						|
 | 
						|
	if (builtins & GPU_VIEW_NORMAL)
 | 
						|
		BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing? varnormal: -varnormal;\n");
 | 
						|
 | 
						|
	/* Calculate tangent space. */
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
	{
 | 
						|
		bool has_tangent = false;
 | 
						|
		for (node = nodes->first; node; node = node->next) {
 | 
						|
			for (input = node->inputs.first; input; input = input->next) {
 | 
						|
				if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
					if (input->attribtype == CD_TANGENT) {
 | 
						|
						BLI_dynstr_appendf(ds, "#ifdef USE_OPENSUBDIV\n");
 | 
						|
						BLI_dynstr_appendf(ds, "\t%s var%d;\n",
 | 
						|
						                   GPU_DATATYPE_STR[input->type],
 | 
						|
						                   input->attribid);
 | 
						|
						if (has_tangent == false) {
 | 
						|
							BLI_dynstr_appendf(ds, "\tvec3 Q1 = dFdx(inpt.v.position.xyz);\n");
 | 
						|
							BLI_dynstr_appendf(ds, "\tvec3 Q2 = dFdy(inpt.v.position.xyz);\n");
 | 
						|
							BLI_dynstr_appendf(ds, "\tvec2 st1 = dFdx(inpt.v.uv);\n");
 | 
						|
							BLI_dynstr_appendf(ds, "\tvec2 st2 = dFdy(inpt.v.uv);\n");
 | 
						|
							BLI_dynstr_appendf(ds, "\tvec3 T = normalize(Q1 * st2.t - Q2 * st1.t);\n");
 | 
						|
						}
 | 
						|
						BLI_dynstr_appendf(ds, "\tvar%d = vec4(T, 1.0);\n", input->attribid);
 | 
						|
						BLI_dynstr_appendf(ds, "#endif\n");
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	codegen_declare_tmps(ds, nodes);
 | 
						|
	codegen_call_functions(ds, nodes, output);
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "}\n");
 | 
						|
 | 
						|
	/* create shader */
 | 
						|
	code = BLI_dynstr_get_cstring(ds);
 | 
						|
	BLI_dynstr_free(ds);
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (G.debug & G_DEBUG) printf("%s\n", code);
 | 
						|
#endif
 | 
						|
 | 
						|
	return code;
 | 
						|
}
 | 
						|
 | 
						|
static char *code_generate_vertex(ListBase *nodes, const GPUMatType type)
 | 
						|
{
 | 
						|
	DynStr *ds = BLI_dynstr_new();
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	char *code;
 | 
						|
	char *vertcode = NULL;
 | 
						|
	
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
				bool skip_opensubdiv = ELEM(input->attribtype, CD_MTFACE, CD_TANGENT);
 | 
						|
				if (skip_opensubdiv) {
 | 
						|
					BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
 | 
						|
				}
 | 
						|
#endif
 | 
						|
				BLI_dynstr_appendf(ds, "%s %s att%d;\n",
 | 
						|
					GLEW_VERSION_3_0 ? "in" : "attribute",
 | 
						|
					GPU_DATATYPE_STR[input->type], input->attribid);
 | 
						|
				BLI_dynstr_appendf(ds, "%s %s var%d;\n",
 | 
						|
					GLEW_VERSION_3_0 ? "out" : "varying",
 | 
						|
					GPU_DATATYPE_STR[input->type], input->attribid);
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
				if (skip_opensubdiv) {
 | 
						|
					BLI_dynstr_appendf(ds, "#endif\n");
 | 
						|
				}
 | 
						|
#endif
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "\n");
 | 
						|
 | 
						|
	switch (type) {
 | 
						|
		case GPU_MATERIAL_TYPE_MESH:
 | 
						|
			vertcode = datatoc_gpu_shader_vertex_glsl;
 | 
						|
			break;
 | 
						|
		case GPU_MATERIAL_TYPE_WORLD:
 | 
						|
			vertcode = datatoc_gpu_shader_vertex_world_glsl;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			fprintf(stderr, "invalid material type, set one after GPU_material_construct_begin\n");
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, vertcode);
 | 
						|
	
 | 
						|
	for (node = nodes->first; node; node = node->next)
 | 
						|
		for (input = node->inputs.first; input; input = input->next)
 | 
						|
			if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
				if (input->attribtype == CD_TANGENT) { /* silly exception */
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
					BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
 | 
						|
#endif
 | 
						|
					BLI_dynstr_appendf(
 | 
						|
					        ds, "\tvar%d.xyz = normalize(gl_NormalMatrix * att%d.xyz);\n",
 | 
						|
					        input->attribid, input->attribid);
 | 
						|
					BLI_dynstr_appendf(
 | 
						|
					        ds, "\tvar%d.w = att%d.w;\n",
 | 
						|
					        input->attribid, input->attribid);
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
					BLI_dynstr_appendf(ds, "#endif\n");
 | 
						|
#endif
 | 
						|
				}
 | 
						|
				else {
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
					bool is_mtface = input->attribtype == CD_MTFACE;
 | 
						|
					if (is_mtface) {
 | 
						|
						BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
 | 
						|
					}
 | 
						|
#endif
 | 
						|
					BLI_dynstr_appendf(ds, "\tvar%d = att%d;\n", input->attribid, input->attribid);
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
					if (is_mtface) {
 | 
						|
						BLI_dynstr_appendf(ds, "#endif\n");
 | 
						|
					}
 | 
						|
#endif
 | 
						|
				}
 | 
						|
			}
 | 
						|
			/* unfortunately special handling is needed here because we abuse gl_Color/gl_SecondaryColor flat shading */
 | 
						|
			else if (input->source == GPU_SOURCE_OPENGL_BUILTIN) {
 | 
						|
				if (input->oglbuiltin == GPU_MATCAP_NORMAL) {
 | 
						|
					/* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors
 | 
						|
					 * between shader stages and we want the full range of the normal */
 | 
						|
					BLI_dynstr_appendf(ds, "\tvec3 matcapcol = vec3(0.5) * varnormal + vec3(0.5);\n");
 | 
						|
					BLI_dynstr_appendf(ds, "\tgl_FrontSecondaryColor = vec4(matcapcol, 1.0);\n");
 | 
						|
				}
 | 
						|
				else if (input->oglbuiltin == GPU_COLOR) {
 | 
						|
					BLI_dynstr_appendf(ds, "\tgl_FrontColor = gl_Color;\n");
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, "}\n");
 | 
						|
 | 
						|
	code = BLI_dynstr_get_cstring(ds);
 | 
						|
 | 
						|
	BLI_dynstr_free(ds);
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (G.debug & G_DEBUG) printf("%s\n", code);
 | 
						|
#endif
 | 
						|
 | 
						|
	return code;
 | 
						|
}
 | 
						|
 | 
						|
static char *code_generate_geometry(ListBase *nodes, bool use_opensubdiv)
 | 
						|
{
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
	if (use_opensubdiv) {
 | 
						|
		DynStr *ds = BLI_dynstr_new();
 | 
						|
		GPUNode *node;
 | 
						|
		GPUInput *input;
 | 
						|
		char *code;
 | 
						|
 | 
						|
		/* Generate varying declarations. */
 | 
						|
		for (node = nodes->first; node; node = node->next) {
 | 
						|
			for (input = node->inputs.first; input; input = input->next) {
 | 
						|
				if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
					if (input->attribtype == CD_MTFACE) {
 | 
						|
						BLI_dynstr_appendf(ds, "%s %s var%d%s;\n",
 | 
						|
						                   GLEW_VERSION_3_0 ? "in" : "varying",
 | 
						|
						                   GPU_DATATYPE_STR[input->type],
 | 
						|
						                   input->attribid,
 | 
						|
						                   GLEW_VERSION_3_0 ? "[]" : "");
 | 
						|
						BLI_dynstr_appendf(ds, "uniform int fvar%d_offset;\n",
 | 
						|
						                   input->attribid);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		BLI_dynstr_append(ds, datatoc_gpu_shader_geometry_glsl);
 | 
						|
 | 
						|
		/* Generate varying assignments. */
 | 
						|
		/* TODO(sergey): Disabled for now, needs revisit. */
 | 
						|
#if 0
 | 
						|
		for (node = nodes->first; node; node = node->next) {
 | 
						|
			for (input = node->inputs.first; input; input = input->next) {
 | 
						|
				if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
 | 
						|
					if (input->attribtype == CD_MTFACE) {
 | 
						|
						BLI_dynstr_appendf(ds,
 | 
						|
						                   "\tINTERP_FACE_VARYING_2(var%d, "
 | 
						|
						                       "fvar%d_offset, st);\n",
 | 
						|
						                   input->attribid,
 | 
						|
						                   input->attribid);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
		BLI_dynstr_append(ds, "}\n");
 | 
						|
		code = BLI_dynstr_get_cstring(ds);
 | 
						|
		BLI_dynstr_free(ds);
 | 
						|
 | 
						|
		//if (G.debug & G_DEBUG) printf("%s\n", code);
 | 
						|
 | 
						|
		return code;
 | 
						|
	}
 | 
						|
#else
 | 
						|
	UNUSED_VARS(nodes, use_opensubdiv);
 | 
						|
#endif
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void GPU_code_generate_glsl_lib(void)
 | 
						|
{
 | 
						|
	DynStr *ds;
 | 
						|
 | 
						|
	/* only initialize the library once */
 | 
						|
	if (glsl_material_library)
 | 
						|
		return;
 | 
						|
 | 
						|
	ds = BLI_dynstr_new();
 | 
						|
 | 
						|
	BLI_dynstr_append(ds, datatoc_gpu_shader_material_glsl);
 | 
						|
 | 
						|
 | 
						|
	glsl_material_library = BLI_dynstr_get_cstring(ds);
 | 
						|
 | 
						|
	BLI_dynstr_free(ds);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* GPU pass binding/unbinding */
 | 
						|
 | 
						|
GPUShader *GPU_pass_shader(GPUPass *pass)
 | 
						|
{
 | 
						|
	return pass->shader;
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes)
 | 
						|
{
 | 
						|
	GPUShader *shader = pass->shader;
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *next, *input;
 | 
						|
	ListBase *inputs = &pass->inputs;
 | 
						|
	int extract, z;
 | 
						|
 | 
						|
	memset(inputs, 0, sizeof(*inputs));
 | 
						|
 | 
						|
	if (!shader)
 | 
						|
		return;
 | 
						|
 | 
						|
	GPU_shader_bind(shader);
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		z = 0;
 | 
						|
		for (input = node->inputs.first; input; input = next, z++) {
 | 
						|
			next = input->next;
 | 
						|
 | 
						|
			/* attributes don't need to be bound, they already have
 | 
						|
			 * an id that the drawing functions will use */
 | 
						|
			if (input->source == GPU_SOURCE_ATTRIB) {
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
				/* We do need mtface attributes for later, so we can
 | 
						|
				 * update face-varuing variables offset in the texture
 | 
						|
				 * buffer for proper sampling from the shader.
 | 
						|
				 *
 | 
						|
				 * We don't do anything about attribute itself, we
 | 
						|
				 * only use it to learn which uniform name is to be
 | 
						|
				 * updated.
 | 
						|
				 *
 | 
						|
				 * TODO(sergey): We can add ad extra uniform input
 | 
						|
				 * for the offset, which will be purely internal and
 | 
						|
				 * which would avoid having such an exceptions.
 | 
						|
				 */
 | 
						|
				if (input->attribtype != CD_MTFACE) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
#else
 | 
						|
				continue;
 | 
						|
#endif
 | 
						|
			}
 | 
						|
			if (input->source == GPU_SOURCE_BUILTIN ||
 | 
						|
			    input->source == GPU_SOURCE_OPENGL_BUILTIN)
 | 
						|
			{
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if (input->ima || input->tex || input->prv)
 | 
						|
				BLI_snprintf(input->shadername, sizeof(input->shadername), "samp%d", input->texid);
 | 
						|
			else
 | 
						|
				BLI_snprintf(input->shadername, sizeof(input->shadername), "unf%d", input->id);
 | 
						|
 | 
						|
			/* pass non-dynamic uniforms to opengl */
 | 
						|
			extract = 0;
 | 
						|
 | 
						|
			if (input->ima || input->tex || input->prv) {
 | 
						|
				if (input->bindtex)
 | 
						|
					extract = 1;
 | 
						|
			}
 | 
						|
			else if (input->dynamicvec)
 | 
						|
				extract = 1;
 | 
						|
 | 
						|
			if (extract)
 | 
						|
				input->shaderloc = GPU_shader_get_uniform(shader, input->shadername);
 | 
						|
 | 
						|
#ifdef WITH_OPENSUBDIV
 | 
						|
			if (input->source == GPU_SOURCE_ATTRIB &&
 | 
						|
			    input->attribtype == CD_MTFACE)
 | 
						|
			{
 | 
						|
				extract = 1;
 | 
						|
			}
 | 
						|
#endif
 | 
						|
 | 
						|
			/* extract nodes */
 | 
						|
			if (extract) {
 | 
						|
				BLI_remlink(&node->inputs, input);
 | 
						|
				BLI_addtail(inputs, input);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	GPU_shader_unbind();
 | 
						|
}
 | 
						|
 | 
						|
void GPU_pass_bind(GPUPass *pass, double time, int mipmap)
 | 
						|
{
 | 
						|
	GPUInput *input;
 | 
						|
	GPUShader *shader = pass->shader;
 | 
						|
	ListBase *inputs = &pass->inputs;
 | 
						|
 | 
						|
	if (!shader)
 | 
						|
		return;
 | 
						|
 | 
						|
	GPU_shader_bind(shader);
 | 
						|
 | 
						|
	/* create the textures */
 | 
						|
	for (input = inputs->first; input; input = input->next) {
 | 
						|
		if (input->ima)
 | 
						|
			input->tex = GPU_texture_from_blender(input->ima, input->iuser, input->textarget, input->image_isdata, time, mipmap);
 | 
						|
		else if (input->prv)
 | 
						|
			input->tex = GPU_texture_from_preview(input->prv, mipmap);
 | 
						|
	}
 | 
						|
 | 
						|
	/* bind the textures, in second loop so texture binding during
 | 
						|
	 * create doesn't overwrite already bound textures */
 | 
						|
	for (input = inputs->first; input; input = input->next) {
 | 
						|
		if (input->tex && input->bindtex) {
 | 
						|
			GPU_texture_bind(input->tex, input->texid);
 | 
						|
			GPU_shader_uniform_texture(shader, input->shaderloc, input->tex);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void GPU_pass_update_uniforms(GPUPass *pass)
 | 
						|
{
 | 
						|
	GPUInput *input;
 | 
						|
	GPUShader *shader = pass->shader;
 | 
						|
	ListBase *inputs = &pass->inputs;
 | 
						|
 | 
						|
	if (!shader)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* pass dynamic inputs to opengl, others were removed */
 | 
						|
	for (input = inputs->first; input; input = input->next) {
 | 
						|
		if (!(input->ima || input->tex || input->prv)) {
 | 
						|
			if (input->dynamictype == GPU_DYNAMIC_MAT_HARD) {
 | 
						|
				// The hardness is actually a short pointer, so we convert it here
 | 
						|
				float val = (float)(*(short *)input->dynamicvec);
 | 
						|
				GPU_shader_uniform_vector(shader, input->shaderloc, 1, 1, &val);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				GPU_shader_uniform_vector(shader, input->shaderloc, input->type, 1,
 | 
						|
					input->dynamicvec);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void GPU_pass_unbind(GPUPass *pass)
 | 
						|
{
 | 
						|
	GPUInput *input;
 | 
						|
	GPUShader *shader = pass->shader;
 | 
						|
	ListBase *inputs = &pass->inputs;
 | 
						|
 | 
						|
	if (!shader)
 | 
						|
		return;
 | 
						|
 | 
						|
	for (input = inputs->first; input; input = input->next) {
 | 
						|
		if (input->tex && input->bindtex)
 | 
						|
			GPU_texture_unbind(input->tex);
 | 
						|
 | 
						|
		if (input->ima || input->prv)
 | 
						|
			input->tex = NULL;
 | 
						|
	}
 | 
						|
	
 | 
						|
	GPU_shader_unbind();
 | 
						|
}
 | 
						|
 | 
						|
/* Node Link Functions */
 | 
						|
 | 
						|
static GPUNodeLink *GPU_node_link_create(void)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = MEM_callocN(sizeof(GPUNodeLink), "GPUNodeLink");
 | 
						|
	link->type = GPU_NONE;
 | 
						|
	link->users++;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_node_link_free(GPUNodeLink *link)
 | 
						|
{
 | 
						|
	link->users--;
 | 
						|
 | 
						|
	if (link->users < 0)
 | 
						|
		fprintf(stderr, "GPU_node_link_free: negative refcount\n");
 | 
						|
	
 | 
						|
	if (link->users == 0) {
 | 
						|
		if (link->output)
 | 
						|
			link->output->link = NULL;
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Node Functions */
 | 
						|
 | 
						|
static GPUNode *GPU_node_begin(const char *name)
 | 
						|
{
 | 
						|
	GPUNode *node = MEM_callocN(sizeof(GPUNode), "GPUNode");
 | 
						|
 | 
						|
	node->name = name;
 | 
						|
 | 
						|
	return node;
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType type)
 | 
						|
{
 | 
						|
	GPUInput *input;
 | 
						|
	GPUNode *outnode;
 | 
						|
	const char *name;
 | 
						|
 | 
						|
	if (link->output) {
 | 
						|
		outnode = link->output->node;
 | 
						|
		name = outnode->name;
 | 
						|
		input = outnode->inputs.first;
 | 
						|
 | 
						|
		if ((STREQ(name, "set_value") || STREQ(name, "set_rgb")) &&
 | 
						|
		    (input->type == type))
 | 
						|
		{
 | 
						|
			input = MEM_dupallocN(outnode->inputs.first);
 | 
						|
			input->type = type;
 | 
						|
			if (input->link)
 | 
						|
				input->link->users++;
 | 
						|
			BLI_addtail(&node->inputs, input);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	input = MEM_callocN(sizeof(GPUInput), "GPUInput");
 | 
						|
	input->node = node;
 | 
						|
 | 
						|
	if (link->builtin) {
 | 
						|
		/* builtin uniform */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_BUILTIN;
 | 
						|
		input->builtin = link->builtin;
 | 
						|
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else if (link->oglbuiltin) {
 | 
						|
		/* builtin uniform */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_OPENGL_BUILTIN;
 | 
						|
		input->oglbuiltin = link->oglbuiltin;
 | 
						|
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else if (link->output) {
 | 
						|
		/* link to a node output */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_TEX_PIXEL;
 | 
						|
		input->link = link;
 | 
						|
		link->users++;
 | 
						|
	}
 | 
						|
	else if (link->dynamictex) {
 | 
						|
		/* dynamic texture, GPUTexture is updated/deleted externally */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_TEX;
 | 
						|
 | 
						|
		input->tex = link->dynamictex;
 | 
						|
		input->textarget = GL_TEXTURE_2D;
 | 
						|
		input->textype = type;
 | 
						|
		input->dynamictex = true;
 | 
						|
		input->dynamicdata = link->ptr2;
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else if (link->texture) {
 | 
						|
		/* small texture created on the fly, like for colorbands */
 | 
						|
		input->type = GPU_VEC4;
 | 
						|
		input->source = GPU_SOURCE_TEX;
 | 
						|
		input->textype = type;
 | 
						|
 | 
						|
#if 0
 | 
						|
		input->tex = GPU_texture_create_2D(link->texturesize, link->texturesize, link->ptr2, NULL);
 | 
						|
#endif
 | 
						|
		input->tex = GPU_texture_create_2D(link->texturesize, 1, link->ptr1, GPU_HDR_NONE, NULL);
 | 
						|
		input->textarget = GL_TEXTURE_2D;
 | 
						|
 | 
						|
		MEM_freeN(link->ptr1);
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else if (link->image) {
 | 
						|
		/* blender image */
 | 
						|
		input->type = GPU_VEC4;
 | 
						|
		input->source = GPU_SOURCE_TEX;
 | 
						|
 | 
						|
		if (link->image == GPU_NODE_LINK_IMAGE_PREVIEW) {
 | 
						|
			input->prv = link->ptr1;
 | 
						|
			input->textarget = GL_TEXTURE_2D;
 | 
						|
			input->textype = GPU_TEX2D;
 | 
						|
		}
 | 
						|
		else if (link->image == GPU_NODE_LINK_IMAGE_BLENDER) {
 | 
						|
			input->ima = link->ptr1;
 | 
						|
			input->iuser = link->ptr2;
 | 
						|
			input->image_isdata = link->image_isdata;
 | 
						|
			input->textarget = GL_TEXTURE_2D;
 | 
						|
			input->textype = GPU_TEX2D;
 | 
						|
		}
 | 
						|
		else if (link->image == GPU_NODE_LINK_IMAGE_CUBE_MAP) {
 | 
						|
			input->ima = link->ptr1;
 | 
						|
			input->iuser = link->ptr2;
 | 
						|
			input->image_isdata = link->image_isdata;
 | 
						|
			input->textarget = GL_TEXTURE_CUBE_MAP;
 | 
						|
			input->textype = GPU_TEXCUBE;
 | 
						|
		}
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else if (link->attribtype) {
 | 
						|
		/* vertex attribute */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_ATTRIB;
 | 
						|
 | 
						|
		input->attribtype = link->attribtype;
 | 
						|
		BLI_strncpy(input->attribname, link->attribname, sizeof(input->attribname));
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/* uniform vector */
 | 
						|
		input->type = type;
 | 
						|
		input->source = GPU_SOURCE_VEC_UNIFORM;
 | 
						|
 | 
						|
		memcpy(input->vec, link->ptr1, type * sizeof(float));
 | 
						|
		if (link->dynamic) {
 | 
						|
			input->dynamicvec = link->ptr1;
 | 
						|
			input->dynamictype = link->dynamictype;
 | 
						|
			input->dynamicdata = link->ptr2;
 | 
						|
		}
 | 
						|
		MEM_freeN(link);
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_addtail(&node->inputs, input);
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_node_input_socket(GPUNode *node, GPUNodeStack *sock)
 | 
						|
{
 | 
						|
	GPUNodeLink *link;
 | 
						|
 | 
						|
	if (sock->link) {
 | 
						|
		gpu_node_input_link(node, sock->link, sock->type);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		link = GPU_node_link_create();
 | 
						|
		link->ptr1 = sock->vec;
 | 
						|
		gpu_node_input_link(node, link, sock->type);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_node_output(GPUNode *node, const GPUType type, GPUNodeLink **link)
 | 
						|
{
 | 
						|
	GPUOutput *output = MEM_callocN(sizeof(GPUOutput), "GPUOutput");
 | 
						|
 | 
						|
	output->type = type;
 | 
						|
	output->node = node;
 | 
						|
 | 
						|
	if (link) {
 | 
						|
		*link = output->link = GPU_node_link_create();
 | 
						|
		output->link->type = type;
 | 
						|
		output->link->output = output;
 | 
						|
 | 
						|
		/* note: the caller owns the reference to the link, GPUOutput
 | 
						|
		 * merely points to it, and if the node is destroyed it will
 | 
						|
		 * set that pointer to NULL */
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_addtail(&node->outputs, output);
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_inputs_free(ListBase *inputs)
 | 
						|
{
 | 
						|
	GPUInput *input;
 | 
						|
 | 
						|
	for (input = inputs->first; input; input = input->next) {
 | 
						|
		if (input->link)
 | 
						|
			gpu_node_link_free(input->link);
 | 
						|
		else if (input->tex && !input->dynamictex)
 | 
						|
			GPU_texture_free(input->tex);
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_freelistN(inputs);
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_node_free(GPUNode *node)
 | 
						|
{
 | 
						|
	GPUOutput *output;
 | 
						|
 | 
						|
	gpu_inputs_free(&node->inputs);
 | 
						|
 | 
						|
	for (output = node->outputs.first; output; output = output->next)
 | 
						|
		if (output->link) {
 | 
						|
			output->link->output = NULL;
 | 
						|
			gpu_node_link_free(output->link);
 | 
						|
		}
 | 
						|
 | 
						|
	BLI_freelistN(&node->outputs);
 | 
						|
	MEM_freeN(node);
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_nodes_free(ListBase *nodes)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
 | 
						|
	while ((node = BLI_pophead(nodes))) {
 | 
						|
		gpu_node_free(node);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* vertex attributes */
 | 
						|
 | 
						|
static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *attribs)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	int a;
 | 
						|
 | 
						|
	/* convert attributes requested by node inputs to an array of layers,
 | 
						|
	 * checking for duplicates and assigning id's starting from zero. */
 | 
						|
 | 
						|
	memset(attribs, 0, sizeof(*attribs));
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next) {
 | 
						|
		for (input = node->inputs.first; input; input = input->next) {
 | 
						|
			if (input->source == GPU_SOURCE_ATTRIB) {
 | 
						|
				for (a = 0; a < attribs->totlayer; a++) {
 | 
						|
					if (attribs->layer[a].type == input->attribtype &&
 | 
						|
					    STREQ(attribs->layer[a].name, input->attribname))
 | 
						|
					{
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (a < GPU_MAX_ATTRIB) {
 | 
						|
					if (a == attribs->totlayer) {
 | 
						|
						input->attribid = attribs->totlayer++;
 | 
						|
						input->attribfirst = 1;
 | 
						|
 | 
						|
						attribs->layer[a].type = input->attribtype;
 | 
						|
						attribs->layer[a].attribid = input->attribid;
 | 
						|
						BLI_strncpy(attribs->layer[a].name, input->attribname,
 | 
						|
						            sizeof(attribs->layer[a].name));
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						input->attribid = attribs->layer[a].attribid;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_nodes_get_builtin_flag(ListBase *nodes, int *builtin)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	
 | 
						|
	*builtin = 0;
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next)
 | 
						|
		for (input = node->inputs.first; input; input = input->next)
 | 
						|
			if (input->source == GPU_SOURCE_BUILTIN)
 | 
						|
				*builtin |= input->builtin;
 | 
						|
}
 | 
						|
 | 
						|
/* varargs linking  */
 | 
						|
 | 
						|
GPUNodeLink *GPU_attribute(const CustomDataType type, const char *name)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->attribtype = type;
 | 
						|
	link->attribname = name;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_uniform(float *num)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->ptr1 = num;
 | 
						|
	link->ptr2 = NULL;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->ptr1 = num;
 | 
						|
	link->ptr2 = data;
 | 
						|
	link->dynamic = true;
 | 
						|
	link->dynamictype = dynamictype;
 | 
						|
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->image = GPU_NODE_LINK_IMAGE_BLENDER;
 | 
						|
	link->ptr1 = ima;
 | 
						|
	link->ptr2 = iuser;
 | 
						|
	link->image_isdata = is_data;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_cube_map(Image *ima, ImageUser *iuser, bool is_data)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->image = GPU_NODE_LINK_IMAGE_CUBE_MAP;
 | 
						|
	link->ptr1 = ima;
 | 
						|
	link->ptr2 = iuser;
 | 
						|
	link->image_isdata = is_data;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_image_preview(PreviewImage *prv)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
	
 | 
						|
	link->image = GPU_NODE_LINK_IMAGE_PREVIEW;
 | 
						|
	link->ptr1 = prv;
 | 
						|
	
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
GPUNodeLink *GPU_texture(int size, float *pixels)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->texture = true;
 | 
						|
	link->texturesize = size;
 | 
						|
	link->ptr1 = pixels;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_dynamic_texture(GPUTexture *tex, GPUDynamicType dynamictype, void *data)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->dynamic = true;
 | 
						|
	link->dynamictex = tex;
 | 
						|
	link->dynamictype = dynamictype;
 | 
						|
	link->ptr2 = data;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_builtin(GPUBuiltin builtin)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->builtin = builtin;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
GPUNodeLink *GPU_opengl_builtin(GPUOpenGLBuiltin builtin)
 | 
						|
{
 | 
						|
	GPUNodeLink *link = GPU_node_link_create();
 | 
						|
 | 
						|
	link->oglbuiltin = builtin;
 | 
						|
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
bool GPU_link(GPUMaterial *mat, const char *name, ...)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUFunction *function;
 | 
						|
	GPUNodeLink *link, **linkptr;
 | 
						|
	va_list params;
 | 
						|
	int i;
 | 
						|
 | 
						|
	function = gpu_lookup_function(name);
 | 
						|
	if (!function) {
 | 
						|
		fprintf(stderr, "GPU failed to find function %s\n", name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	node = GPU_node_begin(name);
 | 
						|
 | 
						|
	va_start(params, name);
 | 
						|
	for (i = 0; i < function->totparam; i++) {
 | 
						|
		if (function->paramqual[i] != FUNCTION_QUAL_IN) {
 | 
						|
			linkptr = va_arg(params, GPUNodeLink **);
 | 
						|
			gpu_node_output(node, function->paramtype[i], linkptr);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			link = va_arg(params, GPUNodeLink *);
 | 
						|
			gpu_node_input_link(node, link, function->paramtype[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	va_end(params);
 | 
						|
 | 
						|
	gpu_material_add_node(mat, node);
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUFunction *function;
 | 
						|
	GPUNodeLink *link, **linkptr;
 | 
						|
	va_list params;
 | 
						|
	int i, totin, totout;
 | 
						|
 | 
						|
	function = gpu_lookup_function(name);
 | 
						|
	if (!function) {
 | 
						|
		fprintf(stderr, "GPU failed to find function %s\n", name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	node = GPU_node_begin(name);
 | 
						|
	totin = 0;
 | 
						|
	totout = 0;
 | 
						|
 | 
						|
	if (in) {
 | 
						|
		for (i = 0; in[i].type != GPU_NONE; i++) {
 | 
						|
			gpu_node_input_socket(node, &in[i]);
 | 
						|
			totin++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	if (out) {
 | 
						|
		for (i = 0; out[i].type != GPU_NONE; i++) {
 | 
						|
			gpu_node_output(node, out[i].type, &out[i].link);
 | 
						|
			totout++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	va_start(params, out);
 | 
						|
	for (i = 0; i < function->totparam; i++) {
 | 
						|
		if (function->paramqual[i] != FUNCTION_QUAL_IN) {
 | 
						|
			if (totout == 0) {
 | 
						|
				linkptr = va_arg(params, GPUNodeLink **);
 | 
						|
				gpu_node_output(node, function->paramtype[i], linkptr);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				totout--;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			if (totin == 0) {
 | 
						|
				link = va_arg(params, GPUNodeLink *);
 | 
						|
				if (link->socket)
 | 
						|
					gpu_node_input_socket(node, link->socket);
 | 
						|
				else
 | 
						|
					gpu_node_input_link(node, link, function->paramtype[i]);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				totin--;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	va_end(params);
 | 
						|
 | 
						|
	gpu_material_add_node(mat, node);
 | 
						|
	
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
int GPU_link_changed(GPUNodeLink *link)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
	const char *name;
 | 
						|
 | 
						|
	if (link->output) {
 | 
						|
		node = link->output->node;
 | 
						|
		name = node->name;
 | 
						|
 | 
						|
		if (STREQ(name, "set_value") || STREQ(name, "set_rgb")) {
 | 
						|
			input = node->inputs.first;
 | 
						|
			return (input->link != NULL);
 | 
						|
		}
 | 
						|
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
		return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Pass create/free */
 | 
						|
 | 
						|
static void gpu_nodes_tag(GPUNodeLink *link)
 | 
						|
{
 | 
						|
	GPUNode *node;
 | 
						|
	GPUInput *input;
 | 
						|
 | 
						|
	if (!link->output)
 | 
						|
		return;
 | 
						|
 | 
						|
	node = link->output->node;
 | 
						|
	if (node->tag)
 | 
						|
		return;
 | 
						|
	
 | 
						|
	node->tag = true;
 | 
						|
	for (input = node->inputs.first; input; input = input->next)
 | 
						|
		if (input->link)
 | 
						|
			gpu_nodes_tag(input->link);
 | 
						|
}
 | 
						|
 | 
						|
static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink)
 | 
						|
{
 | 
						|
	GPUNode *node, *next;
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = node->next)
 | 
						|
		node->tag = false;
 | 
						|
 | 
						|
	gpu_nodes_tag(outlink);
 | 
						|
 | 
						|
	for (node = nodes->first; node; node = next) {
 | 
						|
		next = node->next;
 | 
						|
 | 
						|
		if (!node->tag) {
 | 
						|
			BLI_remlink(nodes, node);
 | 
						|
			gpu_node_free(node);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
GPUPass *GPU_generate_pass(
 | 
						|
        ListBase *nodes, GPUNodeLink *outlink,
 | 
						|
        GPUVertexAttribs *attribs, int *builtins,
 | 
						|
        const GPUMatType type, const char *UNUSED(name),
 | 
						|
        const bool use_opensubdiv,
 | 
						|
        const bool use_new_shading)
 | 
						|
{
 | 
						|
	GPUShader *shader;
 | 
						|
	GPUPass *pass;
 | 
						|
	char *vertexcode, *geometrycode, *fragmentcode;
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (!FUNCTION_LIB) {
 | 
						|
		GPU_nodes_free(nodes);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* prune unused nodes */
 | 
						|
	gpu_nodes_prune(nodes, outlink);
 | 
						|
 | 
						|
	gpu_nodes_get_vertex_attributes(nodes, attribs);
 | 
						|
	gpu_nodes_get_builtin_flag(nodes, builtins);
 | 
						|
 | 
						|
	/* generate code and compile with opengl */
 | 
						|
	fragmentcode = code_generate_fragment(nodes, outlink->output);
 | 
						|
	vertexcode = code_generate_vertex(nodes, type);
 | 
						|
	geometrycode = code_generate_geometry(nodes, use_opensubdiv);
 | 
						|
 | 
						|
	int flags = GPU_SHADER_FLAGS_NONE;
 | 
						|
	if (use_opensubdiv) {
 | 
						|
		flags |= GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV;
 | 
						|
	}
 | 
						|
	if (use_new_shading) {
 | 
						|
		flags |= GPU_SHADER_FLAGS_NEW_SHADING;
 | 
						|
	}
 | 
						|
	shader = GPU_shader_create_ex(vertexcode,
 | 
						|
	                              fragmentcode,
 | 
						|
	                              geometrycode,
 | 
						|
	                              glsl_material_library,
 | 
						|
	                              NULL,
 | 
						|
	                              0,
 | 
						|
	                              0,
 | 
						|
	                              0,
 | 
						|
	                              flags);
 | 
						|
 | 
						|
	/* failed? */
 | 
						|
	if (!shader) {
 | 
						|
		if (fragmentcode)
 | 
						|
			MEM_freeN(fragmentcode);
 | 
						|
		if (vertexcode)
 | 
						|
			MEM_freeN(vertexcode);
 | 
						|
		memset(attribs, 0, sizeof(*attribs));
 | 
						|
		memset(builtins, 0, sizeof(*builtins));
 | 
						|
		gpu_nodes_free(nodes);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	
 | 
						|
	/* create pass */
 | 
						|
	pass = MEM_callocN(sizeof(GPUPass), "GPUPass");
 | 
						|
 | 
						|
	pass->output = outlink->output;
 | 
						|
	pass->shader = shader;
 | 
						|
	pass->fragmentcode = fragmentcode;
 | 
						|
	pass->geometrycode = geometrycode;
 | 
						|
	pass->vertexcode = vertexcode;
 | 
						|
	pass->libcode = glsl_material_library;
 | 
						|
 | 
						|
	/* extract dynamic inputs and throw away nodes */
 | 
						|
	gpu_nodes_extract_dynamic_inputs(pass, nodes);
 | 
						|
	gpu_nodes_free(nodes);
 | 
						|
 | 
						|
	return pass;
 | 
						|
}
 | 
						|
 | 
						|
void GPU_pass_free(GPUPass *pass)
 | 
						|
{
 | 
						|
	GPU_shader_free(pass->shader);
 | 
						|
	gpu_inputs_free(&pass->inputs);
 | 
						|
	if (pass->fragmentcode)
 | 
						|
		MEM_freeN(pass->fragmentcode);
 | 
						|
	if (pass->geometrycode)
 | 
						|
		MEM_freeN(pass->geometrycode);
 | 
						|
	if (pass->vertexcode)
 | 
						|
		MEM_freeN(pass->vertexcode);
 | 
						|
	MEM_freeN(pass);
 | 
						|
}
 |