static void add_nodes(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree,
                      const ProxyMap &proxy_input_map, const ProxyMap &proxy_output_map)
{
	/* add nodes */
	BL::ShaderNodeTree::nodes_iterator b_node;
	PtrInputMap input_map;
	PtrOutputMap output_map;
	
	BL::Node::inputs_iterator b_input;
	BL::Node::outputs_iterator b_output;

	/* find the node to use for output if there are multiple */
	bool found_active_output = false;
	BL::ShaderNode output_node(PointerRNA_NULL);

	for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
		if (is_output_node(*b_node)) {
			BL::ShaderNodeOutputMaterial b_output_node(*b_node);

			if(b_output_node.is_active_output()) {
				output_node = b_output_node;
				found_active_output = true;
				break;
			}
			else if(!output_node.ptr.data && !found_active_output) {
				output_node = b_output_node;
			}
		}
	}

	/* add nodes */
	for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
		if (b_node->mute() || b_node->is_a(&RNA_NodeReroute)) {
			/* replace muted node with internal links */
			BL::Node::internal_links_iterator b_link;
			for (b_node->internal_links.begin(b_link); b_link != b_node->internal_links.end(); ++b_link) {
				ProxyNode *proxy = new ProxyNode(convert_socket_type(b_link->to_socket()));
				
				input_map[b_link->from_socket().ptr.data] = proxy->inputs[0];
				output_map[b_link->to_socket().ptr.data] = proxy->outputs[0];
				
				graph->add(proxy);
			}
		}
		else if (b_node->is_a(&RNA_ShaderNodeGroup) || b_node->is_a(&RNA_NodeCustomGroup)) {
			
			BL::ShaderNodeTree b_group_ntree(PointerRNA_NULL);
			if (b_node->is_a(&RNA_ShaderNodeGroup))
				b_group_ntree = BL::ShaderNodeTree(((BL::NodeGroup)(*b_node)).node_tree());
			else
				b_group_ntree = BL::ShaderNodeTree(((BL::NodeCustomGroup)(*b_node)).node_tree());
			ProxyMap group_proxy_input_map, group_proxy_output_map;
			
			/* Add a proxy node for each socket
			 * Do this even if the node group has no internal tree,
			 * so that links have something to connect to and assert won't fail.
			 */
			for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
				ProxyNode *proxy = new ProxyNode(convert_socket_type(*b_input));
				graph->add(proxy);
				
				/* register the proxy node for internal binding */
				group_proxy_input_map[b_input->identifier()] = proxy;
				
				input_map[b_input->ptr.data] = proxy->inputs[0];
				
				set_default_value(proxy->inputs[0], *b_node, *b_input, b_data, b_ntree);
			}
			for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
				ProxyNode *proxy = new ProxyNode(convert_socket_type(*b_output));
				graph->add(proxy);
				
				/* register the proxy node for internal binding */
				group_proxy_output_map[b_output->identifier()] = proxy;
				
				output_map[b_output->ptr.data] = proxy->outputs[0];
			}
			
			if (b_group_ntree)
				add_nodes(scene, b_data, b_scene, graph, b_group_ntree, group_proxy_input_map, group_proxy_output_map);
		}
		else if (b_node->is_a(&RNA_NodeGroupInput)) {
			/* map each socket to a proxy node */
			for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
				ProxyMap::const_iterator proxy_it = proxy_input_map.find(b_output->identifier());
				if (proxy_it != proxy_input_map.end()) {
					ProxyNode *proxy = proxy_it->second;
					
					output_map[b_output->ptr.data] = proxy->outputs[0];
				}
			}
		}
		else if (b_node->is_a(&RNA_NodeGroupOutput)) {
			BL::NodeGroupOutput b_output_node(*b_node);
			/* only the active group output is used */
			if (b_output_node.is_active_output()) {
				/* map each socket to a proxy node */
				for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
					ProxyMap::const_iterator proxy_it = proxy_output_map.find(b_input->identifier());
					if (proxy_it != proxy_output_map.end()) {
						ProxyNode *proxy = proxy_it->second;
						
						input_map[b_input->ptr.data] = proxy->inputs[0];
						
						set_default_value(proxy->inputs[0], *b_node, *b_input, b_data, b_ntree);
					}
				}
			}
		}
		else {
			ShaderNode *node = NULL;

			if (is_output_node(*b_node)) {
				if (b_node->ptr.data == output_node.ptr.data) {
					node = graph->output();
				}
			}
			else {
				node = add_node(scene, b_data, b_scene, graph, b_ntree, BL::ShaderNode(*b_node));
			}
			
			if(node) {
				/* map node sockets for linking */
				for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
					ShaderInput *input = node_find_input_by_name(node, *b_node, *b_input);
					input_map[b_input->ptr.data] = input;
					
					set_default_value(input, *b_node, *b_input, b_data, b_ntree);
				}
				for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
					ShaderOutput *output = node_find_output_by_name(node, *b_node, *b_output);
					output_map[b_output->ptr.data] = output;
				}
			}
		}
	}

	/* connect nodes */
	BL::NodeTree::links_iterator b_link;

	for(b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
		/* get blender link data */
		BL::NodeSocket b_from_sock = b_link->from_socket();
		BL::NodeSocket b_to_sock = b_link->to_socket();

		ShaderOutput *output = 0;
		ShaderInput *input = 0;
		
		PtrOutputMap::iterator output_it = output_map.find(b_from_sock.ptr.data);
		if (output_it != output_map.end())
			output = output_it->second;
		PtrInputMap::iterator input_it = input_map.find(b_to_sock.ptr.data);
		if (input_it != input_map.end())
			input = input_it->second;

		/* either node may be NULL when the node was not exported, typically
		 * because the node type is not supported */
		if(output && input)
			graph->connect(output, input);
	}
}
static void add_nodes(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map)
{
	/* add nodes */
	BL::ShaderNodeTree::nodes_iterator b_node;
	PtrNodeMap node_map;
	PtrSockMap proxy_map;

	for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
		if(b_node->mute()) {
			BL::Node::inputs_iterator b_input;
			BL::Node::outputs_iterator b_output;
			bool found_match = false;

			/* this is slightly different than blender logic, we just connect a
			 * single pair for of input/output, but works ok for the node we have */
			for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
				if(b_input->is_linked()) {
					for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
						if(b_output->is_linked() && b_input->type() == b_output->type()) {
							ProxyNode *proxy = new ProxyNode(convert_socket_type(b_input->type()), convert_socket_type(b_output->type()));
							graph->add(proxy);

							proxy_map[b_input->ptr.data] = SocketPair(proxy, proxy->inputs[0]->name);
							proxy_map[b_output->ptr.data] = SocketPair(proxy, proxy->outputs[0]->name);
							found_match = true;

							break;
						}
					}
				}

				if(found_match)
					break;
			}
		}
		else if(b_node->is_a(&RNA_NodeGroup)) {
			/* add proxy converter nodes for inputs and outputs */
			BL::NodeGroup b_gnode(*b_node);
			BL::ShaderNodeTree b_group_ntree(b_gnode.node_tree());
			if (!b_group_ntree)
				continue;

			BL::Node::inputs_iterator b_input;
			BL::Node::outputs_iterator b_output;
			
			PtrSockMap group_sockmap;
			
			for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
				ShaderSocketType extern_type = convert_socket_type(b_input->type());
				ShaderSocketType intern_type = convert_socket_type(b_input->group_socket().type());
				ShaderNode *proxy = graph->add(new ProxyNode(extern_type, intern_type));
				
				/* map the external node socket to the proxy node socket */
				proxy_map[b_input->ptr.data] = SocketPair(proxy, proxy->inputs[0]->name);
				/* map the internal group socket to the proxy node socket */
				group_sockmap[b_input->group_socket().ptr.data] = SocketPair(proxy, proxy->outputs[0]->name);
				
				/* default input values of the group node */
				set_default_value(proxy->inputs[0], *b_input, b_data, b_group_ntree);
			}
			
			for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
				ShaderSocketType extern_type = convert_socket_type(b_output->type());
				ShaderSocketType intern_type = convert_socket_type(b_output->group_socket().type());
				ShaderNode *proxy = graph->add(new ProxyNode(intern_type, extern_type));
				
				/* map the external node socket to the proxy node socket */
				proxy_map[b_output->ptr.data] = SocketPair(proxy, proxy->outputs[0]->name);
				/* map the internal group socket to the proxy node socket */
				group_sockmap[b_output->group_socket().ptr.data] = SocketPair(proxy, proxy->inputs[0]->name);
				
				/* default input values of internal, unlinked group outputs */
				set_default_value(proxy->inputs[0], b_output->group_socket(), b_data, b_group_ntree);
			}
			
			add_nodes(scene, b_data, b_scene, graph, b_group_ntree, group_sockmap);
		}
		else {
			ShaderNode *node = add_node(scene, b_data, b_scene, graph, b_ntree, BL::ShaderNode(*b_node));
			
			if(node) {
				BL::Node::inputs_iterator b_input;
				
				node_map[b_node->ptr.data] = node;
				
				for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
					SocketPair pair = node_socket_map_pair(node_map, *b_node, *b_input);
					ShaderInput *input = pair.first->input(pair.second.c_str());
					
					assert(input);
					
					/* copy values for non linked inputs */
					set_default_value(input, *b_input, b_data, b_ntree);
				}
			}
		}
	}

	/* connect nodes */
	BL::NodeTree::links_iterator b_link;

	for(b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
		/* get blender link data */
		BL::Node b_from_node = b_link->from_node();
		BL::Node b_to_node = b_link->to_node();

		BL::NodeSocket b_from_sock = b_link->from_socket();
		BL::NodeSocket b_to_sock = b_link->to_socket();

		SocketPair from_pair, to_pair;

		/* links without a node pointer are connections to group inputs/outputs */

		/* from sock */
		if(b_from_node) {
			if (b_from_node.mute() || b_from_node.is_a(&RNA_NodeGroup))
				from_pair = proxy_map[b_from_sock.ptr.data];
			else
				from_pair = node_socket_map_pair(node_map, b_from_node, b_from_sock);
		}
		else
			from_pair = sockets_map[b_from_sock.ptr.data];

		/* to sock */
		if(b_to_node) {
			if (b_to_node.mute() || b_to_node.is_a(&RNA_NodeGroup))
				to_pair = proxy_map[b_to_sock.ptr.data];
			else
				to_pair = node_socket_map_pair(node_map, b_to_node, b_to_sock);
		}
		else
			to_pair = sockets_map[b_to_sock.ptr.data];

		/* either node may be NULL when the node was not exported, typically
		 * because the node type is not supported */
		if(from_pair.first && to_pair.first) {
			ShaderOutput *output = from_pair.first->output(from_pair.second.c_str());
			ShaderInput *input = to_pair.first->input(to_pair.second.c_str());

			graph->connect(output, input);
		}
	}
}