SetSceneBlending() публичный Метод

Allows very fine control of blending this Pass with the existing contents of the scene.
Wheras the texture blending operations seen in the TextureUnitState class are concerned with blending between texture layers, this blending is about combining the output of the material as a whole with the existing contents of the rendering target. This blending therefore allows object transparency and other special effects.

This version of the method allows complete control over the blending operation, by specifying the source and destination blending factors. The result of the blending operation is: final = (texture * sourceFactor) + (pixel * destFactor)

Each of the factors is specified as one of a number of options, as specified in the SceneBlendFactor enumerated type.

This method is applicable for both the fixed-function and programmable pipelines.

public SetSceneBlending ( SceneBlendFactor src, SceneBlendFactor dest ) : void
src SceneBlendFactor The source factor in the above calculation, i.e. multiplied by the texture color components.
dest SceneBlendFactor The destination factor in the above calculation, i.e. multiplied by the pixel color components.
Результат void
Пример #1
0
		/// <summary>
		///		Internal method for splitting the passes into illumination passes.
		/// </summary>
		public void CompileIlluminationPasses()
		{
			ClearIlluminationPasses();

			// don't need to split transparent passes since they are rendered seperately
			if ( this.IsTransparent )
			{
				return;
			}

			// start off with ambient passes
			IlluminationStage stage = IlluminationStage.Ambient;

			bool hasAmbient = false;

			for ( int i = 0; i < _passes.Count; /* increment in logic */)
			{
				Pass pass = (Pass)_passes[ i ];
				IlluminationPass iPass;

				switch ( stage )
				{
					case IlluminationStage.Ambient:
						// keep looking for ambient only
						if ( pass.IsAmbientOnly )
						{
							iPass = new IlluminationPass();
							iPass.OriginalPass = pass;
							iPass.Pass = pass;
							iPass.Stage = stage;
							_illuminationPasses.Add( iPass );
							hasAmbient = true;

							// progress to the next pass
							i++;
						}
						else
						{
							// split off any ambient part
							if ( pass.Ambient.CompareTo( ColorEx.Black ) != 0 ||
								pass.Emissive.CompareTo( ColorEx.Black ) != 0 )
							{

								Pass newPass = new Pass( this, pass.Index );
								pass.CopyTo( newPass );

								// remove any texture units
								newPass.RemoveAllTextureUnitStates();

								// also remove any fragment program
								if ( newPass.HasFragmentProgram )
								{
									newPass.SetFragmentProgram( "" );
								}

								// We have to leave vertex program alone (if any) and
								// just trust that the author is using light bindings, which 
								// we will ensure there are none in the ambient pass
								newPass.Diffuse = ColorEx.Black;
								newPass.Specular = ColorEx.Black;

								// if ambient and emissive are zero, then color write isn't needed
								if ( newPass.Ambient.CompareTo( ColorEx.Black ) == 0 &&
									newPass.Emissive.CompareTo( ColorEx.Black ) == 0 )
								{

									newPass.ColorWriteEnabled = false;
								}

								iPass = new IlluminationPass();
								iPass.DestroyOnShutdown = true;
								iPass.OriginalPass = pass;
								iPass.Pass = newPass;
								iPass.Stage = stage;

								_illuminationPasses.Add( iPass );
								hasAmbient = true;
							}

							if ( !hasAmbient )
							{
								// make up a new basic pass
								Pass newPass = new Pass( this, pass.Index );
								pass.CopyTo( newPass );

								newPass.Ambient = ColorEx.Black;
								newPass.Diffuse = ColorEx.Black;

								iPass = new IlluminationPass();
								iPass.DestroyOnShutdown = true;
								iPass.OriginalPass = pass;
								iPass.Pass = newPass;
								iPass.Stage = stage;
								_illuminationPasses.Add( iPass );
								hasAmbient = true;
							}

							// this means we are done with ambients, progress to per-light
							stage = IlluminationStage.PerLight;
						}

						break;

					case IlluminationStage.PerLight:
						if ( pass.IteratePerLight )
						{
							// if this is per-light already, use it directly
							iPass = new IlluminationPass();
							iPass.DestroyOnShutdown = false;
							iPass.OriginalPass = pass;
							iPass.Pass = pass;
							iPass.Stage = stage;
							_illuminationPasses.Add( iPass );

							// progress to the next pass
							i++;
						}
						else
						{
							// split off per-light details (can only be done for one)
							if ( pass.LightingEnabled &&
								( pass.Diffuse.CompareTo( ColorEx.Black ) != 0 ||
								pass.Specular.CompareTo( ColorEx.Black ) != 0 ) )
							{

								// copy existing pass
								Pass newPass = new Pass( this, pass.Index );
								pass.CopyTo( newPass );

								newPass.RemoveAllTextureUnitStates();

								// also remove any fragment program
								if ( newPass.HasFragmentProgram )
								{
									newPass.SetFragmentProgram( "" );
								}

								// Cannot remove vertex program, have to assume that
								// it will process diffuse lights, ambient will be turned off
								newPass.Ambient = ColorEx.Black;
								newPass.Emissive = ColorEx.Black;

								// must be additive
								newPass.SetSceneBlending( SceneBlendFactor.One, SceneBlendFactor.One );

								iPass = new IlluminationPass();
								iPass.DestroyOnShutdown = true;
								iPass.OriginalPass = pass;
								iPass.Pass = newPass;
								iPass.Stage = stage;

								_illuminationPasses.Add( iPass );
							}

							// This means the end of per-light passes
							stage = IlluminationStage.Decal;
						}

						break;

					case IlluminationStage.Decal:
						// We just want a 'lighting off' pass to finish off
						// and only if there are texture units
						if ( pass.TextureUnitStageCount > 0 )
						{
							if ( !pass.LightingEnabled )
							{
								// we assume this pass already combines as required with the scene
								iPass = new IlluminationPass();
								iPass.DestroyOnShutdown = false;
								iPass.OriginalPass = pass;
								iPass.Pass = pass;
								iPass.Stage = stage;
								_illuminationPasses.Add( iPass );
							}
							else
							{
								// Copy the pass and tweak away the lighting parts
								Pass newPass = new Pass( this, pass.Index );
								pass.CopyTo( newPass );
								newPass.Ambient = ColorEx.Black;
								newPass.Diffuse = ColorEx.Black;
								newPass.Specular = ColorEx.Black;
								newPass.Emissive = ColorEx.Black;
								newPass.LightingEnabled = false;
								// modulate
								newPass.SetSceneBlending( SceneBlendFactor.DestColor, SceneBlendFactor.Zero );

								// there is nothing we can do about vertex & fragment
								// programs here, so people will just have to make their
								// programs friendly-like if they want to use this technique
								iPass = new IlluminationPass();
								iPass.DestroyOnShutdown = true;
								iPass.OriginalPass = pass;
								iPass.Pass = newPass;
								iPass.Stage = stage;
								_illuminationPasses.Add( iPass );
							}
						}

						// always increment on decal, since nothing more to do with this pass
						i++;

						break;
				}
			}

			_compiledIlluminationPasses = true;
		}
Пример #2
0
			/// <see cref="Translator.Translate"/>
			public override void Translate( ScriptCompiler compiler, AbstractNode node )
			{
				ObjectAbstractNode obj = (ObjectAbstractNode)node;

				Technique technique = (Technique)obj.Parent.Context;
				_pass = technique.CreatePass();
				obj.Context = _pass;

				// Get the name of the technique
				if ( !string.IsNullOrEmpty( obj.Name ) )
					_pass.Name = obj.Name;

				// Set the properties for the material
				foreach ( AbstractNode i in obj.Children )
				{
					if ( i is PropertyAbstractNode )
					{
						PropertyAbstractNode prop = (PropertyAbstractNode)i;

						switch ( (Keywords)prop.Id )
						{
							#region ID_AMBIENT
							case Keywords.ID_AMBIENT:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 4 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"ambient must have at most 4 parameters" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode &&
										( (AtomAbstractNode)prop.Values[ 0 ] ).Id == (uint)Keywords.ID_VERTEX_COLOUR )
									{
										_pass.VertexColorTracking = _pass.VertexColorTracking | TrackVertexColor.Ambient;
									}
									else
									{
										ColorEx val = ColorEx.White;
										if ( getColor( prop.Values, 0, out val ) )
											_pass.Ambient = val;
										else
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
												"ambient requires 3 or 4 colour arguments, or a \"vertexcolour\" directive" );
									}
								}
								break;
							#endregion ID_AMBIENT

							#region ID_DIFFUSE
							case Keywords.ID_DIFFUSE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 4 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"diffuse must have at most 4 arguments" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode &&
										( (AtomAbstractNode)prop.Values[ 0 ] ).Id == (uint)Keywords.ID_VERTEX_COLOUR )
									{
										_pass.VertexColorTracking = _pass.VertexColorTracking | TrackVertexColor.Diffuse;
									}
									else
									{
										ColorEx val;
										if ( getColor( prop.Values, 0, out val ) )
											_pass.Diffuse = val;
										else
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
												"diffuse requires 3 or 4 colour arguments, or a \"vertexcolour\" directive" );
									}
								}
								break;
							#endregion ID_DIFFUSE

							#region ID_SPECULAR
							case Keywords.ID_SPECULAR:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 5 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"specular must have at most 5 arguments" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode &&
										( (AtomAbstractNode)prop.Values[ 0 ] ).Id == (uint)Keywords.ID_VERTEX_COLOUR )
									{
										_pass.VertexColorTracking = _pass.VertexColorTracking | TrackVertexColor.Specular;

										if ( prop.Values.Count >= 2 )
										{
											Real val = 0;
											if ( getReal( prop.Values[ prop.Values.Count - 1 ], out val ) )
												_pass.Shininess = val;
											else
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													"specular does not support \"" + prop.Values[ prop.Values.Count - 1 ].Value + "\" as its second argument" );
											}
										}
									}
									else
									{
										if ( prop.Values.Count < 4 )
										{
											compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line,
												"specular expects at least 4 arguments" );
										}
										else
										{
											AbstractNode i0 = getNodeAt( prop.Values, 0 ),
											i1 = getNodeAt( prop.Values, 1 ),
											i2 = getNodeAt( prop.Values, 2 );
											ColorEx val = new ColorEx( 0, 0, 0, 0 );
											if ( getFloat( i0, out val.r ) && getFloat( i1, out val.g ) && getFloat( i2, out val.b ) )
											{
												if ( prop.Values.Count == 4 )
												{
													_pass.Specular = val;

													AbstractNode i3 = getNodeAt( prop.Values, 3 );

													Real shininess = 0.0f;
													if ( getReal( i3, out shininess ) )
														_pass.Shininess = shininess;
													else
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
															 "specular fourth argument must be a valid number for shininess attribute" );
												}
												else
												{
													AbstractNode i3 = getNodeAt( prop.Values, 3 );
													if ( !getFloat( i3, out val.a ) )
													{
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
															"specular fourth argument must be a valid color component value" );
													}
													else
														_pass.Specular = val;

													AbstractNode i4 = getNodeAt( prop.Values, 4 );

													Real shininess = 0.0f;
													if ( getReal( i4, out shininess ) )
														_pass.Shininess = shininess;
													else
													{
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
															"specular fourth argument must be a valid number for shininess attribute" );
													}
												}
											}
											else
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													"specular must have first 3 arguments be a valid colour" );
											}
										}
									}
								}
								break;
							#endregion ID_SPECULAR

							#region ID_EMISSIVE
							case Keywords.ID_EMISSIVE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 4 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"emissive must have at most 4 arguments" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode &&
													( (AtomAbstractNode)prop.Values[ 0 ] ).Id == (uint)Keywords.ID_VERTEX_COLOUR )
									{
										_pass.VertexColorTracking = _pass.VertexColorTracking | TrackVertexColor.Emissive;
									}
									else
									{
										ColorEx val;
										if ( getColor( prop.Values, 0, out val ) )
											_pass.SelfIllumination = val;
										else
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
												"emissive requires 3 or 4 colour arguments, or a \"vertexcolour\" directive" );
									}
								}
								break;
							#endregion ID_EMISSIVE

							#region ID_SCENE_BLEND
							case Keywords.ID_SCENE_BLEND:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 2 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"scene_blend supports at most 2 arguments" );
								}
								else if ( prop.Values.Count == 1 )
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_ADD:
												_pass.SetSceneBlending( SceneBlendType.Add );
												break;

											case Keywords.ID_MODULATE:
												_pass.SetSceneBlending( SceneBlendType.Modulate );
												break;

											case Keywords.ID_COLOUR_BLEND:
												_pass.SetSceneBlending( SceneBlendType.TransparentColor );

												break;

											case Keywords.ID_ALPHA_BLEND:
												_pass.SetSceneBlending( SceneBlendType.TransparentAlpha );
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													"scene_blend does not support \"" + prop.Values[ 0 ].Value + "\" for argument 1" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"scene_blend does not support \"" + prop.Values[ 0 ].Value + "\" for argument 1" );
									}
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 );
									SceneBlendFactor sbf0, sbf1;
									if ( getEnumeration<SceneBlendFactor>( i0, compiler, out sbf0 ) && getEnumeration<SceneBlendFactor>( i1, compiler, out sbf1 ) )
									{
										_pass.SetSceneBlending( sbf0, sbf1 );
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"scene_blend does not support \"" + i0.Value + "\" and \"" + i1.Value + "\" as arguments" );
									}
								}
								break;
							#endregion ID_SCENE_BLEND

							#region ID_SEPARATE_SCENE_BLEND
							case Keywords.ID_SEPARATE_SCENE_BLEND:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count == 3 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"separate_scene_blend must have 2 or 4 arguments" );
								}
								else if ( prop.Values.Count > 4 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"separate_scene_blend must have 2 or 4 arguments" );
								}
								else if ( prop.Values.Count == 2 )
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 );
									if ( i0 is AtomAbstractNode && i1 is AtomAbstractNode )
									{
										AtomAbstractNode atom0 = (AtomAbstractNode)i0, atom1 = (AtomAbstractNode)i1;
										SceneBlendType sbt0, sbt1;
										switch ( (Keywords)atom0.Id )
										{
											case Keywords.ID_ADD:
												sbt0 = SceneBlendType.Add;
												break;

											case Keywords.ID_MODULATE:
												sbt0 = SceneBlendType.Modulate;
												break;

											case Keywords.ID_COLOUR_BLEND:
												sbt0 = SceneBlendType.TransparentColor;
												break;

											case Keywords.ID_ALPHA_BLEND:
												sbt0 = SceneBlendType.TransparentAlpha;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													"separate_scene_blend does not support \"" + atom0.Value + "\" as argument 1" );
												return;
										}

										switch ( (Keywords)atom1.Id )
										{
											case Keywords.ID_ADD:
												sbt1 = SceneBlendType.Add;
												break;

											case Keywords.ID_MODULATE:
												sbt1 = SceneBlendType.Modulate;
												break;

											case Keywords.ID_COLOUR_BLEND:
												sbt1 = SceneBlendType.TransparentColor;
												break;

											case Keywords.ID_ALPHA_BLEND:
												sbt1 = SceneBlendType.TransparentAlpha;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													"separate_scene_blend does not support \"" + atom1.Value + "\" as argument 2" );
												return;
										}

									    throw new NotImplementedException(
                                            string.Format("SetSeparateSceneBlending({0}, {1})", sbt0, sbt1));
									    //TODO
									    //mPass->setSeparateSceneBlending(sbt0, sbt1);
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"separate_scene_blend does not support \"" + i0.Value + "\" as argument 1" );
									}
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 ),
										i2 = getNodeAt( prop.Values, 2 ), i3 = getNodeAt( prop.Values, 3 );

									if ( i0 is AtomAbstractNode && i1 is AtomAbstractNode
										&& i2 is AtomAbstractNode && i3 is AtomAbstractNode )
									{
										SceneBlendFactor sbf0, sbf1, sbf2, sbf3;
										if ( getEnumeration<SceneBlendFactor>( i0, compiler, out sbf0 ) && getEnumeration<SceneBlendFactor>( i1, compiler, out sbf1 )
											&& getEnumeration<SceneBlendFactor>( i2, compiler, out sbf2 ) && getEnumeration<SceneBlendFactor>( i3, compiler, out sbf3 ) )
										{
											//TODO
											//mPass->setSeparateSceneBlending(sbf0, sbf1, sbf2, sbf3);
										}
										else
										{
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
												"one of the arguments to separate_scene_blend is not a valid scene blend factor directive" );
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"one of the arguments to separate_scene_blend is not a valid scene blend factor directive" );
									}
								}
								break;
							#endregion ID_SEPARATE_SCENE_BLEND

							#region ID_SCENE_BLEND_OP
							case Keywords.ID_SCENE_BLEND_OP:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"scene_blend_op must have 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];

										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_ADD:
												//TODO
												//mPass->setSceneBlendingOperation(SBO_ADD);
												break;

											case Keywords.ID_SUBTRACT:
												//TODO
												//mPass->setSceneBlendingOperation(SBO_SUBTRACT);
												break;

											case Keywords.ID_REVERSE_SUBTRACT:
												//TODO
												//mPass->setSceneBlendingOperation(SBO_REVERSE_SUBTRACT);
												break;

											case Keywords.ID_MIN:
												//TODO
												//mPass->setSceneBlendingOperation(SBO_MIN);
												break;

											case Keywords.ID_MAX:
												//TODO
												//mPass->setSceneBlendingOperation(SBO_MAX);
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													atom.Value + ": unrecognized argument" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + ": unrecognized argument" );
									}
								}
								break;
							#endregion ID_SCENE_BLEND_OP

							#region ID_SEPARATE_SCENE_BLEND_OP
							case Keywords.ID_SEPARATE_SCENE_BLEND_OP:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count != 2 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"separate_scene_blend_op must have 2 arguments" );
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 );
									if ( i0 is AtomAbstractNode && i1 is AtomAbstractNode )
									{
										AtomAbstractNode atom0 = (AtomAbstractNode)i0,
											atom1 = (AtomAbstractNode)i1;

										//TODO
										//SceneBlendOperation op = SBO_ADD, alphaOp = SBO_ADD;
										switch ( (Keywords)atom0.Id )
										{
											case Keywords.ID_ADD:
												//TODO
												//op = SBO_ADD;
												break;

											case Keywords.ID_SUBTRACT:
												//TODO
												//op = SBO_SUBTRACT;
												break;

											case Keywords.ID_REVERSE_SUBTRACT:
												//TODO
												//op = SBO_REVERSE_SUBTRACT;
												break;

											case Keywords.ID_MIN:
												//TODO
												//op = SBO_MIN;
												break;

											case Keywords.ID_MAX:
												//TODO
												//op = SBO_MAX;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													atom0.Value + ": unrecognized first argument" );
												break;
										}

										switch ( (Keywords)atom1.Id )
										{
											case Keywords.ID_ADD:
												//TODO
												//alphaOp = SBO_ADD;
												break;

											case Keywords.ID_SUBTRACT:
												//TODO
												//alphaOp = SBO_SUBTRACT;
												break;

											case Keywords.ID_REVERSE_SUBTRACT:
												//TODO
												//alphaOp = SBO_REVERSE_SUBTRACT;
												break;

											case Keywords.ID_MIN:
												//TODO
												//alphaOp = SBO_MIN;
												break;

											case Keywords.ID_MAX:
												//TODO
												//alphaOp = SBO_MAX;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													atom1.Value + ": unrecognized second argument" );
												break;
										}

										//TODO
										//mPass->setSeparateSceneBlendingOperation(op, alphaOp);
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + ": unrecognized argument" );
									}
								}
								break;
							#endregion ID_SEPARATE_SCENE_BLEND_OP

							#region ID_DEPTH_CHECK
							case Keywords.ID_DEPTH_CHECK:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"depth_check must have 1 argument" );
								}
								else
								{
									bool val = true;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.DepthCheck = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"depth_check third argument must be \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\"" );
								}
								break;
							#endregion ID_DEPTH_CHECK

							#region ID_DEPTH_WRITE
							case Keywords.ID_DEPTH_WRITE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"depth_write must have 1 argument" );
								}
								else
								{
									bool val = true;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.DepthWrite = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"depth_write third argument must be \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\"" );
								}
								break;
							#endregion ID_DEPTH_WRITE

							#region ID_DEPTH_BIAS
							case Keywords.ID_DEPTH_BIAS:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 2 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"depth_bias must have at most 2 arguments" );
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 );
									float val0, val1 = 0.0f;
									if ( getFloat( i0, out val0 ) )
									{
										if ( i1 != null )
											getFloat( i1, out val1 );

										_pass.SetDepthBias( val0, val1 );
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											"depth_bias does not support \"" + i0.Value + "\" for argument 1" );
									}
								}
								break;
							#endregion ID_DEPTH_BIAS

							#region ID_DEPTH_FUNC
							case Keywords.ID_DEPTH_FUNC:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"depth_func must have 1 argument" );
								}
								else
								{
									CompareFunction func;
									if ( getEnumeration<CompareFunction>( prop.Values[ 0 ], compiler, out func ) )
										_pass.DepthFunction = func;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid CompareFunction" );
								}
								break;
							#endregion ID_DEPTH_FUNC

							#region ID_ITERATION_DEPTH_BIAS
							case Keywords.ID_ITERATION_DEPTH_BIAS:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"iteration_depth_bias must have 1 argument" );
								}
								else
								{
									float val = 0.0f;
									if ( getFloat( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setIterationDepthBias(val);*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid float value" );
								}
								break;
							#endregion ID_ITERATION_DEPTH_BIAS

							#region ID_ALPHA_REJECTION
							case Keywords.ID_ALPHA_REJECTION:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 2 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"alpha_rejection must have at most 2 arguments" );
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 );
									CompareFunction func;
									if ( getEnumeration<CompareFunction>( i0, compiler, out func ) )
									{
										if ( i1 != null )
										{
											int val = 0;
											if ( getInt( i1, out val ) )
												_pass.SetAlphaRejectSettings( func, val );
											else
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i1.Value + " is not a valid integer" );
										}
										else
											_pass.AlphaRejectFunction = func;
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											i0.Value + " is not a valid CompareFunction" );
								}
								break;
							#endregion ID_ALPHA_REJECTION

							#region ID_ALPHA_TO_COVERAGE
							case Keywords.ID_ALPHA_TO_COVERAGE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"alpha_to_coverage must have 1 argument" );
								}
								else
								{
									bool val = true;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.IsAlphaToCoverageEnabled = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
										"alpha_to_coverage argument must be \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\"" );
								}
								break;
							#endregion ID_ALPHA_TO_COVERAGE

							#region ID_LIGHT_SCISSOR
							case Keywords.ID_LIGHT_SCISSOR:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"light_scissor must have only 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setLightScissoringEnabled(val);*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_LIGHT_SCISSOR

							#region ID_LIGHT_CLIP_PLANES
							case Keywords.ID_LIGHT_CLIP_PLANES:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"light_clip_planes must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setLightClipPlanesEnabled(val);*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_LIGHT_CLIP_PLANES

							#region ID_TRANSPARENT_SORTING
							case Keywords.ID_TRANSPARENT_SORTING:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"transparent_sorting must have at most 1 argument" );
								}
								else
								{
									bool val = true;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										//TODO
										//mPass->setTransparentSortingEnabled(val);
										//mPass->setTransparentSortingForced(false);
									}
									else
									{
										string val2;
										if ( getString( prop.Values[ 0 ], out val2 ) && val2 == "force" )
										{
											//TODO
											//mPass->setTransparentSortingEnabled(true);
											//mPass->setTransparentSortingForced(true);
										}
										else
										{
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
												prop.Values[ 0 ].Value + " must be boolean or force" );
										}
									}
								}
								break;
							#endregion ID_TRANSPARENT_SORTING

							#region ID_ILLUMINATION_STAGE
							case Keywords.ID_ILLUMINATION_STAGE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"illumination_stage must have at most 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_AMBIENT:
												//TODO
												//mPass->setIlluminationStage(IS_AMBIENT);
												break;

											case Keywords.ID_PER_LIGHT:
												//TODO
												//mPass->setIlluminationStage(IS_PER_LIGHT);
												break;

											case Keywords.ID_DECAL:
												//TODO
												//mPass->setIlluminationStage(IS_DECAL);
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													prop.Values[ 0 ].Value + " is not a valid IlluminationStage" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid IlluminationStage" );
									}
								}
								break;
							#endregion ID_ILLUMINATION_STAGE

							#region ID_CULL_HARDWARE
							case Keywords.ID_CULL_HARDWARE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"cull_hardware must have at most 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_CLOCKWISE:
												_pass.CullingMode = CullingMode.Clockwise;
												break;

											case Keywords.ID_ANTICLOCKWISE:
												_pass.CullingMode = CullingMode.CounterClockwise;
												break;

											case Keywords.ID_NONE:
												_pass.CullingMode = CullingMode.None;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													prop.Values[ 0 ].Value + " is not a valid CullingMode" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid CullingMode" );
									}
								}
								break;
							#endregion ID_CULL_HARDWARE

							#region ID_CULL_SOFTWARE
							case Keywords.ID_CULL_SOFTWARE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"cull_software must have at most 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_FRONT:
												_pass.ManualCullingMode = ManualCullingMode.Front;
												break;

											case Keywords.ID_BACK:
												_pass.ManualCullingMode = ManualCullingMode.Back;
												break;

											case Keywords.ID_NONE:
												_pass.ManualCullingMode = ManualCullingMode.None;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													prop.Values[ 0 ].Value + " is not a valid ManualCullingMode" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid ManualCullingMode" );
									}
								}
								break;
							#endregion ID_CULL_SOFTWARE

							#region ID_NORMALISE_NORMALS
							case Keywords.ID_NORMALISE_NORMALS:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"normalise_normals must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setNormaliseNormals(val);*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_NORMALISE_NORMALS

							#region ID_LIGHTING
							case Keywords.ID_LIGHTING:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"lighting must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.LightingEnabled = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_LIGHTING

							#region ID_SHADING
							case Keywords.ID_SHADING:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"shading must have at most 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_FLAT:
												_pass.ShadingMode = Shading.Flat;
												break;

											case Keywords.ID_GOURAUD:
												_pass.ShadingMode = Shading.Gouraud;
												break;

											case Keywords.ID_PHONG:
												_pass.ShadingMode = Shading.Phong;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													prop.Values[ 0 ].Value + " is not a valid shading mode (flat, gouraud, or phong)" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid shading mode (flat, gouraud, or phong)" );
									}
								}
								break;
							#endregion ID_SHADING

							#region ID_POLYGON_MODE
							case Keywords.ID_POLYGON_MODE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"polygon_mode must have at most 1 argument" );
								}
								else
								{
									if ( prop.Values[ 0 ] is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)prop.Values[ 0 ];
										switch ( (Keywords)atom.Id )
										{
											case Keywords.ID_SOLID:
												_pass.PolygonMode = PolygonMode.Solid;
												break;

											case Keywords.ID_POINTS:
												_pass.PolygonMode = PolygonMode.Points;
												break;

											case Keywords.ID_WIREFRAME:
												_pass.PolygonMode = PolygonMode.Wireframe;
												break;

											default:
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													prop.Values[ 0 ].Value + " is not a valid polygon mode (solid, points, or wireframe)" );
												break;
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid polygon mode (solid, points, or wireframe)" );
									}
								}
								break;
							#endregion ID_POLYGON_MODE

							#region ID_POLYGON_MODE_OVERRIDEABLE
							case Keywords.ID_POLYGON_MODE_OVERRIDEABLE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"polygon_mode_overrideable must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setPolygonModeOverrideable(val);*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_POLYGON_MODE_OVERRIDEABLE

							#region ID_FOG_OVERRIDE
							case Keywords.ID_FOG_OVERRIDE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 8 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"fog_override must have at most 8 arguments" );
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 ), i1 = getNodeAt( prop.Values, 1 ), i2 = getNodeAt( prop.Values, 2 );
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										FogMode mode = FogMode.None;
										ColorEx clr = ColorEx.White;

										Real dens = 0.001, start = 0.0f, end = 1.0f;

										if ( i1 != null )
										{
											if ( i1 is AtomAbstractNode )
											{
												AtomAbstractNode atom = (AtomAbstractNode)i1;
												switch ( (Keywords)atom.Id )
												{
													case Keywords.ID_NONE:
														mode = FogMode.None;
														break;

													case Keywords.ID_LINEAR:
														mode = FogMode.Linear;
														break;

													case Keywords.ID_EXP:
														mode = FogMode.Exp;
														break;

													case Keywords.ID_EXP2:
														mode = FogMode.Exp2;
														break;

													default:
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
															i1.Value + " is not a valid FogMode" );
														break;
												}
											}
											else
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i1.Value + " is not a valid FogMode" );
												break;
											}
										}

										if ( i2 != null )
										{
											// following line code was if(!getColour(i2, prop->values.end(), &clr, 3))
											if ( !getColor( prop.Values, 2, out clr, 3 ) )
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i2.Value + " is not a valid colour" );
												break;
											}

											i2 = getNodeAt( prop.Values, 5 );
										}

										if ( i2 != null )
										{
											if ( !getReal( i2, out dens ) )
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i2.Value + " is not a valid number" );
												break;
											}
											//++i2;
											i2 = getNodeAt( prop.Values, 6 );
										}

										if ( i2 != null )
										{
											if ( !getReal( i2, out start ) )
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i2.Value + " is not a valid number" );
												return;
											}
											//++i2;
											i2 = getNodeAt( prop.Values, 7 );
										}

										if ( i2 != null )
										{
											if ( !getReal( i2, out end ) )
											{
												compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
													i2.Value + " is not a valid number" );
												return;
											}
											//++i2;
											i2 = getNodeAt( prop.Values, 8 );
										}

										_pass.SetFog( val, mode, clr, dens, start, end );
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_FOG_OVERRIDE

							#region ID_COLOUR_WRITE
							case Keywords.ID_COLOUR_WRITE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"colour_write must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.ColorWriteEnabled = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_COLOUR_WRITE

							#region ID_MAX_LIGHTS
							case Keywords.ID_MAX_LIGHTS:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"max_lights must have at most 1 argument" );
								}
								else
								{
									int val = 0;
									if ( getInt( prop.Values[ 0 ], out val ) )
										_pass.MaxSimultaneousLights = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid integer" );
								}
								break;
							#endregion ID_MAX_LIGHTS

							#region ID_START_LIGHT
							case Keywords.ID_START_LIGHT:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"start_light must have at most 1 argument" );
								}
								else
								{
									uint val = 0;
									if ( getUInt( prop.Values[ 0 ], out val ) )
									{
										//TODO
										/*mPass->setStartLight(static_cast<unsigned short>(val));*/
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid integer" );
								}
								break;
							#endregion ID_START_LIGHT

							#region ID_ITERATION
							case Keywords.ID_ITERATION:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else
								{
									AbstractNode i0 = getNodeAt( prop.Values, 0 );
									if ( i0 is AtomAbstractNode )
									{
										AtomAbstractNode atom = (AtomAbstractNode)i0;
										if ( atom.Id == (uint)Keywords.ID_ONCE )
										{
											_pass.IteratePerLight = false;
										}
										else if ( atom.Id == (uint)Keywords.ID_ONCE_PER_LIGHT )
										{
											AbstractNode i1 = getNodeAt( prop.Values, 1 );
											if ( i1 != null && i1 is AtomAbstractNode )
											{
												atom = (AtomAbstractNode)i1;
												switch ( (Keywords)atom.Id )
												{
													case Keywords.ID_POINT:
														_pass.IteratePerLight = true;
														break;

													case Keywords.ID_DIRECTIONAL:
														//TODO
														//_pass.SetIteratePerLight(true, true, LightType.Directional );
														break;

													case Keywords.ID_SPOT:
														//TODO
                                                        //_pass.SetIteratePerLight(true, true, LightType.Spotlight );
														break;

													default:
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
															prop.Values[ 0 ].Value + " is not a valid light type (point, directional, or spot)" );
														break;
												}
											}
											else
											{
												//TODO
                                                //_pass.SetIteratePerLight(true, false);
											}

										}
										else if ( atom.IsNumber )
										{
											//TODO
											_pass.IterationCount = Int32.Parse( atom.Value );

											AbstractNode i1 = getNodeAt( prop.Values, 1 );
											if ( i1 != null && i1 is AtomAbstractNode )
											{
												atom = (AtomAbstractNode)i1;
												if ( atom.Id == (uint)Keywords.ID_PER_LIGHT )
												{
													AbstractNode i2 = getNodeAt( prop.Values, 2 );
													if ( i2 != null && i2 is AtomAbstractNode )
													{
														atom = (AtomAbstractNode)i2;
														switch ( (Keywords)atom.Id )
														{
															case Keywords.ID_POINT:
																_pass.IteratePerLight = true;
																break;

															case Keywords.ID_DIRECTIONAL:
																//TODO
																//mPass->setIteratePerLight(true, true, Light::LT_DIRECTIONAL);
																break;

															case Keywords.ID_SPOT:
																//TODO
																//mPass->setIteratePerLight(true, true, Light::LT_SPOTLIGHT);
																break;

															default:
																compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
																	i2.Value + " is not a valid light type (point, directional, or spot)" );
																break;
														}
													}
													else
													{
														//TODO
														//mPass->setIteratePerLight(true, false);
													}
												}
												else if ( atom.Id == (uint)Keywords.ID_PER_N_LIGHTS )
												{
													AbstractNode i2 = getNodeAt( prop.Values, 2 );
													if ( i2 != null && i2 is AtomAbstractNode )
													{
														atom = (AtomAbstractNode)i2;
														if ( atom.IsNumber )
														{
															//TODO
															//mPass->setLightCountPerIteration(
															//    static_cast<unsigned short>(StringConverter::parseInt(atom->value)));

															AbstractNode i3 = getNodeAt( prop.Values, 3 );
															if ( i3 != null && i3 is AtomAbstractNode )
															{
																atom = (AtomAbstractNode)i3;
																switch ( (Keywords)atom.Id )
																{
																	case Keywords.ID_POINT:
																		_pass.IteratePerLight = true;
																		break;

																	case Keywords.ID_DIRECTIONAL:
																		//TODO
																		//mPass->setIteratePerLight(true, true, Light::LT_DIRECTIONAL);
																		break;

																	case Keywords.ID_SPOT:
																		//TODO
																		//mPass->setIteratePerLight(true, true, Light::LT_SPOTLIGHT);
																		break;

																	default:
																		compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
																			i3.Value + " is not a valid light type (point, directional, or spot)" );
																		break;
																}
															}
															else
															{
																//TODO
																//mPass->setIteratePerLight(true, false);
															}
														}
														else
														{
															compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line,
																i2.Value + " is not a valid number" );
														}
													}
													else
													{
														compiler.AddError( CompileErrorCode.NumberExpected, prop.File, prop.Line,
															prop.Values[ 0 ].Value + " is not a valid number" );
													}
												}
											}
										}
										else
										{
											compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line );
										}
									}
									else
									{
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line );
									}
								}
								break;
							#endregion ID_ITERATION

							#region ID_POINT_SIZE
							case Keywords.ID_POINT_SIZE:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"point_size must have at most 1 argument" );
								}
								else
								{
									Real val = 0.0f;
									if ( getReal( prop.Values[ 0 ], out val ) )
										_pass.PointSize = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid number" );
								}
								break;
							#endregion ID_POINT_SIZE

							#region ID_POINT_SPRITES
							case Keywords.ID_POINT_SPRITES:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"point_sprites must have at most 1 argument" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
										_pass.PointSpritesEnabled = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_POINT_SPRITES

							#region ID_POINT_SIZE_ATTENUATION
							case Keywords.ID_POINT_SIZE_ATTENUATION:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 4 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"point_size_attenuation must have at most 4 arguments" );
								}
								else
								{
									bool val = false;
									if ( getBoolean( prop.Values[ 0 ], out val ) )
									{
										if ( val )
										{
											AbstractNode i1 = getNodeAt( prop.Values, 1 ), i2 = getNodeAt( prop.Values, 2 ),
												i3 = getNodeAt( prop.Values, 3 );

											if ( prop.Values.Count > 1 )
											{
												Real constant = 0.0f, linear = 1.0f, quadratic = 0.0f;

												if ( i1 != null && i1 is AtomAbstractNode )
												{
													AtomAbstractNode atom = (AtomAbstractNode)i1;
													if ( atom.IsNumber )
														constant = atom.Number;
													else
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line );
												}
												else
												{
													compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
														i1.Value + " is not a valid number" );
												}

												if ( i2 != null && i2 is AtomAbstractNode )
												{
													AtomAbstractNode atom = (AtomAbstractNode)i2;
													if ( atom.IsNumber )
														linear = atom.Number;
													else
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line );
												}
												else
												{
													compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
														i2.Value + " is not a valid number" );
												}

												if ( i3 != null && i3 is AtomAbstractNode )
												{
													AtomAbstractNode atom = (AtomAbstractNode)i3;
													if ( atom.IsNumber )
														quadratic = atom.Number;
													else
														compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line );
												}
												else
												{
													compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
														i3.Value + " is not a valid number" );
												}

												//TODO
												//mPass->setPointAttenuation(true, constant, linear, quadratic);
											}
											else
											{
												//TODO
												//mPass->setPointAttenuation(true);
											}
										}
										else
										{
											//TODO
											//mPass->setPointAttenuation(false);
										}
									}
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid boolean" );
								}
								break;
							#endregion ID_POINT_SIZE_ATTENUATION

							#region ID_POINT_SIZE_MIN
							case Keywords.ID_POINT_SIZE_MIN:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"point_size_min must have at most 1 argument" );
								}
								else
								{
									Real val = 0.0f;
									if ( getReal( prop.Values[ 0 ], out val ) )
										_pass.PointMinSize = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid number" );
								}
								break;
							#endregion ID_POINT_SIZE_MIN

							#region ID_POINT_SIZE_MAX
							case Keywords.ID_POINT_SIZE_MAX:
								if ( prop.Values.Count == 0 )
								{
									compiler.AddError( CompileErrorCode.StringExpected, prop.File, prop.Line );
								}
								else if ( prop.Values.Count > 1 )
								{
									compiler.AddError( CompileErrorCode.FewerParametersExpected, prop.File, prop.Line,
										"point_size_max must have at most 1 argument" );
								}
								else
								{
									Real val = 0.0f;
									if ( getReal( prop.Values[ 0 ], out val ) )
										_pass.PointMaxSize = val;
									else
										compiler.AddError( CompileErrorCode.InvalidParameters, prop.File, prop.Line,
											prop.Values[ 0 ].Value + " is not a valid number" );
								}
								break;
							#endregion ID_POINT_SIZE_MAX

							default:
								compiler.AddError( CompileErrorCode.UnexpectedToken, prop.File, prop.Line,
									"token \"" + prop.Name + "\" is not recognized" );
								break;

						} // end of switch statement
					} // end of if ( i is PropertyAbstractNode )
					else if ( i is ObjectAbstractNode )
					{
						ObjectAbstractNode child = (ObjectAbstractNode)i;
						switch ( (Keywords)child.Id )
						{
							case Keywords.ID_FRAGMENT_PROGRAM_REF:
								_translateFragmentProgramRef( compiler, child );
								break;

							case Keywords.ID_VERTEX_PROGRAM_REF:
								_translateVertexProgramRef( compiler, child );
								break;

							case Keywords.ID_GEOMETRY_PROGRAM_REF:
								_translateGeometryProgramRef( compiler, child );
								break;

							case Keywords.ID_SHADOW_CASTER_VERTEX_PROGRAM_REF:
								_translateShadowCasterVertexProgramRef( compiler, child );
								break;

							case Keywords.ID_SHADOW_RECEIVER_VERTEX_PROGRAM_REF:
								_translateShadowReceiverVertexProgramRef( compiler, child );
								break;

							case Keywords.ID_SHADOW_RECEIVER_FRAGMENT_PROGRAM_REF:
								_translateShadowReceiverFragmentProgramRef( compiler, child );
								break;

							default:
								_processNode( compiler, i );
								break;
						}
					}
				}
			}
Пример #3
0
		protected void InitTextureLighting()
		{
			if ( targetRenderSystem.Capabilities.TextureUnitCount < 2 )
				LogManager.Instance.Write( "--WARNING--At least 2 available texture units are required for BSP dynamic lighting!" );

			Texture texLight = TextureLight.CreateTexture();

			textureLightMaterial = (Material)MaterialManager.Instance.GetByName( "Axiom/BspTextureLightMaterial" );
			if ( textureLightMaterial == null )
			{
				textureLightMaterial = (Material)MaterialManager.Instance.Create( "Axiom/BspTextureLightMaterial", ResourceGroupManager.DefaultResourceGroupName );
				textureLightPass = textureLightMaterial.GetTechnique( 0 ).GetPass( 0 );
				// the texture light
				TextureUnitState tex = textureLightPass.CreateTextureUnitState( texLight.Name );
				tex.SetColorOperation( LayerBlendOperation.Modulate );
				tex.ColorBlendMode.source2 = LayerBlendSource.Diffuse;
				tex.SetAlphaOperation( LayerBlendOperationEx.Modulate );
				tex.AlphaBlendMode.source2 = LayerBlendSource.Diffuse;
				tex.TextureCoordSet = 2;
                tex.SetTextureAddressingMode( TextureAddressing.Clamp );

				// The geometry texture without lightmap. Use the light texture on this
				// pass, the appropriate texture will be rendered at RenderTextureLighting
				tex = textureLightPass.CreateTextureUnitState( texLight.Name );
				tex.SetColorOperation( LayerBlendOperation.Modulate );
				tex.SetAlphaOperation( LayerBlendOperationEx.Modulate );
                tex.SetTextureAddressingMode( TextureAddressing.Wrap );

				textureLightPass.SetSceneBlending( SceneBlendType.TransparentAlpha );

				textureLightMaterial.CullingMode = CullingMode.None;
				textureLightMaterial.Lighting = false;
			}
			else
			{
				textureLightPass = textureLightMaterial.GetTechnique( 0 ).GetPass( 0 );
			}
		}
        private void InitShadowReceiverPass()
        {
            Material matShadRec = MaterialManager.Instance.GetByName(TEXTURE_SHADOW_RECEIVER_MATERIAL);

            if (matShadRec == null) {
                matShadRec = (Material) MaterialManager.Instance.Create(TEXTURE_SHADOW_RECEIVER_MATERIAL);
                shadowReceiverPass = matShadRec.GetTechnique(0).GetPass(0);
                shadowReceiverPass.SetSceneBlending(SceneBlendFactor.DestColor, SceneBlendFactor.Zero);
                // No lighting, one texture unit
                // everything else will be bound as needed during the receiver pass
                shadowReceiverPass.LightingEnabled = false;
                TextureUnitState t = shadowReceiverPass.CreateTextureUnitState();
                t.TextureAddressing = TextureAddressing.Clamp;
                shadowReceiverPass.DepthBias = 1;
            }
            else {
                shadowReceiverPass = matShadRec.GetTechnique(0).GetPass(0);
            }
        }
        private void InitShadowModulativePass()
        {
            Material matModStencil =
                MaterialManager.Instance.GetByName(STENCIL_SHADOW_MODULATIVE_MATERIAL);

            if(matModStencil == null) {
                // Create
                matModStencil =
                    (Material)MaterialManager.Instance.Create(STENCIL_SHADOW_MODULATIVE_MATERIAL);

                shadowModulativePass = matModStencil.GetTechnique(0).GetPass(0);
                shadowModulativePass.SetSceneBlending(SceneBlendFactor.DestColor, SceneBlendFactor.Zero);
                shadowModulativePass.LightingEnabled = false;
                shadowModulativePass.DepthWrite = false;
                shadowModulativePass.DepthCheck = false;
                TextureUnitState t = shadowModulativePass.CreateTextureUnitState();
                t.SetColorOperationEx(
                    LayerBlendOperationEx.Modulate,
                    LayerBlendSource.Manual,
                    LayerBlendSource.Current,
                    shadowColor);
            }
            else {
                shadowModulativePass = matModStencil.GetTechnique(0).GetPass(0);
            }
        }
        private void InitShadowDebugPass()
        {
            Material matDebug = MaterialManager.Instance.GetByName(SHADOW_VOLUMES_MATERIAL);

            if(matDebug == null) {
                // Create
                matDebug = (Material)MaterialManager.Instance.Create(SHADOW_VOLUMES_MATERIAL);
                shadowDebugPass = matDebug.GetTechnique(0).GetPass(0);
                shadowDebugPass.SetSceneBlending(SceneBlendType.Add);
                shadowDebugPass.LightingEnabled = false;
                shadowDebugPass.DepthWrite = false;
                TextureUnitState t = shadowDebugPass.CreateTextureUnitState();
                t.SetColorOperationEx(
                    LayerBlendOperationEx.Modulate,
                    LayerBlendSource.Manual,
                    LayerBlendSource.Current,
                    new ColorEx(0.7f, 0.0f, 0.2f));

                shadowDebugPass.CullMode = CullingMode.None;

                if(targetRenderSystem.Caps.CheckCap(Capabilities.VertexPrograms)) {
                    ShadowVolumeExtrudeProgram.Initialize();

                    // Enable the (infinite) point light extruder for now, just to get some params
                    shadowDebugPass.SetVertexProgram(
                        ShadowVolumeExtrudeProgram.GetProgramName(ShadowVolumeExtrudeProgram.Programs.PointLight));

                    infiniteExtrusionParams = shadowDebugPass.VertexProgramParameters;
                    infiniteExtrusionParams.SetAutoConstant(0, AutoConstants.WorldViewProjMatrix);
                    infiniteExtrusionParams.SetAutoConstant(4, AutoConstants.LightPositionObjectSpace);
                }

                matDebug.Compile();
            }
            else {
                shadowDebugPass = matDebug.GetTechnique(0).GetPass(0);
            }
        }
Пример #7
0
        /// <summary>
        ///		Internal method for splitting the passes into illumination passes.
        /// </summary>
        public void CompileIlluminationPasses()
        {
            ClearIlluminationPasses();

            // don't need to split transparent passes since they are rendered seperately
            if(this.IsTransparent) {
                return;
            }

            // start off with ambient passes
            IlluminationStage stage = IlluminationStage.Ambient;

            bool hasAmbient = false;

            for(int i = 0; i < passes.Count; /* increment in logic */) {
                Pass pass = (Pass)passes[i];
                IlluminationPass iPass;

                switch(stage) {
                    case IlluminationStage.Ambient:
                        // keep looking for ambient only
                        if(pass.IsAmbientOnly) {
                            iPass = new IlluminationPass();
                            iPass.OriginalPass = pass;
                            iPass.Pass = pass;
                            iPass.Stage = stage;
                            illuminationPasses.Add(iPass);
                            hasAmbient = true;

                            // progress to the next pass
                            i++;
                        }
                        else {
                            // split off any ambient part
                            if(pass.Ambient.CompareTo(ColorEx.Black) != 0 ||
                               pass.Emissive.CompareTo(ColorEx.Black) != 0 ||
                               pass.AlphaRejectFunction != CompareFunction.AlwaysPass) {

                                Pass newPass = new Pass(this, pass.Index);
                                pass.CopyTo(newPass);
                                if (newPass.AlphaRejectFunction != CompareFunction.AlwaysPass) {
                                    // Alpha rejection passes must retain their transparency, so
                                    // we allow the texture units, but override the colour functions
                                    for (int tindex = 0; tindex < newPass.NumTextureUnitStages; tindex++)
                                    {
                                        TextureUnitState tus = newPass.GetTextureUnitState(tindex);
                                        tus.SetColorOperationEx(LayerBlendOperationEx.Source1, LayerBlendSource.Current, LayerBlendSource.Current);
                                    }
                                }
                                else
                                    // remove any texture units
                                    newPass.RemoveAllTextureUnitStates();

                                // also remove any fragment program
                                if(newPass.HasFragmentProgram) {
                                    newPass.SetFragmentProgram("");
                                }

                                // We have to leave vertex program alone (if any) and
                                // just trust that the author is using light bindings, which
                                // we will ensure there are none in the ambient pass
                                newPass.Diffuse = new ColorEx(newPass.Diffuse.a, 0f, 0f, 0f); // Preserving alpha
                                newPass.Specular = ColorEx.Black;

                                // Calculate hash value for new pass, because we are compiling
                                // illumination passes on demand, which will loss hash calculate
                                // before it add to render queue first time.
                                newPass.RecalculateHash();

                                iPass = new IlluminationPass();
                                iPass.DestroyOnShutdown = true;
                                iPass.OriginalPass = pass;
                                iPass.Pass = newPass;
                                iPass.Stage = stage;

                                illuminationPasses.Add(iPass);
                                hasAmbient = true;
                            }

                            if(!hasAmbient) {
                                // make up a new basic pass
                                Pass newPass = new Pass(this, pass.Index);
                                pass.CopyTo(newPass);

                                newPass.Ambient = ColorEx.Black;
                                newPass.Diffuse = ColorEx.Black;

                                // Calculate hash value for new pass, because we are compiling
                                // illumination passes on demand, which will loss hash calculate
                                // before it add to render queue first time.
                                newPass.RecalculateHash();

                                iPass = new IlluminationPass();
                                iPass.DestroyOnShutdown = true;
                                iPass.OriginalPass = pass;
                                iPass.Pass = newPass;
                                iPass.Stage = stage;
                                illuminationPasses.Add(iPass);
                                hasAmbient = true;
                            }

                            // this means we are done with ambients, progress to per-light
                            stage = IlluminationStage.PerLight;
                        }

                        break;

                    case IlluminationStage.PerLight:
                        if(pass.RunOncePerLight) {
                            // if this is per-light already, use it directly
                            iPass = new IlluminationPass();
                            iPass.DestroyOnShutdown = false;
                            iPass.OriginalPass = pass;
                            iPass.Pass = pass;
                            iPass.Stage = stage;
                            illuminationPasses.Add(iPass);

                            // progress to the next pass
                            i++;
                        }
                        else {
                            // split off per-light details (can only be done for one)
                            if(pass.LightingEnabled &&
                                (pass.Diffuse.CompareTo(ColorEx.Black) != 0 ||
                                pass.Specular.CompareTo(ColorEx.Black) != 0)) {

                                // copy existing pass
                                Pass newPass = new Pass(this, pass.Index);
                                pass.CopyTo(newPass);

                                if (newPass.AlphaRejectFunction != CompareFunction.AlwaysPass) {
                                    // Alpha rejection passes must retain their transparency, so
                                    // we allow the texture units, but override the colour functions
                                    for (int tindex = 0; tindex < newPass.NumTextureUnitStages; tindex++)
                                    {
                                        TextureUnitState tus = newPass.GetTextureUnitState(tindex);
                                        tus.SetColorOperationEx(LayerBlendOperationEx.Source1, LayerBlendSource.Current, LayerBlendSource.Current);
                                    }
                                }
                                else
                                    // remove any texture units
                                    newPass.RemoveAllTextureUnitStates();

                                // also remove any fragment program
                                if(newPass.HasFragmentProgram) {
                                    newPass.SetFragmentProgram("");
                                }

                                // Cannot remove vertex program, have to assume that
                                // it will process diffuse lights, ambient will be turned off
                                newPass.Ambient = ColorEx.Black;
                                newPass.Emissive = ColorEx.Black;

                                // must be additive
                                newPass.SetSceneBlending(SceneBlendFactor.One, SceneBlendFactor.One);

                                iPass = new IlluminationPass();
                                iPass.DestroyOnShutdown = true;
                                iPass.OriginalPass = pass;
                                iPass.Pass = newPass;
                                iPass.Stage = stage;

                                illuminationPasses.Add(iPass);
                            }

                            // This means the end of per-light passes
                            stage = IlluminationStage.Decal;
                        }

                        break;

                    case IlluminationStage.Decal:
                        // We just want a 'lighting off' pass to finish off
                        // and only if there are texture units
                        if(pass.NumTextureUnitStages > 0) {
                            if(!pass.LightingEnabled) {
                                // we assume this pass already combines as required with the scene
                                iPass = new IlluminationPass();
                                iPass.DestroyOnShutdown = false;
                                iPass.OriginalPass = pass;
                                iPass.Pass = pass;
                                iPass.Stage = stage;
                                illuminationPasses.Add(iPass);
                            }
                            else {
                                // Copy the pass and tweak away the lighting parts
                                Pass newPass = new Pass(this, pass.Index);
                                pass.CopyTo(newPass);
                                newPass.Ambient = ColorEx.Black;
                                newPass.Diffuse = new ColorEx(newPass.Diffuse.a, 0f, 0f, 0f); // Preserving alpha
                                newPass.Specular = ColorEx.Black;
                                newPass.Emissive = ColorEx.Black;
                                newPass.LightingEnabled = false;
                                // modulate
                                newPass.SetSceneBlending(SceneBlendFactor.DestColor, SceneBlendFactor.Zero);

                                // Calculate hash value for new pass, because we are compiling
                                // illumination passes on demand, which will loss hash calculate
                                // before it add to render queue first time.
                                newPass.RecalculateHash();

                                // there is nothing we can do about vertex & fragment
                                // programs here, so people will just have to make their
                                // programs friendly-like if they want to use this technique
                                iPass = new IlluminationPass();
                                iPass.DestroyOnShutdown = true;
                                iPass.OriginalPass = pass;
                                iPass.Pass = newPass;
                                iPass.Stage = stage;
                                illuminationPasses.Add(iPass);
                            }
                        }

                        // always increment on decal, since nothing more to do with this pass
                        i++;

                        break;
                }
            }
        }