public override String keyboard(Keys keyCode, int x, int y)
        {
            StringBuilder result = new StringBuilder();
            result.AppendLine(keyCode.ToString());
            switch (keyCode) {
            case Keys.NumPad6:
                Camera.MoveTarget(0.5f, 0f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad4:
                Camera.MoveTarget(-0.5f, 0f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad8:
                Camera.MoveTarget(0.0f, 0.5f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad2:
                Camera.MoveTarget(0f, -0.5f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad7:
                Camera.MoveTarget(0.0f, 0.0f, 0.5f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad3:
                Camera.MoveTarget(0f, 0.0f, -0.5f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.D1:
                axis = Vector3.UnitX;
                angle = angle + 1;
                break;
            case Keys.D2:
                axis = Vector3.UnitY;
                angle = angle + 1;
                break;
            case Keys.D3:
                axis = Vector3.UnitZ;
                angle = angle + 1;
                break;
            case Keys.D4:
                break;
            case Keys.D5:
                break;
            case Keys.D6:
                break;
            case Keys.C:
                if (cull)
                {
                    cull = false;
                    GL.Disable(EnableCap.CullFace);
                    result.AppendLine("cull disabled");
                }
                else
                {
                    cull = true;
                    GL.Enable(EnableCap.CullFace);
                    result.AppendLine("cull enabled");
                }
                break;
            case Keys.R:
                if (rotateWorld)
                {
                    rotateWorld = false;
                    result.AppendLine("rotateWorld disabled");
                }
                else
                {
                    rotateWorld = true;
                    result.AppendLine("rotateWorld enabled");
                }
                break;
            case Keys.Escape:
                //timer.Enabled = false;
                break;
            case Keys.Space:
                newPerspectiveAngle = perspectiveAngle + 5f;
                if (newPerspectiveAngle > 120f)
                {
                    newPerspectiveAngle = 30f;
                }
                break;
            case Keys.Z:
                break;
            case Keys.X:
                noWorldMatrix = true;
                currentProgram = g_Unlit;
                reshape();
                break;
            case Keys.Y:
                noWorldMatrix = true;
                currentProgram = g_litShaderProg;
                reshape();
                break;
            case Keys.Q:
                result.AppendLine("currentProgram = " + currentProgram.ToString());
                break;
            case Keys.P:
                if (pause)
                {
                    pause = false;
                }
                else
                {
                    pause = true;
                }
                break;
            case Keys.V:
                playerNumber++;
                if (playerNumber > lastPlayer)
                {
                    playerNumber = 0;
                }
                ChangePlayerView();
                result.AppendLine("Player number = " + playerNumber.ToString());
                break;
            case Keys.I:
                result.AppendLine("cameraToClipMatrix " + cameraToClipMatrix.ToString());
                result.AppendLine(AnalysisTools.CalculateMatrixEffects(cameraToClipMatrix));
                result.AppendLine("worldToCameraMatrix " + worldToCameraMatrix.ToString());
                result.AppendLine(AnalysisTools.CalculateMatrixEffects(worldToCameraMatrix));

                Matrix4 cameraToClipMatrixTimesWorldToCameraMatrix =
                    Matrix4.Mult(cameraToClipMatrix, worldToCameraMatrix);
                result.AppendLine("cameraToClipMatrixTimesWorldToCameraMatrix " +
                    cameraToClipMatrixTimesWorldToCameraMatrix.ToString());
                result.AppendLine(AnalysisTools.CalculateMatrixEffects(cameraToClipMatrixTimesWorldToCameraMatrix));

                Matrix4 worldToCameraMatrixTimesCameraToClipMatrix =
                    Matrix4.Mult(worldToCameraMatrix, cameraToClipMatrix);
                result.AppendLine("worldToCameraMatrixTimesCameraToClipMatrix " +
                    worldToCameraMatrixTimesCameraToClipMatrix.ToString());
                result.AppendLine(AnalysisTools.CalculateMatrixEffects(worldToCameraMatrixTimesCameraToClipMatrix));
                break;
            case Keys.S:
                ball.SetSocketControl();
                result.AppendLine("Socket Control");
                break;
            case Keys.E:
                ball.SetElasticControl();
                result.AppendLine("Socket Control");
                break;
            case Keys.W:
                if (drawWalls)
                    drawWalls = false;
                else
                    drawWalls = true;
                break;
            }
            return result.ToString();
        }
        public override String keyboard(Keys keyCode, int x, int y)
        {
            StringBuilder result = new StringBuilder();
            result.AppendLine(keyCode.ToString());
            switch (keyCode) {
            case Keys.NumPad6:
                Camera.MoveTarget(0.5f, 0f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad4:
                Camera.MoveTarget(-0.5f, 0f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad8:
                Camera.MoveTarget(0.0f, 0.5f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad2:
                Camera.MoveTarget(0f, -0.5f, 0.0f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad7:
                Camera.MoveTarget(0.0f, 0.0f, 0.5f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.NumPad3:
                Camera.MoveTarget(0f, 0.0f, -0.5f);
                result.AppendFormat(Camera.GetTargetString());
                break;
            case Keys.D1:
                axis = Vector3.UnitX;
                angle = angle + 1;
                break;
            case Keys.D2:
                axis = Vector3.UnitY;
                angle = angle + 1;
                break;
            case Keys.D3:
                axis = Vector3.UnitZ;
                angle = angle + 1;
                break;
            case Keys.D4:
                break;
            case Keys.D5:
                break;
            case Keys.D6:
                break;
            case Keys.V:
                newPerspectiveAngle = perspectiveAngle + 5f;
                if (newPerspectiveAngle > 120f)
                {
                    newPerspectiveAngle = 30f;
                }
                break;
            case Keys.S:
                renderWithString = true;
                renderString = "flat";
                break;
            case Keys.M:
                renderWithString = false;
                currentMesh++;
                if (currentMesh > meshes.Count - 1) currentMesh = 0;
                result.AppendLine("Mesh = " + meshes[currentMesh].fileName);
                break;
            case Keys.X:
                noWorldMatrix = true;
                reshape();
                break;
            case Keys.P:
                noWorldMatrix = true;
                currentProgram = otherPrograms[currentOtherProgram++];
                if (currentOtherProgram > otherPrograms.Count - 1) currentOtherProgram = 0;
                result.AppendLine("Program = " + otherPrograms[currentOtherProgram].name);
                break;
            case Keys.Q:
                result.AppendLine("currentProgram = " + currentProgram.ToString());
                break;
            case Keys.I:
                result.AppendLine("Current Mesh Data");
                result.Append(meshes[currentMesh].GetMeshData());
                break;
            case Keys.O:
                scaleFactor = meshes[currentMesh].GetUnitScaleFactor();
                result.Append(scaleFactor.ToString());
                break;
            }

            reshape();
            display();
            return result.ToString();
        }
        //Called whenever a key on the keyboard was pressed.
        //The key is given by the ''key'' parameter, which is in ASCII.
        //It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to
        //exit the program.
        public override String keyboard(Keys keyCode, int x, int y)
        {
            StringBuilder result = new StringBuilder();
            switch (keyCode) {

                case Keys.D1:
                    currentProgram = ObjectColor;
                    noWorldMatrix = false;
                    break;
                case Keys.D2:
                    currentProgram = UniformColor;
                    noWorldMatrix = false;
                    break;
                case Keys.D3:
                    currentProgram = UniformColorTint;
                    noWorldMatrix = false;
                    break;
                case Keys.D4:
                    currentProgram = g_WhiteDiffuseColor;
                    noWorldMatrix = true;
                    break;
                case Keys.D5:
                    currentProgram = g_WhiteAmbDiffuseColor;
                    noWorldMatrix = true;
                    break;
                case Keys.D6:
                    currentProgram = g_VertexDiffuseColor;
                    noWorldMatrix = true;
                    break;

                case Keys.A:
                    current_mesh = g_pCylinderMesh;
                    break;
                case Keys.B:
                    current_mesh = g_pCubeColorMesh;
                    break;
                case Keys.C:
                    current_mesh = g_pPlaneMesh;
                    break;
                case Keys.D:
                    current_mesh = g_pInfinityMesh;
                    break;

                case Keys.I:
                    result.AppendLine("I Decrease g_camTarget.X");
                    Camera.MoveTarget(-4.0f, 0, 0);
                    break;
                case Keys.M:
                    result.AppendLine("M Increase g_camTarget.X");
                    Camera.MoveTarget(4.0f, 0, 0);
                    break;
                case Keys.J:
                    result.AppendLine("J Increase g_camTarget.Z");
                    Camera.MoveTarget(0, 0, 4.0f);
                    break;
                case Keys.K:
                    result.AppendLine("K Decrease g_camTarget.Z");
                    Camera.MoveTarget(0, 0, -4.0f);
                    break;
                case Keys.Escape:
                    //timer.Enabled = false;
                    break;
                case Keys.Space:
                    break;
            }
            result.AppendLine(keyCode.ToString());
            result.AppendLine("currentProgram = " + currentProgram.ToString());

            reshape();
            display();
            return result.ToString();
        }