private void RenderMaterialPasses(DrawSurface surface)
		{
			Surface tri = surface.Geometry;
			idMaterial material = surface.Material;
			int count;

			if(material.HasAmbient == false)
			{
				// disabled because we don't do lighting right now
				//TODO: return;
			}

			if(material.IsPortalSky == true)
			{
				return;
			}

			// change the matrix if needed
			if(surface.Space != _currentSpace)
			{
				_effect.View = surface.Space.ModelViewMatrix;
				_currentSpace = surface.Space;
				//idConsole.Warning("TODO: RB_SetProgramEnvironmentSpace();");
			}

			// change the scissor if needed
			if((idE.CvarSystem.GetBool("r_useScissor") == true) && (_currentScissor != surface.ScissorRectangle))
			{
				_currentScissor = surface.ScissorRectangle;

				_graphicsDevice.ScissorRectangle = new Rectangle(
					_viewDef.ViewPort.X1 + _currentScissor.X1,
					_viewDef.ViewPort.Y1 + _currentScissor.Y1,
					_currentScissor.X2 + 1 - _currentScissor.X1,
					_currentScissor.Y2 + 1 - _currentScissor.Y1);
			}

			// some deforms may disable themselves by setting numIndexes = 0
			if(tri.Indexes.Length == 0)
			{
				return;
			}

			if(tri.AmbientCache == null)
			{
				idConsole.WriteLine("RenderShaderPasses: !tri.AmbientCache");
				return;
			}

			// get the expressions for conditionals / color / texcoords
			float[] registers = surface.MaterialRegisters;

			// set face culling appropriately
			SetCull(material.CullType);

			// set polygon offset if necessary
			if(material.TestMaterialFlag(MaterialFlags.PolygonOffset) == true)
			{
				idConsole.Warning("TODO: polygon offset fill");
				//Gl.glEnable(Gl.GL_POLYGON_OFFSET_FILL);
				//Gl.glPolygonOffset(idE.CvarSystem.GetFloat("r_offsetFactor"), idE.CvarSystem.GetFloat("r_offsetUnits") * material.PolygonOffset);
			}

			if(surface.Space.WeaponDepthHack == true)
			{
				idConsole.Warning("TODO: RB_EnterWeaponDepthHack();");
			}

			if(surface.Space.ModelDepthHack != 0.0f)
			{
				idConsole.Warning("TODO: RB_EnterModelDepthHack( surf->space->modelDepthHack );");
			}

			foreach(MaterialStage stage in material.Stages)
			{
				// check the enable condition
				if(registers[stage.ConditionRegister] == 0)
				{
					continue;
				}

				// skip the stages involved in lighting
				if(stage.Lighting != StageLighting.Ambient)
				{
					// disabled because we don't do lighting right now
					// TODO: continue;
				}

				// skip if the stage is ( GL_ZERO, GL_ONE ), which is used for some alpha masks
				if((stage.DrawStateBits & (MaterialStates.SourceBlendBits | MaterialStates.DestinationBlendBits))
					== (MaterialStates.SourceBlendZero | MaterialStates.DestinationBlendOne))
				{
					continue;
				}

				// see if we are a new-style stage
				NewMaterialStage newStage = stage.NewStage;

				if(newStage.IsEmpty == false)
				{
					throw new Exception("THIS MIGHT NOT WORK!!!");
					//--------------------------
					//
					// new style stages
					//
					//--------------------------

					if(idE.CvarSystem.GetBool("r_skipNewAmbient") == true)
					{
						continue;
					}

					idConsole.Warning("TODO: render");
					/*Gl.glColorPointer(4, Gl.GL_UNSIGNED_BYTE, Marshal.SizeOf(typeof(Vertex)), (void*) &ambientCacheData->color);
					Gl.glVertexAttribPointerARB(9, 3, Gl.GL_FLOAT, false, Marshal.SizeOf(typeof(Vertex)), ambientCacheData->tangents[0].ToFloatPtr());
					Gl.glVertexAttribPointerARB(10, 3, Gl.GL_FLOAT, false, Marshal.SizeOf(typeof(Vertex)), ambientCacheData->tangents[1].ToFloatPtr());
					Gl.glNormalPointer(Gl.GL_FLOAT, Marshal.SizeOf(typeof(Vertex)), ambientCacheData->normal.ToFloatPtr());*/

					//Gl.glEnableClientState(Gl.GL_COLOR_ARRAY);
					//Gl.glEnableVertexAttribArray(9);
					//Gl.glEnableVertexAttribArray(10);
					//Gl.glEnableClientState(Gl.GL_NORMAL_ARRAY);

					SetState(stage.DrawStateBits);

					idConsole.Warning("TODO: glBindProgramARB");
					/*Gl.glBindProgramARB(Gl.GL_VERTEX_PROGRAM_ARB, newStage.VertexProgram);
					Gl.glEnable(Gl.GL_VERTEX_PROGRAM_ARB);*/

					// megaTextures bind a lot of images and set a lot of parameters
					// TODO: megatextures
					/*if ( newStage->megaTexture ) {
						newStage->megaTexture->SetMappingForSurface( tri );
						idVec3	localViewer;
						R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.viewDef->renderView.vieworg, localViewer );
						newStage->megaTexture->BindForViewOrigin( localViewer );
					}*/

					count = newStage.VertexParameters.Length;

					for(int i = 0; i < count; i++)
					{
						float[] parm = new float[4];
						parm[0] = registers[newStage.VertexParameters[i, 0]];
						parm[1] = registers[newStage.VertexParameters[i, 1]];
						parm[2] = registers[newStage.VertexParameters[i, 2]];
						parm[3] = registers[newStage.VertexParameters[i, 3]];

						//Gl.glProgramLocalParameter4fvARB(Gl.GL_VERTEX_PROGRAM_ARB, i, parm);
					}

					count = newStage.FragmentProgramImages.Length;

					for(int i = 0; i < count; i++)
					{
						if(newStage.FragmentProgramImages[i] != null)
						{
							SetTextureUnit(i);
							newStage.FragmentProgramImages[i].Bind();
						}
					}

					//Gl.glBindProgramARB(Gl.GL_FRAGMENT_PROGRAM_ARB, newStage.FragmentProgram);
					//Gl.glEnable(Gl.GL_FRAGMENT_PROGRAM_ARB);

					// draw it
					DrawElementsWithCounters(tri);

					count = newStage.FragmentProgramImages.Length;

					for(int i = 1; i < count; i++)
					{
						if(newStage.FragmentProgramImages[i] != null)
						{
							SetTextureUnit(i);
							idE.ImageManager.BindNullTexture();
						}
					}

					// TODO: megatexture
					/*if ( newStage->megaTexture ) {
						newStage->megaTexture->Unbind();
					}*/

					SetTextureUnit(0);

					//Gl.glDisable(Gl.GL_VERTEX_PROGRAM_ARB);
					//Gl.glDisable(Gl.GL_FRAGMENT_PROGRAM_ARB);
					// Fixme: Hack to get around an apparent bug in ATI drivers.  Should remove as soon as it gets fixed.
					//Gl.glBindProgramARB(Gl.GL_VERTEX_PROGRAM_ARB, 0);

					//Gl.glDisableClientState(Gl.GL_COLOR_ARRAY);
					//Gl.glDisableVertexAttribArrayARB(9);
					//Gl.glDisableVertexAttribArrayARB(10);
					//Gl.glDisableClientState(Gl.GL_NORMAL_ARRAY);

					continue;
				}
				else
				{
					//--------------------------
					//
					// old style stages
					//
					//--------------------------

					// set the color
					float[] color = new float[4];
					color[0] = registers[stage.Color.Registers[0]];
					color[1] = registers[stage.Color.Registers[1]];
					color[2] = registers[stage.Color.Registers[2]];
					color[3] = registers[stage.Color.Registers[3]];

					// skip the entire stage if an add would be black
					if(((stage.DrawStateBits & (MaterialStates.SourceBlendBits & MaterialStates.DestinationBlendBits)) == (MaterialStates.SourceBlendOne | MaterialStates.DestinationBlendOne))
						&& (color[0] <= 0) && (color[1] <= 0) && (color[2] <= 0))
					{
						continue;
					}

					// skip the entire stage if a blend would be completely transparent
					if(((stage.DrawStateBits & (MaterialStates.SourceBlendBits & MaterialStates.DestinationBlendBits)) == (MaterialStates.SourceBlendSourceAlpha | MaterialStates.DestinationBlendOneMinusSourceAlpha))
						&& (color[3] <= 0))
					{
						continue;
					}

					// select the vertex color source
					if(stage.VertexColor == StageVertexColor.Ignore)
					{
						_effect.DiffuseColor = new Vector3(color[0], color[1], color[2]);
						_effect.Alpha = color[3];
					}
					else
					{
						if(stage.VertexColor == StageVertexColor.InverseModulate)
						{
							idConsole.Warning("TODO: InverseModulate");
							//GL_TextureEnvironment(Gl.GL_COMBINE_ARB);

							/*GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.CombineRgb, (int) All.Modulate);
							GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.Source0Rgb, (int) All.Texture);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_SOURCE1_RGB_ARB, Gl.GL_PRIMARY_COLOR_ARB);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND0_RGB_ARB, Gl.GL_SRC_COLOR);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND1_RGB_ARB, Gl.GL_ONE_MINUS_SRC_COLOR);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_RGB_SCALE_ARB, 1);*/
						}

						// for vertex color and modulated color, we need to enable a second texture stage
						if(color[0] != 1 || color[1] != 1 || color[2] != 1 || color[3] != 1)
						{
							SetTextureUnit(1);
							idE.ImageManager.WhiteImage.Bind();
							idConsole.Warning("TODO: vertex color");
							// GL_TextureEnvironment(Gl.GL_COMBINE_ARB);

							/*Gl.glTexEnvfv(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_COLOR, color);

							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_COMBINE_RGB_ARB, Gl.GL_MODULATE);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_SOURCE0_RGB_ARB, Gl.GL_PREVIOUS_ARB);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_SOURCE1_RGB_ARB, Gl.GL_CONSTANT_ARB);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND0_RGB_ARB, Gl.GL_SRC_COLOR);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND1_RGB_ARB, Gl.GL_SRC_COLOR);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_RGB_SCALE_ARB, 1);

							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_COMBINE_ALPHA_ARB, Gl.GL_MODULATE);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_SOURCE0_ALPHA_ARB, Gl.GL_PREVIOUS_ARB);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_SOURCE1_ALPHA_ARB, Gl.GL_CONSTANT_ARB);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND0_ALPHA_ARB, Gl.GL_SRC_ALPHA);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_OPERAND1_ALPHA_ARB, Gl.GL_SRC_ALPHA);
							Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_ALPHA_SCALE, 1);

							GL_SelectTexture(0);*/
						}
					}

					// bind the texture
					BindVariableStageImage(stage.Texture, registers);

					// set the state
					SetState(stage.DrawStateBits);

					PrepareStageTexturing(stage, surface, tri.AmbientCache.Data);

					// draw it
					DrawElementsWithCounters(tri);

					FinishStageTexturing(stage, surface, tri.AmbientCache.Data);

					if(stage.VertexColor != StageVertexColor.Ignore)
					{
						idConsole.Warning("TODO: SVC ignore");
						/*GL.DisableClientState(ArrayCap.ColorArray);*/

						SetTextureUnit(1);
						SetTextureEnvironment(Gl.GL_MODULATE);

						idE.ImageManager.BindNullTexture();

						SetTextureUnit(0);
						SetTextureEnvironment(Gl.GL_MODULATE);
					}
				}

				// reset polygon offset
				if(material.TestMaterialFlag(MaterialFlags.PolygonOffset) == true)
				{
					// TODO: Gl.glDisable(Gl.GL_POLYGON_OFFSET_FILL);
				}

				if((surface.Space.WeaponDepthHack == true) || (surface.Space.ModelDepthHack != 0.0f))
				{
					idConsole.Warning("TODO: RB_LeaveDepthHack();");
				}
			}
		}
		private float _maxLights = 999;								// 1.0 for standard, unlimited for floats
		#endregion

		#region Methods
		#region Frame control
		/// <summary>
		/// Any mirrored or portaled views have already been drawn, so prepare
		/// to actually render the visible surfaces for this view.
		/// </summary>
		private void BeginDrawingView()
		{
			Vector2 viewPortOffset = idE.RenderSystem.ViewPortOffset;

			// set the window clipping
			_graphicsDevice.Viewport = new Viewport(
				(int) viewPortOffset.X + _viewDef.ViewPort.X1,
				(int) viewPortOffset.Y + _viewDef.ViewPort.Y1,
				_viewDef.ViewPort.X2 + 1 - _viewDef.ViewPort.X1,
				_viewDef.ViewPort.Y2 + 1 - _viewDef.ViewPort.Y1);

			// the scissor may be smaller than the viewport for subviews
			_graphicsDevice.ScissorRectangle = new Rectangle(
				(int) viewPortOffset.X + _viewDef.ViewPort.X1 + _viewDef.Scissor.X1,
				(int) viewPortOffset.Y + _viewDef.ViewPort.Y1 + _viewDef.Scissor.Y1,
				_viewDef.Scissor.X2 + 1 - _viewDef.Scissor.X1,
				_viewDef.Scissor.Y2 + 1 - _viewDef.Scissor.Y1);

			_currentScissor = _viewDef.Scissor;

			// ensures that depth writes are enabled for the depth clear
			SetState(MaterialStates.DepthFunctionAlways);

			// we don't have to clear the depth / stencil buffer for 2D rendering
			if(_viewDef.ViewEntities.Count > 0)
			{
				idConsole.Warning("TODO: stencil mask");

				/*Gl.glStencilMask(0xFF);
				// some cards may have 7 bit stencil buffers, so don't assume this
				// should be 128
				Gl.glClearStencil(1 << (glConfig.stencilBits - 1));
				Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT | Gl.GL_STENCIL_BUFFER_BIT);
				Gl.glEnable(Gl.GL_DEPTH_TEST);*/
			}
			else
			{
				DepthStencilState s = new DepthStencilState();
				s.DepthBufferEnable = false;
				s.StencilEnable = false;

				_graphicsDevice.DepthStencilState = s;
			}
			
			_state.FaceCulling = CullType.None; // force face culling to set next time

			SetCull(CullType.Front);
		}
		/// <summary>
		/// The triangle functions can check backEnd.currentSpace != surf->space
		/// to see if they need to perform any new matrix setup.  The modelview
		/// matrix will already have been loaded, and backEnd.currentSpace will
		/// be updated after the triangle function completes.
		/// </summary>
		/// <param name="surfaces"></param>
		/// <param name="handler"></param>
		private void RenderDrawSurfaceListWithFunction(DrawSurface[] surfaces, RenderHandler handler)
		{
			int count = surfaces.Length;

			for(int i = 0; i < count; i++)
			{
				DrawSurface surface = surfaces[i];

				// change the matrix if needed
				if(surface.Space != _currentSpace)
				{
					_effect.View = surface.Space.ModelViewMatrix;
				}

				if(surface.Space.WeaponDepthHack == true)
				{
					idConsole.Warning("TODO: RB_EnterWeaponDepthHack();");
				}

				if(surface.Space.ModelDepthHack != 0.0f)
				{
					idConsole.Warning("TODO: RB_EnterModelDepthHack( drawSurf->space->modelDepthHack );");
				}

				// change the scissor if needed
				if((idE.CvarSystem.GetBool("r_useScissor") == true) && (_currentScissor != surface.ScissorRectangle))
				{
					_currentScissor = surface.ScissorRectangle;

					_graphicsDevice.ScissorRectangle = new Rectangle(
						_viewDef.ViewPort.X1 + _currentScissor.X1,
						_viewDef.ViewPort.Y1 + _currentScissor.Y1,
						_currentScissor.X2 + 1 - _currentScissor.X1,
						_currentScissor.Y2 + 1 - _currentScissor.Y1);
				}

				// render it
				handler(surface);

				if((surface.Space.WeaponDepthHack == true) || (surface.Space.ModelDepthHack != 0.0f))
				{
					idConsole.Warning("TODO: RB_LeaveDepthHack();");
				}

				_currentSpace = surface.Space;
			}
		}