/// <summary>
        ///		Internal utility method for rendering a single object.
        /// </summary>
        /// <param name="renderable">The renderable to issue to the pipeline.</param>
        /// <param name="pass">The pass which is being used.</param>
        /// <param name="doLightIteration">If true, this method will issue the renderable to
        /// the pipeline possibly multiple times, if the pass indicates it should be
        /// done once per light.</param>
        /// <param name="manualLightList">Only applicable if 'doLightIteration' is false, this
        /// method allows you to pass in a previously determined set of lights
        /// which will be used for a single render of this object.</param>
        protected virtual void RenderSingleObject(IRenderable renderable, Pass pass, 
			bool doLightIteration, List<Light> manualLightList)
        {
            targetRenderSystem.BeginProfileEvent(ColorEx.Red, "RenderSingleObject: Material = " + renderable.Material.Name);

            ushort numMatrices = 0;

            // grab the current scene detail level
            SceneDetailLevel camDetailLevel = cameraInProgress.SceneDetail;

            // 			// update auto params if this is a programmable pass
            // 			if(pass.IsProgrammable) {
            // 				autoParamDataSource.Renderable = renderable;
            // 				pass.UpdateAutoParamsNoLights(autoParamDataSource);
            // 			}

            // get the world matrices and the count
            renderable.GetWorldTransforms(xform);
            numMatrices = renderable.NumWorldTransforms;

            // set the world matrices in the render system
            if(numMatrices > 1) {
                targetRenderSystem.SetWorldMatrices(xform, numMatrices);
            }
            else {
                targetRenderSystem.WorldMatrix = xform[0];
            }

            // issue view/projection changes (if any)
            UseRenderableViewProjection(renderable);

            if (!suppressRenderStateChanges) {
                bool passSurfaceAndLightParams = true;
                if (pass.IsProgrammable) {
                    // Tell auto params object about the renderable change
                    autoParamDataSource.Renderable = renderable;
                    // Tell auto params object about the world matrices, eliminated query from renderable again
                    autoParamDataSource.SetWorldMatrices(xform, numMatrices);
                    pass.UpdateAutoParamsNoLights(autoParamDataSource);
                    if (pass.HasVertexProgram)
                        passSurfaceAndLightParams = pass.VertexProgram.PassSurfaceAndLightStates;
                }

                // issue texture units that depend on updated view matrix
                // reflective env mapping is one case
                for(int i = 0; i < pass.NumTextureUnitStages; i++) {
                    TextureUnitState texUnit = pass.GetTextureUnitState(i);

                    if(texUnit.HasViewRelativeTexCoordGen) {
                        targetRenderSystem.SetTextureUnit(i, texUnit, !pass.HasFragmentProgram);
                    }
                }

                // Normalize normals
                bool thisNormalize = renderable.NormalizeNormals;

                if(thisNormalize != normalizeNormals) {
                    targetRenderSystem.NormalizeNormals = thisNormalize;
                    normalizeNormals = thisNormalize;
                }

                // Set up the solid / wireframe override
                SceneDetailLevel requestedDetail = renderable.RenderDetail;
                if (requestedDetail != lastDetailLevel || requestedDetail != camDetailLevel) {
                    if (requestedDetail > camDetailLevel) {
                        // only downgrade detail; if cam says wireframe we don't go up to solid
                        requestedDetail = camDetailLevel;
                    }
                    targetRenderSystem.RasterizationMode = requestedDetail;
                    lastDetailLevel = requestedDetail;

                }

                // TODO: Add ClipPlanes to RenderSystem.cs
                //targetRenderSystem.ClipPlanes = renderable.ClipPlanes;

                // get the renderables render operation
                renderable.GetRenderOperation(op);
                // TODO: Add srcRenderable to RenderOperation.cs
                //op.srcRenderable = renderable;

                if(doLightIteration) {
                    // Here's where we issue the rendering operation to the render system
                    // Note that we may do this once per light, therefore it's in a loop
                    // and the light parameters are updated once per traversal through the
                    // loop
                    doLightMeter.Enter();
                    if (MeterManager.Collecting) {
                        string s = string.Format("Rendering material '{0}'", renderable.Material.Name);
                        if (renderable is SubEntity) {
                            SubEntity subEntity = (SubEntity)renderable;
                            s += string.Format(", SubMesh '{0}', Entity '{1}'", subEntity.SubMesh.Name, subEntity.Parent.Name);
                        }
                        MeterManager.AddInfoEvent(s);
                    }

                    List<Light> rendLightList = renderable.Lights;
                    bool iteratePerLight = pass.RunOncePerLight;
                    int startLight = pass.StartLight;
                    int lightsLeft = iteratePerLight ? rendLightList.Count - startLight : 1;
                    List<Light> lightListToUse = null;
                    int lightIndex = startLight;

                    while (lightsLeft > 0) {
                        // determine light list to use
                        if(iteratePerLight) {
                            localLightList.Clear();
                            int lightsPerIteration = pass.LightsPerIteration;
                            int numShadowTextureLights = 0;
                            int lightsConsidered = 0;
                            for (int i=0; i<lightsPerIteration && lightIndex < rendLightList.Count; i++,lightIndex++,lightsLeft--) {
                                // check whether we need to filter this one out
                                lightsConsidered++;
                                if(pass.RunOnlyForOneLightType && pass.OnlyLightType != rendLightList[lightIndex].Type)
                                    // skip this one
                                    continue;
                                localLightList.Add(rendLightList[lightIndex]);
                                // potentially need to update content_type shadow texunit
                                // corresponding to this light
                                if (IsShadowTechniqueTextureBased && lightIndex < shadowTextures.Count) {
                                    // link the numShadowTextureLights'th shadow texture unit
                                    int tuindex = pass.GetTextureUnitWithContentTypeIndex(
                                        TextureContentType.Shadow, numShadowTextureLights);
                                    if (tuindex < pass.NumTextureUnitStages) {
                                        TextureUnitState tu = pass.GetTextureUnitState(tuindex);
                                        tu.SetTexturePtr(shadowTextures[lightIndex]);
                                        Camera cam = shadowTextures[lightIndex].GetBuffer().GetRenderTarget().GetViewport(0).Camera;
                                        if (!pass.HasVertexProgram)
                                            tu.SetProjectiveTexturing(true, cam);
                                        autoParamDataSource.SetTextureProjector(cam, numShadowTextureLights);
                                        ++numShadowTextureLights;
                                        // Have to set TU on rendersystem right now, although
                                        // autoparams will be set later
                                        targetRenderSystem.SetTextureUnit(tuindex, tu, !pass.HasFragmentProgram);
                                    }
                                }
                            }
                            // Did we run out of lights before slots? e.g. 5 lights, 2 per iteration
                            if (lightsPerIteration != lightsConsidered)
                                lightsLeft = 0;
                            lightListToUse = localLightList;
                        }
                        else {
                            if (startLight != 0) {
                                if (startLight >= rendLightList.Count) {
                                    lightsLeft = 0;
                                    break;
                                }
                                else {
                                    localLightList.Clear();
                                    for (int i=startLight; i<rendLightList.Count; i++)
                                        localLightList.Add(rendLightList[i]);
                                    lightListToUse = localLightList;
                                }
                            }
                            else
                                // use complete light list
                                lightListToUse = rendLightList;
                            lightsLeft = 0;
                        }

                        if(pass.IsProgrammable) {
                            // Update any automatic gpu params for lights
                            // Other bits of information will have to be looked up
                            updateAutoParmsMeter.Enter();
                            autoParamDataSource.SetCurrentLightList(lightListToUse);
                            pass.UpdateAutoParamsLightsOnly(autoParamDataSource);
                            updateAutoParmsMeter.Exit();

                            // note: parameters must be bound after auto params are updated
                            setGpuParmsMeter.Enter();
                            if(pass.HasVertexProgram) {
                                if (MeterManager.Collecting)
                                    MeterManager.AddInfoEvent("Vertex Program " + pass.VertexProgramName);
                                targetRenderSystem.BindGpuProgramParameters(GpuProgramType.Vertex, pass.VertexProgramParameters);
                            }
                            if(pass.HasFragmentProgram) {
                                if (MeterManager.Collecting)
                                    MeterManager.AddInfoEvent("Fragment Program " + pass.FragmentProgramName);
                                targetRenderSystem.BindGpuProgramParameters(GpuProgramType.Fragment, pass.FragmentProgramParameters);
                            }
                            setGpuParmsMeter.Exit();
                        }

                        // Do we need to update light states?
                        // Only do this if fixed-function vertex lighting applies
                        if(pass.LightingEnabled && passSurfaceAndLightParams) {
                            //useLightsMeter.Enter();
                            targetRenderSystem.UseLights(lightListToUse, pass.MaxLights);
                            //useLightsMeter.Exit();
                        }
                        // issue the render op
                        renderOpMeter.Enter();
                        targetRenderSystem.Render(op);
                        renderOpMeter.Exit();
                    } // iterate per light
                    doLightMeter.Exit();
                }
                else {
                    // do we need to update GPU program parameters?
                    if(pass.IsProgrammable) {
                        // do we have a manual light list
                        if(manualLightList != null) {
                            // Update any automatic gpu params for lights
                            // Other bits of information will have to be looked up
                            autoParamDataSource.SetCurrentLightList(manualLightList);
                            pass.UpdateAutoParamsLightsOnly(autoParamDataSource);
                        }

                        // note: parameters must be bound after auto params are updated
                        if(pass.HasVertexProgram) {
                            targetRenderSystem.BindGpuProgramParameters(GpuProgramType.Vertex, pass.VertexProgramParameters);
                        }

                        if(pass.HasFragmentProgram) {
                             targetRenderSystem.BindGpuProgramParameters(GpuProgramType.Fragment, pass.FragmentProgramParameters);
                        }
                    }

                    // Use manual lights if present, and not using vertex programs
                    if(manualLightList != null && pass.LightingEnabled && passSurfaceAndLightParams) {
                        targetRenderSystem.UseLights(manualLightList, pass.MaxLights);
                    }

                    // issue the render op
                    renderOpMeter.Enter();
                    targetRenderSystem.Render(op);
                    renderOpMeter.Exit();
                }
            }
            else { // suppressRenderStateChanges
                // Just render
                renderOpMeter.Enter();
                targetRenderSystem.Render(op);
                renderOpMeter.Exit();
            }

            // Reset view / projection changes if any
            ResetViewProjMode();

            targetRenderSystem.EndProfileEvent();
        }