예제 #1
0
static void xml_read_film(const XMLReadState& state, pugi::xml_node node)
{
	Camera *cam = state.scene->camera;

	xml_read_int(&cam->width, node, "width");
	xml_read_int(&cam->height, node, "height");

	float aspect = (float)cam->width/(float)cam->height;

	if(cam->width >= cam->height) {
		cam->left = -aspect;
		cam->right = aspect;
		cam->bottom = -1.0f;
		cam->top = 1.0f;
	}
	else {
		cam->left = -1.0f;
		cam->right = 1.0f;
		cam->bottom = -1.0f/aspect;
		cam->top = 1.0f/aspect;
	}

	cam->need_update = true;
	cam->update();
}
예제 #2
0
static void xml_read_camera(const XMLReadState& state, pugi::xml_node node)
{
	Camera *cam = state.scene->camera;

	xml_read_int(&cam->width, node, "width");
	xml_read_int(&cam->height, node, "height");

	if(xml_read_float(&cam->fov, node, "fov"))
		cam->fov = DEG2RADF(cam->fov);

	xml_read_float(&cam->nearclip, node, "nearclip");
	xml_read_float(&cam->farclip, node, "farclip");
	xml_read_float(&cam->aperturesize, node, "aperturesize"); // 0.5*focallength/fstop
	xml_read_float(&cam->focaldistance, node, "focaldistance");
	xml_read_float(&cam->shuttertime, node, "shuttertime");
	xml_read_float(&cam->aperture_ratio, node, "aperture_ratio");

	if(xml_equal_string(node, "type", "orthographic"))
		cam->type = CAMERA_ORTHOGRAPHIC;
	else if(xml_equal_string(node, "type", "perspective"))
		cam->type = CAMERA_PERSPECTIVE;
	else if(xml_equal_string(node, "type", "panorama"))
		cam->type = CAMERA_PANORAMA;

	if(xml_equal_string(node, "panorama_type", "equirectangular"))
		cam->panorama_type = PANORAMA_EQUIRECTANGULAR;
	else if(xml_equal_string(node, "panorama_type", "fisheye_equidistant"))
		cam->panorama_type = PANORAMA_FISHEYE_EQUIDISTANT;
	else if(xml_equal_string(node, "panorama_type", "fisheye_equisolid"))
		cam->panorama_type = PANORAMA_FISHEYE_EQUISOLID;

	xml_read_float(&cam->fisheye_fov, node, "fisheye_fov");
	xml_read_float(&cam->fisheye_lens, node, "fisheye_lens");

	xml_read_float(&cam->sensorwidth, node, "sensorwidth");
	xml_read_float(&cam->sensorheight, node, "sensorheight");

	cam->matrix = state.tfm;

	cam->need_update = true;
	cam->update();
}
예제 #3
0
static void xml_read_integrator(const XMLReadState& state, pugi::xml_node node)
{
	Integrator *integrator = state.scene->integrator;

	xml_read_int(&integrator->min_bounce, node, "min_bounce");
	xml_read_int(&integrator->max_bounce, node, "max_bounce");
	
	xml_read_int(&integrator->max_diffuse_bounce, node, "max_diffuse_bounce");
	xml_read_int(&integrator->max_glossy_bounce, node, "max_glossy_bounce");
	xml_read_int(&integrator->max_transmission_bounce, node, "max_transmission_bounce");
	
	xml_read_int(&integrator->transparent_min_bounce, node, "transparent_min_bounce");
	xml_read_int(&integrator->transparent_max_bounce, node, "transparent_max_bounce");
	
	xml_read_bool(&integrator->transparent_shadows, node, "transparent_shadows");
	xml_read_bool(&integrator->no_caustics, node, "no_caustics");
	
	xml_read_int(&integrator->seed, node, "seed");
}
예제 #4
0
static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pugi::xml_node graph_node)
{
	ShaderGraph *graph = new ShaderGraph();

	map<string, ShaderNode*> nodemap;

	nodemap["output"] = graph->output();

	for(pugi::xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
		ShaderNode *snode = NULL;

		if(string_iequals(node.name(), "image_texture")) {
			ImageTextureNode *img = new ImageTextureNode();

			xml_read_string(&img->filename, node, "src");
			img->filename = path_join(state.base, img->filename);
			
			xml_read_enum(&img->color_space, ImageTextureNode::color_space_enum, node, "color_space");
			xml_read_enum(&img->projection, ImageTextureNode::projection_enum, node, "projection");
			xml_read_float(&img->projection_blend, node, "projection_blend");

			snode = img;
		}
		else if(string_iequals(node.name(), "environment_texture")) {
			EnvironmentTextureNode *env = new EnvironmentTextureNode();

			xml_read_string(&env->filename, node, "src");
			env->filename = path_join(state.base, env->filename);
			
			xml_read_enum(&env->color_space, EnvironmentTextureNode::color_space_enum, node, "color_space");
			xml_read_enum(&env->projection, EnvironmentTextureNode::projection_enum, node, "projection");

			snode = env;
		}
		else if(string_iequals(node.name(), "osl_shader")) {
			OSLScriptNode *osl = new OSLScriptNode();

			/* Source */
			xml_read_string(&osl->filepath, node, "src");
			if(path_is_relative(osl->filepath)) {
				osl->filepath = path_join(state.base, osl->filepath);
			}

			/* Generate inputs/outputs from node sockets
			 *
			 * Note: ShaderInput/ShaderOutput store shallow string copies only!
			 * Socket names must be stored in the extra lists instead. */
			/* read input values */
			for(pugi::xml_node param = node.first_child(); param; param = param.next_sibling()) {
				if (string_iequals(param.name(), "input")) {
					string name;
					if (!xml_read_string(&name, param, "name"))
						continue;
					
					ShaderSocketType type = xml_read_socket_type(param, "type");
					if (type == SHADER_SOCKET_UNDEFINED)
						continue;
					
					osl->input_names.push_back(ustring(name));
					osl->add_input(osl->input_names.back().c_str(), type);
				}
				else if (string_iequals(param.name(), "output")) {
					string name;
					if (!xml_read_string(&name, param, "name"))
						continue;
					
					ShaderSocketType type = xml_read_socket_type(param, "type");
					if (type == SHADER_SOCKET_UNDEFINED)
						continue;
					
					osl->output_names.push_back(ustring(name));
					osl->add_output(osl->output_names.back().c_str(), type);
				}
			}
			
			snode = osl;
		}
		else if(string_iequals(node.name(), "sky_texture")) {
			SkyTextureNode *sky = new SkyTextureNode();
			
			xml_read_enum(&sky->type, SkyTextureNode::type_enum, node, "type");
			xml_read_float3(&sky->sun_direction, node, "sun_direction");
			xml_read_float(&sky->turbidity, node, "turbidity");
			xml_read_float(&sky->ground_albedo, node, "ground_albedo");
			
			snode = sky;
		}
		else if(string_iequals(node.name(), "noise_texture")) {
			snode = new NoiseTextureNode();
		}
		else if(string_iequals(node.name(), "checker_texture")) {
			snode = new CheckerTextureNode();
		}
		else if(string_iequals(node.name(), "brick_texture")) {
			BrickTextureNode *brick = new BrickTextureNode();

			xml_read_float(&brick->offset, node, "offset");
			xml_read_int(&brick->offset_frequency, node, "offset_frequency");
			xml_read_float(&brick->squash, node, "squash");
			xml_read_int(&brick->squash_frequency, node, "squash_frequency");

			snode = brick;
		}
		else if(string_iequals(node.name(), "gradient_texture")) {
			GradientTextureNode *blend = new GradientTextureNode();
			xml_read_enum(&blend->type, GradientTextureNode::type_enum, node, "type");
			snode = blend;
		}
		else if(string_iequals(node.name(), "voronoi_texture")) {
			VoronoiTextureNode *voronoi = new VoronoiTextureNode();
			xml_read_enum(&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring");
			snode = voronoi;
		}
		else if(string_iequals(node.name(), "musgrave_texture")) {
			MusgraveTextureNode *musgrave = new MusgraveTextureNode();
			xml_read_enum(&musgrave->type, MusgraveTextureNode::type_enum, node, "type");
			snode = musgrave;
		}
		else if(string_iequals(node.name(), "magic_texture")) {
			MagicTextureNode *magic = new MagicTextureNode();
			xml_read_int(&magic->depth, node, "depth");
			snode = magic;
		}
		else if(string_iequals(node.name(), "noise_texture")) {
			NoiseTextureNode *dist = new NoiseTextureNode();
			snode = dist;
		}
		else if(string_iequals(node.name(), "wave_texture")) {
			WaveTextureNode *wave = new WaveTextureNode();
			xml_read_enum(&wave->type, WaveTextureNode::type_enum, node, "type");
			snode = wave;
		}
		else if(string_iequals(node.name(), "normal")) {
			NormalNode *normal = new NormalNode();
			xml_read_float3(&normal->direction, node, "direction");
			snode = normal;
		}
		else if(string_iequals(node.name(), "mapping")) {
			snode = new MappingNode();
		}
		else if(string_iequals(node.name(), "ward_bsdf")) {
			snode = new WardBsdfNode();
		}
		else if(string_iequals(node.name(), "diffuse_bsdf")) {
			snode = new DiffuseBsdfNode();
		}
		else if(string_iequals(node.name(), "translucent_bsdf")) {
			snode = new TranslucentBsdfNode();
		}
		else if(string_iequals(node.name(), "transparent_bsdf")) {
			snode = new TransparentBsdfNode();
		}
		else if(string_iequals(node.name(), "velvet_bsdf")) {
			snode = new VelvetBsdfNode();
		}
		else if(string_iequals(node.name(), "toon_bsdf")) {
			ToonBsdfNode *toon = new ToonBsdfNode();
			xml_read_enum(&toon->component, ToonBsdfNode::component_enum, node, "component");
			snode = toon;
		}
		else if(string_iequals(node.name(), "glossy_bsdf")) {
			GlossyBsdfNode *glossy = new GlossyBsdfNode();
			xml_read_enum(&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution");
			snode = glossy;
		}
		else if(string_iequals(node.name(), "glass_bsdf")) {
			GlassBsdfNode *diel = new GlassBsdfNode();
			xml_read_enum(&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution");
			snode = diel;
		}
		else if(string_iequals(node.name(), "refraction_bsdf")) {
			RefractionBsdfNode *diel = new RefractionBsdfNode();
			xml_read_enum(&diel->distribution, RefractionBsdfNode::distribution_enum, node, "distribution");
			snode = diel;
		}
		else if(string_iequals(node.name(), "hair_bsdf")) {
			HairBsdfNode *hair = new HairBsdfNode();
			xml_read_enum(&hair->component, HairBsdfNode::component_enum, node, "component");
			snode = hair;
		}
		else if(string_iequals(node.name(), "emission")) {
			EmissionNode *emission = new EmissionNode();
			xml_read_bool(&emission->total_power, node, "total_power");
			snode = emission;
		}
		else if(string_iequals(node.name(), "ambient_occlusion")) {
			snode = new AmbientOcclusionNode();
		}
		else if(string_iequals(node.name(), "background")) {
			snode = new BackgroundNode();
		}
		else if(string_iequals(node.name(), "absorption_volume")) {
			snode = new AbsorptionVolumeNode();
		}
		else if(string_iequals(node.name(), "scatter_volume")) {
			snode = new ScatterVolumeNode();
		}
		else if(string_iequals(node.name(), "subsurface_scattering")) {
			SubsurfaceScatteringNode *sss = new SubsurfaceScatteringNode();
			//xml_read_enum(&sss->falloff, SubsurfaceScatteringNode::falloff_enum, node, "falloff");
			snode = sss;
		}
		else if(string_iequals(node.name(), "geometry")) {
			snode = new GeometryNode();
		}
		else if(string_iequals(node.name(), "texture_coordinate")) {
			snode = new TextureCoordinateNode();
		}
		else if(string_iequals(node.name(), "light_path")) {
			snode = new LightPathNode();
		}
		else if(string_iequals(node.name(), "light_falloff")) {
			snode = new LightFalloffNode();
		}
		else if(string_iequals(node.name(), "object_info")) {
			snode = new ObjectInfoNode();
		}
		else if(string_iequals(node.name(), "particle_info")) {
			snode = new ParticleInfoNode();
		}
		else if(string_iequals(node.name(), "hair_info")) {
			snode = new HairInfoNode();
		}
		else if(string_iequals(node.name(), "value")) {
			ValueNode *value = new ValueNode();
			xml_read_float(&value->value, node, "value");
			snode = value;
		}
		else if(string_iequals(node.name(), "color")) {
			ColorNode *color = new ColorNode();
			xml_read_float3(&color->value, node, "value");
			snode = color;
		}
		else if(string_iequals(node.name(), "mix_closure")) {
			snode = new MixClosureNode();
		}
		else if(string_iequals(node.name(), "add_closure")) {
			snode = new AddClosureNode();
		}
		else if(string_iequals(node.name(), "invert")) {
			snode = new InvertNode();
		}
		else if(string_iequals(node.name(), "mix")) {
			MixNode *mix = new MixNode();
			xml_read_enum(&mix->type, MixNode::type_enum, node, "type");
			xml_read_bool(&mix->use_clamp, node, "use_clamp");
			snode = mix;
		}
		else if(string_iequals(node.name(), "gamma")) {
			snode = new GammaNode();
		}
		else if(string_iequals(node.name(), "brightness")) {
			snode = new BrightContrastNode();
		}
		else if(string_iequals(node.name(), "combine_rgb")) {
			snode = new CombineRGBNode();
		}
		else if(string_iequals(node.name(), "separate_rgb")) {
			snode = new SeparateRGBNode();
		}
		else if(string_iequals(node.name(), "combine_hsv")) {
			snode = new CombineHSVNode();
		}
		else if(string_iequals(node.name(), "separate_hsv")) {
			snode = new SeparateHSVNode();
		}
		else if(string_iequals(node.name(), "hsv")) {
			snode = new HSVNode();
		}
		else if(string_iequals(node.name(), "wavelength")) {
			snode = new WavelengthNode();
		}
		else if(string_iequals(node.name(), "blackbody")) {
			snode = new BlackbodyNode();
		}
		else if(string_iequals(node.name(), "attribute")) {
			AttributeNode *attr = new AttributeNode();
			xml_read_ustring(&attr->attribute, node, "attribute");
			snode = attr;
		}
		else if(string_iequals(node.name(), "camera")) {
			snode = new CameraNode();
		}
		else if(string_iequals(node.name(), "fresnel")) {
			snode = new FresnelNode();
		}
		else if(string_iequals(node.name(), "layer_weight")) {
			snode = new LayerWeightNode();
		}
		else if(string_iequals(node.name(), "wireframe")) {
			WireframeNode *wire = new WireframeNode;
			xml_read_bool(&wire->use_pixel_size, node, "use_pixel_size");
			snode = wire;
		}
		else if(string_iequals(node.name(), "normal_map")) {
			NormalMapNode *nmap = new NormalMapNode;
			xml_read_ustring(&nmap->attribute, node, "attribute");
			xml_read_enum(&nmap->space, NormalMapNode::space_enum, node, "space");
			snode = nmap;
		}
		else if(string_iequals(node.name(), "tangent")) {
			TangentNode *tangent = new TangentNode;
			xml_read_ustring(&tangent->attribute, node, "attribute");
			xml_read_enum(&tangent->direction_type, TangentNode::direction_type_enum, node, "direction_type");
			xml_read_enum(&tangent->axis, TangentNode::axis_enum, node, "axis");
			snode = tangent;
		}
		else if(string_iequals(node.name(), "math")) {
			MathNode *math = new MathNode();
			xml_read_enum(&math->type, MathNode::type_enum, node, "type");
			xml_read_bool(&math->use_clamp, node, "use_clamp");
			snode = math;
		}
		else if(string_iequals(node.name(), "vector_math")) {
			VectorMathNode *vmath = new VectorMathNode();
			xml_read_enum(&vmath->type, VectorMathNode::type_enum, node, "type");
			snode = vmath;
		}
		else if(string_iequals(node.name(), "vector_transform")) {
			VectorTransformNode *vtransform = new VectorTransformNode();
			xml_read_enum(&vtransform->type, VectorTransformNode::type_enum, node, "type");
			xml_read_enum(&vtransform->convert_from, VectorTransformNode::convert_space_enum, node, "convert_from");
			xml_read_enum(&vtransform->convert_to, VectorTransformNode::convert_space_enum, node, "convert_to");
			snode = vtransform;
		}
		else if(string_iequals(node.name(), "connect")) {
			/* connect nodes */
			vector<string> from_tokens, to_tokens;

			string_split(from_tokens, node.attribute("from").value());
			string_split(to_tokens, node.attribute("to").value());

			if(from_tokens.size() == 2 && to_tokens.size() == 2) {
				/* find nodes and sockets */
				ShaderOutput *output = NULL;
				ShaderInput *input = NULL;

				if(nodemap.find(from_tokens[0]) != nodemap.end()) {
					ShaderNode *fromnode = nodemap[from_tokens[0]];

					foreach(ShaderOutput *out, fromnode->outputs)
						if(string_iequals(xml_socket_name(out->name), from_tokens[1]))
							output = out;

					if(!output)
						fprintf(stderr, "Unknown output socket name \"%s\" on \"%s\".\n", from_tokens[1].c_str(), from_tokens[0].c_str());
				}
				else
예제 #5
0
static void xml_read_integrator(const XMLReadState& state, pugi::xml_node node)
{
	Integrator *integrator = state.scene->integrator;
	
	/* Branched Path */
	bool branched = false;
	xml_read_bool(&branched, node, "branched");

	if(branched) {
		integrator->method = Integrator::BRANCHED_PATH;

		xml_read_int(&integrator->diffuse_samples, node, "diffuse_samples");
		xml_read_int(&integrator->glossy_samples, node, "glossy_samples");
		xml_read_int(&integrator->transmission_samples, node, "transmission_samples");
		xml_read_int(&integrator->ao_samples, node, "ao_samples");
		xml_read_int(&integrator->mesh_light_samples, node, "mesh_light_samples");
		xml_read_int(&integrator->subsurface_samples, node, "subsurface_samples");
		xml_read_int(&integrator->volume_samples, node, "volume_samples");
	}
	
	/* Bounces */
	xml_read_int(&integrator->min_bounce, node, "min_bounce");
	xml_read_int(&integrator->max_bounce, node, "max_bounce");
	
	xml_read_int(&integrator->max_diffuse_bounce, node, "max_diffuse_bounce");
	xml_read_int(&integrator->max_glossy_bounce, node, "max_glossy_bounce");
	xml_read_int(&integrator->max_transmission_bounce, node, "max_transmission_bounce");
	xml_read_int(&integrator->max_volume_bounce, node, "max_volume_bounce");
	
	/* Transparency */
	xml_read_int(&integrator->transparent_min_bounce, node, "transparent_min_bounce");
	xml_read_int(&integrator->transparent_max_bounce, node, "transparent_max_bounce");
	xml_read_bool(&integrator->transparent_shadows, node, "transparent_shadows");
	
	/* Volume */
	xml_read_int(&integrator->volume_homogeneous_sampling, node, "volume_homogeneous_sampling");
	xml_read_float(&integrator->volume_step_size, node, "volume_step_size");
	xml_read_int(&integrator->volume_max_steps, node, "volume_max_steps");
	
	/* Various Settings */
	xml_read_bool(&integrator->no_caustics, node, "no_caustics");
	xml_read_float(&integrator->filter_glossy, node, "filter_glossy");
	
	xml_read_int(&integrator->seed, node, "seed");
	xml_read_float(&integrator->sample_clamp_direct, node, "sample_clamp_direct");
	xml_read_float(&integrator->sample_clamp_indirect, node, "sample_clamp_indirect");
}
예제 #6
0
static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pugi::xml_node graph_node)
{
	ShaderGraph *graph = new ShaderGraph();

	map<string, ShaderNode*> nodemap;

	nodemap["output"] = graph->output();

	for(pugi::xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
		ShaderNode *snode = NULL;

		if(string_iequals(node.name(), "image_texture")) {
			ImageTextureNode *img = new ImageTextureNode();

			xml_read_string(&img->filename, node, "src");
			img->filename = path_join(state.base, img->filename);

			snode = img;
		}
		else if(string_iequals(node.name(), "environment_texture")) {
			EnvironmentTextureNode *env = new EnvironmentTextureNode();

			xml_read_string(&env->filename, node, "src");
			env->filename = path_join(state.base, env->filename);

			snode = env;
		}
		else if(string_iequals(node.name(), "sky_texture")) {
			SkyTextureNode *sky = new SkyTextureNode();

			xml_read_float3(&sky->sun_direction, node, "sun_direction");
			xml_read_float(&sky->turbidity, node, "turbidity");
			
			snode = sky;
		}
		else if(string_iequals(node.name(), "noise_texture")) {
			snode = new NoiseTextureNode();
		}
		else if(string_iequals(node.name(), "checker_texture")) {
			snode = new CheckerTextureNode();
		}
		else if(string_iequals(node.name(), "gradient_texture")) {
			GradientTextureNode *blend = new GradientTextureNode();
			xml_read_enum(&blend->type, GradientTextureNode::type_enum, node, "type");
			snode = blend;
		}
		else if(string_iequals(node.name(), "voronoi_texture")) {
			VoronoiTextureNode *voronoi = new VoronoiTextureNode();
			xml_read_enum(&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring");
			snode = voronoi;
		}
		else if(string_iequals(node.name(), "musgrave_texture")) {
			MusgraveTextureNode *musgrave = new MusgraveTextureNode();
			xml_read_enum(&musgrave->type, MusgraveTextureNode::type_enum, node, "type");
			snode = musgrave;
		}
		else if(string_iequals(node.name(), "magic_texture")) {
			MagicTextureNode *magic = new MagicTextureNode();
			xml_read_int(&magic->depth, node, "depth");
			snode = magic;
		}
		else if(string_iequals(node.name(), "noise_texture")) {
			NoiseTextureNode *dist = new NoiseTextureNode();
			snode = dist;
		}
		else if(string_iequals(node.name(), "wave_texture")) {
			WaveTextureNode *wood = new WaveTextureNode();
			xml_read_enum(&wood->type, WaveTextureNode::type_enum, node, "type");
			snode = wood;
		}
		else if(string_iequals(node.name(), "normal")) {
			snode = new NormalNode();
		}
		else if(string_iequals(node.name(), "mapping")) {
			snode = new MappingNode();
		}
		else if(string_iequals(node.name(), "ward_bsdf")) {
			snode = new WardBsdfNode();
		}
		else if(string_iequals(node.name(), "diffuse_bsdf")) {
			snode = new DiffuseBsdfNode();
		}
		else if(string_iequals(node.name(), "translucent_bsdf")) {
			snode = new TranslucentBsdfNode();
		}
		else if(string_iequals(node.name(), "transparent_bsdf")) {
			snode = new TransparentBsdfNode();
		}
		else if(string_iequals(node.name(), "velvet_bsdf")) {
			snode = new VelvetBsdfNode();
		}
		else if(string_iequals(node.name(), "glossy_bsdf")) {
			GlossyBsdfNode *glossy = new GlossyBsdfNode();
			xml_read_enum(&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution");
			snode = glossy;
		}
		else if(string_iequals(node.name(), "glass_bsdf")) {
			GlassBsdfNode *diel = new GlassBsdfNode();
			xml_read_enum(&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution");
			snode = diel;
		}
		else if(string_iequals(node.name(), "emission")) {
			EmissionNode *emission = new EmissionNode();
			xml_read_bool(&emission->total_power, node, "total_power");
			snode = emission;
		}
		else if(string_iequals(node.name(), "background")) {
			snode = new BackgroundNode();
		}
		else if(string_iequals(node.name(), "transparent_volume")) {
			snode = new TransparentVolumeNode();
		}
		else if(string_iequals(node.name(), "isotropic_volume")) {
			snode = new IsotropicVolumeNode();
		}
		else if(string_iequals(node.name(), "geometry")) {
			snode = new GeometryNode();
		}
		else if(string_iequals(node.name(), "texture_coordinate")) {
			snode = new TextureCoordinateNode();
		}
		else if(string_iequals(node.name(), "lightPath")) {
			snode = new LightPathNode();
		}
		else if(string_iequals(node.name(), "value")) {
			ValueNode *value = new ValueNode();
			xml_read_float(&value->value, node, "value");
			snode = value;
		}
		else if(string_iequals(node.name(), "color")) {
			ColorNode *color = new ColorNode();
			xml_read_float3(&color->value, node, "value");
			snode = color;
		}
		else if(string_iequals(node.name(), "mix_closure")) {
			snode = new MixClosureNode();
		}
		else if(string_iequals(node.name(), "add_closure")) {
			snode = new AddClosureNode();
		}
		else if(string_iequals(node.name(), "invert")) {
			snode = new InvertNode();
		}
		else if(string_iequals(node.name(), "mix")) {
			MixNode *mix = new MixNode();
			xml_read_enum(&mix->type, MixNode::type_enum, node, "type");
			snode = mix;
		}
		else if(string_iequals(node.name(), "gamma")) {
			snode = new GammaNode();
		}
		else if(string_iequals(node.name(), "brightness")) {
			snode = new BrightContrastNode();
		}
		else if(string_iequals(node.name(), "combine_rgb")) {
			snode = new CombineRGBNode();
		}
		else if(string_iequals(node.name(), "separate_rgb")) {
			snode = new SeparateRGBNode();
		}
		else if(string_iequals(node.name(), "hsv")) {
			snode = new HSVNode();
		}
		else if(string_iequals(node.name(), "attribute")) {
			AttributeNode *attr = new AttributeNode();
			xml_read_ustring(&attr->attribute, node, "attribute");
			snode = attr;
		}
		else if(string_iequals(node.name(), "camera")) {
			snode = new CameraNode();
		}
		else if(string_iequals(node.name(), "fresnel")) {
			snode = new FresnelNode();
		}
		else if(string_iequals(node.name(), "math")) {
			MathNode *math = new MathNode();
			xml_read_enum(&math->type, MathNode::type_enum, node, "type");
			snode = math;
		}
		else if(string_iequals(node.name(), "vector_math")) {
			VectorMathNode *vmath = new VectorMathNode();
			xml_read_enum(&vmath->type, VectorMathNode::type_enum, node, "type");
			snode = vmath;
		}
		else if(string_iequals(node.name(), "connect")) {
			/* connect nodes */
			vector<string> from_tokens, to_tokens;

			string_split(from_tokens, node.attribute("from").value());
			string_split(to_tokens, node.attribute("to").value());

			if(from_tokens.size() == 2 && to_tokens.size() == 2) {
				/* find nodes and sockets */
				ShaderOutput *output = NULL;
				ShaderInput *input = NULL;

				if(nodemap.find(from_tokens[0]) != nodemap.end()) {
					ShaderNode *fromnode = nodemap[from_tokens[0]];

					foreach(ShaderOutput *out, fromnode->outputs)
						if(string_iequals(xml_socket_name(out->name), from_tokens[1]))
							output = out;

					if(!output)
						fprintf(stderr, "Unknown output socket name \"%s\" on \"%s\".\n", from_tokens[1].c_str(), from_tokens[0].c_str());
				}
				else