// renders to 0,0,0 ; size will be (mapwidth + 1) * xscale by (mapheight + 1) * yscale
        public void Render(Vector3 camerapos)
        {
            //ExportAsSingleTexture.GetInstance().Export("");

            Gl.glPushMatrix();
            FrustrumCulling culling = FrustrumCulling.GetInstance();
            //IGraphicsHelper g = GraphicsHelperFactory.GetInstance();
            GraphicsHelperGl g = new GraphicsHelperGl();

            int iSectorsDrawn = 0;
            int iChunkDrawsSkippedNoTextureSection = 0;

            int numxchunks = width / chunksize;
            int numychunks = height / chunksize;

            double chunkboundingradius = chunksize * xscale * 1.414 / 2;
            //Console.WriteLine("chunkboundingradius: " + chunkboundingradius);

            g.SetMaterialColor(new Color(1.0, 1.0, 1.0));

            Gl.glDepthFunc(Gl.GL_LEQUAL);
            g.EnableBlendSrcAlpha();

            foreach( RendererPass rendererpass in rendererpasses )
            {
                rendererpass.Apply();
                for (int chunkx = 0; chunkx < numxchunks - 1; chunkx++)
                {
                    for (int chunky = 0; chunky < numychunks - 1; chunky++)
                    {
                        if (chunkusestexturestage[rendererpass.texturestages[0].maptexturestage.maptexturestagemodel][chunkx, chunky])
                        {
                            //if (iSectorsDrawn == 0)
                            //{
                            int chunkmapposx = chunkx * chunksize;
                            int chunkmapposy = chunky * chunksize;
                            int chunkdisplayposx = chunkmapposx * xscale;
                            int chunkdisplayposy = chunkmapposy * yscale;
                            Vector3 chunkmappos = new Vector3(chunkmapposx, chunkmapposy, heightmap[chunkmapposx, chunkmapposy]);
                            Vector3 chunkdisplaypos = new Vector3(chunkdisplayposx, chunkdisplayposy, heightmap[chunkmapposx, chunkmapposy]);
                            Vector3 chunkcentredisplaypos = chunkdisplaypos +
                                new Vector3(chunksize * xscale / 2, chunksize * yscale / 2, 0);
                            if (culling.IsInsideFrustum(chunkcentredisplaypos, chunkboundingradius))
                            //if (true)
                            {
                                iSectorsDrawn++;

                                // check how far away sector is
                                // if nearby we render it in detail
                                // otherwise just render a few points from it
                                double distancesquared = Vector3.DistanceSquared(chunkcentredisplaypos, camerapos);
                                //if ( distancesquared > detaildistance * detaildistance)
                                int stepsize = 16;
                                if (distancesquared < loddistances[0] * loddistances[0])
                                {
                                    stepsize = 1;
                                }
                                else if (distancesquared < loddistances[1] * loddistances[2])
                                {
                                    stepsize = 2;
                                }
                                else if (distancesquared < loddistances[3] * loddistances[3])
                                {
                                    stepsize = 4;
                                }
                                else if (distancesquared < loddistances[4] * loddistances[4])
                                {
                                    stepsize = 8;
                                }
                                else if (distancesquared < loddistances[5] * loddistances[5])
                                {
                                   stepsize = 16;
                                }

                                RenderChunk(chunkx, chunky, stepsize);
                            }
                        }
                        else
                        {
                            iChunkDrawsSkippedNoTextureSection++;
                        }
                        // System.Environment.Exit(1);
                    }
                }
                //Gl.glTranslated(0, 0, 500);
            }
            //System.Environment.Exit(0);
            // Console.WriteLine("chunk renders: " + iSectorsDrawn + " maptextureculls: " + iChunkDrawsSkippedNoTextureSection);
            for (int i = maxtexels - 1; i >= 0; i--)
            {
                g.ActiveTexture(i);
                g.SetTextureScale(1 );
                g.DisableTexture2d();
            }
            Gl.glDepthFunc(Gl.GL_LESS);
            Gl.glDisable(Gl.GL_BLEND);
            g.EnableModulate();
            Gl.glPopMatrix();
        }
        // renders to 0,0,0 ; size will be (mapwidth + 1) * xscale by (mapheight + 1) * yscale
        public void Render(Vector3 camerapos)
        {
            //ExportAsSingleTexture.GetInstance().Export("");

            Gl.glPushMatrix();
            FrustrumCulling culling = FrustrumCulling.GetInstance();
            //IGraphicsHelper g = GraphicsHelperFactory.GetInstance();
            GraphicsHelperGl g = new GraphicsHelperGl();

            int iSectorsDrawn = 0;
            int iChunkDrawsSkippedNoTextureSection = 0;

            int numxchunks = width / chunksize;
            int numychunks = height / chunksize;

            double chunkboundingradius = chunksize * xscale * 1.414 / 2;

            //Console.WriteLine("chunkboundingradius: " + chunkboundingradius);

            g.SetMaterialColor(new Color(1.0, 1.0, 1.0));

            Gl.glDepthFunc(Gl.GL_LEQUAL);
            g.EnableBlendSrcAlpha();

            foreach (RendererPass rendererpass in rendererpasses)
            {
                rendererpass.Apply();
                for (int chunkx = 0; chunkx < numxchunks - 1; chunkx++)
                {
                    for (int chunky = 0; chunky < numychunks - 1; chunky++)
                    {
                        if (chunkusestexturestage[rendererpass.texturestages[0].maptexturestage.maptexturestagemodel][chunkx, chunky])
                        {
                            //if (iSectorsDrawn == 0)
                            //{
                            int     chunkmapposx          = chunkx * chunksize;
                            int     chunkmapposy          = chunky * chunksize;
                            int     chunkdisplayposx      = chunkmapposx * xscale;
                            int     chunkdisplayposy      = chunkmapposy * yscale;
                            Vector3 chunkmappos           = new Vector3(chunkmapposx, chunkmapposy, heightmap[chunkmapposx, chunkmapposy]);
                            Vector3 chunkdisplaypos       = new Vector3(chunkdisplayposx, chunkdisplayposy, heightmap[chunkmapposx, chunkmapposy]);
                            Vector3 chunkcentredisplaypos = chunkdisplaypos +
                                                            new Vector3(chunksize * xscale / 2, chunksize * yscale / 2, 0);
                            if (culling.IsInsideFrustum(chunkcentredisplaypos, chunkboundingradius))
                            //if (true)
                            {
                                iSectorsDrawn++;

                                // check how far away sector is
                                // if nearby we render it in detail
                                // otherwise just render a few points from it
                                double distancesquared = Vector3.DistanceSquared(chunkcentredisplaypos, camerapos);
                                //if ( distancesquared > detaildistance * detaildistance)
                                int stepsize = 16;
                                if (distancesquared < loddistances[0] * loddistances[0])
                                {
                                    stepsize = 1;
                                }
                                else if (distancesquared < loddistances[1] * loddistances[2])
                                {
                                    stepsize = 2;
                                }
                                else if (distancesquared < loddistances[3] * loddistances[3])
                                {
                                    stepsize = 4;
                                }
                                else if (distancesquared < loddistances[4] * loddistances[4])
                                {
                                    stepsize = 8;
                                }
                                else if (distancesquared < loddistances[5] * loddistances[5])
                                {
                                    stepsize = 16;
                                }

                                RenderChunk(chunkx, chunky, stepsize);
                            }
                        }
                        else
                        {
                            iChunkDrawsSkippedNoTextureSection++;
                        }
                        // System.Environment.Exit(1);
                    }
                }
                //Gl.glTranslated(0, 0, 500);
            }
            //System.Environment.Exit(0);
            // Console.WriteLine("chunk renders: " + iSectorsDrawn + " maptextureculls: " + iChunkDrawsSkippedNoTextureSection);
            for (int i = maxtexels - 1; i >= 0; i--)
            {
                g.ActiveTexture(i);
                g.SetTextureScale(1);
                g.DisableTexture2d();
            }
            Gl.glDepthFunc(Gl.GL_LESS);
            Gl.glDisable(Gl.GL_BLEND);
            g.EnableModulate();
            Gl.glPopMatrix();
        }
        // note to self: move this to subscriber?
        void DrawMinimap()
        {
            TerrainModel terrain = MetaverseClient.GetInstance().worldstorage.terrainmodel;

            if (DateTime.Now.Subtract(LastMinimapUpdate).TotalMilliseconds > 1000)
            //if( true )
            {
                List<RendererPass> rendererpasses = new List<RendererPass>();
                bool multipass = true; // force multipass for now for simplicity
                int maxtexels = RendererSdl.GetInstance().MaxTexelUnits;
                if (multipass)
                {
                    for (int i = 0; i < terrain.texturestages.Count; i++)
                    {
                        MapTextureStageModel maptexturestage = terrain.texturestages[i];
                        int numtexturestagesrequired = maptexturestage.NumTextureStagesRequired;
                        if (numtexturestagesrequired > 0) // exclude Nops
                        {
                            RendererPass rendererpass = new RendererPass(maxtexels);
                            for (int j = 0; j < maptexturestage.NumTextureStagesRequired; j++)
                            {
                                rendererpass.AddStage(new RendererTextureStage(maptexturestage, j, true, mapwidth, mapheight));
                            }
                            rendererpasses.Add(rendererpass);
                        }
                    }
                }

                GraphicsHelperGl g = new GraphicsHelperGl();

                //g.ApplyOrtho(windowwidth, windowheight, RendererSdl.GetInstance().OuterWindowWidth, RendererSdl.GetInstance().OuterWindowHeight);

                g.EnableBlendSrcAlpha();
                Gl.glDepthFunc(Gl.GL_LEQUAL);

                int chunkwidth = minimapwidth / numchunks;
                int chunkheight = minimapheight / numchunks;

                float[] ambientLight = new float[] { 0.4f, 0.4f, 0.4f, 1.0f };
                float[] diffuseLight = new float[] { 0.6f, 0.6f, 0.6f, 1.0f };
                float[] specularLight = new float[] { 0.2f, 0.2f, 0.2f, 1.0f };
                float[] position = new float[] { -1.0f, 0.2f, -0.4f, 1.0f };

                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambientLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuseLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, specularLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, position);

                foreach (RendererPass rendererpass in rendererpasses)
                {
                    rendererpass.Apply();

                    for (int x = 0; x + chunkwidth < minimapwidth; x += chunkwidth)
                    {
                        for (int y = 0; y + chunkheight < minimapheight; y += chunkheight)
                        {
                            Gl.glBegin(Gl.GL_QUADS);

                            //double ul = 0;
                            //double ur = mapwidth * Terrain.SquareSize;
                            //double vt = 0;
                            //double vb = mapheight * Terrain.SquareSize;
                            double ul = (double)x / minimapwidth * mapwidth;
                            double ur = (double)(x + chunkwidth) / minimapwidth * mapwidth;
                            double vt = (double)y / minimapheight * mapheight;
                            double vb = (double)(y + chunkheight) / minimapheight * mapheight;

                            double xl = minimapx + x;
                            double xr = minimapx + x + minimapwidth / (double)numchunks;
                            double yt = minimapy + y;
                            double yb = minimapy + y + minimapheight / (double)numchunks;

                            Gl.glTexCoord2d(ul, vt);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ul, vt);
                            g.Normal(renderableheightmap.GetNormal(x * mapwidth / minimapwidth, y * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[, ]);
                            Gl.glVertex2d(xl, yt);

                            Gl.glTexCoord2d(ul, vb);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ul, vb);
                            g.Normal(renderableheightmap.GetNormal(x * mapwidth / minimapwidth, (y + chunkheight) * mapheight / minimapheight));
                            //g.Normal( renderableheightmap.normalsperquad[x * mapwidth / minimapwidth, (y + 1) * mapheight / minimapheight ] );
                            Gl.glVertex2d(xl, yb);

                            Gl.glTexCoord2d(ur, vb);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ur, vb);
                            g.Normal(renderableheightmap.GetNormal((x + chunkwidth) * mapwidth / minimapwidth, (y + chunkheight) * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[(x + 1) * mapwidth / minimapwidth, (y + 1) * mapheight / minimapheight]);
                            Gl.glVertex2d(xr, yb);

                            Gl.glTexCoord2d(ur, vt);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ur, vt);
                            g.Normal(renderableheightmap.GetNormal((x + chunkwidth) * mapwidth / minimapwidth, y * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[(x + 1) * mapwidth / minimapwidth, y * mapheight / minimapheight]);
                            Gl.glVertex2d(xr, yt);

                            Gl.glEnd();
                        }
                    }
                }

                g.ActiveTexture(0);
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, minimaptexture);
                Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, minimapx,
                    RendererSdl.GetInstance().WindowHeight - minimapy - minimapsize,
                    minimapsize, minimapsize, 0);
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
                LastMinimapUpdate = DateTime.Now;

            //                g.RemoveOrtho();

                g.ActiveTexture(1);
                g.DisableTexture2d();
                g.SetTextureScale(1);
                g.ActiveTexture(0);
                g.SetTextureScale(1);

                g.EnableModulate();

                Gl.glDisable(Gl.GL_BLEND);
            }
            else
            {
                GraphicsHelperGl g = new GraphicsHelperGl();

                //Gl.glMatrixMode(Gl.GL_PROJECTION);
                //Gl.glPushMatrix();
                //Gl.glLoadIdentity();
                //Gl.glOrtho(0, windowwidth, windowheight - RendererSdl.GetInstance().OuterWindowHeight, 0, -1, 1); // we'll just draw the minimap directly onto our display
                //Gl.glOrtho(0, windowwidth, windowheight, windowheight - RendererSdl.GetInstance().OuterWindowHeight, -1, 1); // we'll just draw the minimap directly onto our display

                //Gl.glMatrixMode(Gl.GL_MODELVIEW);
                //Gl.glPushMatrix();
                //Gl.glLoadIdentity();

                g.ActiveTexture(0);
                g.EnableTexture2d();
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, minimaptexture);
                //Gl.glBindTexture(Gl.GL_TEXTURE_2D, (terrain.texturestages[0].texture as GlTexture).GlReference);
                Gl.glDisable(Gl.GL_LIGHTING);
                Gl.glBegin(Gl.GL_QUADS);

                Gl.glTexCoord2d(0, 1);
                Gl.glVertex2i(minimapx, minimapy);

                Gl.glTexCoord2d(0, 1 - minimapwidth / (double)minimapsize);
                Gl.glVertex2i(minimapx, minimapy + minimapheight);

                Gl.glTexCoord2d(minimapwidth / (double)minimapsize, 1 - minimapheight / (double)minimapsize);
                Gl.glVertex2i(minimapx + minimapwidth, minimapy + minimapheight);

                Gl.glTexCoord2d(minimapwidth / (double)minimapsize, 1);
                Gl.glVertex2i(minimapx + minimapwidth, minimapy);

                Gl.glEnd();

                Gl.glEnable(Gl.GL_LIGHTING);

                //Gl.glMatrixMode(Gl.GL_PROJECTION);
                //Gl.glPopMatrix();
                //Gl.glMatrixMode(Gl.GL_MODELVIEW);
                //Gl.glPopMatrix();
            }
        }
        // note to self: move this to subscriber?
        void DrawMinimap()
        {
            TerrainModel terrain = MetaverseClient.GetInstance().worldstorage.terrainmodel;

            if (DateTime.Now.Subtract(LastMinimapUpdate).TotalMilliseconds > 1000)
            //if( true )
            {
                List <RendererPass> rendererpasses = new List <RendererPass>();
                bool multipass = true; // force multipass for now for simplicity
                int  maxtexels = RendererSdl.GetInstance().MaxTexelUnits;
                if (multipass)
                {
                    for (int i = 0; i < terrain.texturestages.Count; i++)
                    {
                        MapTextureStageModel maptexturestage = terrain.texturestages[i];
                        int numtexturestagesrequired         = maptexturestage.NumTextureStagesRequired;
                        if (numtexturestagesrequired > 0) // exclude Nops
                        {
                            RendererPass rendererpass = new RendererPass(maxtexels);
                            for (int j = 0; j < maptexturestage.NumTextureStagesRequired; j++)
                            {
                                rendererpass.AddStage(new RendererTextureStage(maptexturestage, j, true, mapwidth, mapheight));
                            }
                            rendererpasses.Add(rendererpass);
                        }
                    }
                }

                GraphicsHelperGl g = new GraphicsHelperGl();

                //g.ApplyOrtho(windowwidth, windowheight, RendererSdl.GetInstance().OuterWindowWidth, RendererSdl.GetInstance().OuterWindowHeight);

                g.EnableBlendSrcAlpha();
                Gl.glDepthFunc(Gl.GL_LEQUAL);

                int chunkwidth  = minimapwidth / numchunks;
                int chunkheight = minimapheight / numchunks;

                float[] ambientLight  = new float[] { 0.4f, 0.4f, 0.4f, 1.0f };
                float[] diffuseLight  = new float[] { 0.6f, 0.6f, 0.6f, 1.0f };
                float[] specularLight = new float[] { 0.2f, 0.2f, 0.2f, 1.0f };
                float[] position      = new float[] { -1.0f, 0.2f, -0.4f, 1.0f };

                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambientLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuseLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, specularLight);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, position);

                foreach (RendererPass rendererpass in rendererpasses)
                {
                    rendererpass.Apply();

                    for (int x = 0; x + chunkwidth < minimapwidth; x += chunkwidth)
                    {
                        for (int y = 0; y + chunkheight < minimapheight; y += chunkheight)
                        {
                            Gl.glBegin(Gl.GL_QUADS);

                            //double ul = 0;
                            //double ur = mapwidth * Terrain.SquareSize;
                            //double vt = 0;
                            //double vb = mapheight * Terrain.SquareSize;
                            double ul = (double)x / minimapwidth * mapwidth;
                            double ur = (double)(x + chunkwidth) / minimapwidth * mapwidth;
                            double vt = (double)y / minimapheight * mapheight;
                            double vb = (double)(y + chunkheight) / minimapheight * mapheight;

                            double xl = minimapx + x;
                            double xr = minimapx + x + minimapwidth / (double)numchunks;
                            double yt = minimapy + y;
                            double yb = minimapy + y + minimapheight / (double)numchunks;

                            Gl.glTexCoord2d(ul, vt);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ul, vt);
                            g.Normal(renderableheightmap.GetNormal(x * mapwidth / minimapwidth, y * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[, ]);
                            Gl.glVertex2d(xl, yt);

                            Gl.glTexCoord2d(ul, vb);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ul, vb);
                            g.Normal(renderableheightmap.GetNormal(x * mapwidth / minimapwidth, (y + chunkheight) * mapheight / minimapheight));
                            //g.Normal( renderableheightmap.normalsperquad[x * mapwidth / minimapwidth, (y + 1) * mapheight / minimapheight ] );
                            Gl.glVertex2d(xl, yb);

                            Gl.glTexCoord2d(ur, vb);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ur, vb);
                            g.Normal(renderableheightmap.GetNormal((x + chunkwidth) * mapwidth / minimapwidth, (y + chunkheight) * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[(x + 1) * mapwidth / minimapwidth, (y + 1) * mapheight / minimapheight]);
                            Gl.glVertex2d(xr, yb);

                            Gl.glTexCoord2d(ur, vt);
                            Gl.glMultiTexCoord2dARB(Gl.GL_TEXTURE1_ARB, ur, vt);
                            g.Normal(renderableheightmap.GetNormal((x + chunkwidth) * mapwidth / minimapwidth, y * mapheight / minimapheight));
                            //g.Normal(renderableheightmap.normalsperquad[(x + 1) * mapwidth / minimapwidth, y * mapheight / minimapheight]);
                            Gl.glVertex2d(xr, yt);

                            Gl.glEnd();
                        }
                    }
                }

                g.ActiveTexture(0);
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, minimaptexture);
                Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, minimapx,
                                    RendererSdl.GetInstance().WindowHeight - minimapy - minimapsize,
                                    minimapsize, minimapsize, 0);
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
                LastMinimapUpdate = DateTime.Now;

//                g.RemoveOrtho();

                g.ActiveTexture(1);
                g.DisableTexture2d();
                g.SetTextureScale(1);
                g.ActiveTexture(0);
                g.SetTextureScale(1);

                g.EnableModulate();

                Gl.glDisable(Gl.GL_BLEND);
            }
            else
            {
                GraphicsHelperGl g = new GraphicsHelperGl();

                //Gl.glMatrixMode(Gl.GL_PROJECTION);
                //Gl.glPushMatrix();
                //Gl.glLoadIdentity();
                //Gl.glOrtho(0, windowwidth, windowheight - RendererSdl.GetInstance().OuterWindowHeight, 0, -1, 1); // we'll just draw the minimap directly onto our display
                //Gl.glOrtho(0, windowwidth, windowheight, windowheight - RendererSdl.GetInstance().OuterWindowHeight, -1, 1); // we'll just draw the minimap directly onto our display

                //Gl.glMatrixMode(Gl.GL_MODELVIEW);
                //Gl.glPushMatrix();
                //Gl.glLoadIdentity();

                g.ActiveTexture(0);
                g.EnableTexture2d();
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, minimaptexture);
                //Gl.glBindTexture(Gl.GL_TEXTURE_2D, (terrain.texturestages[0].texture as GlTexture).GlReference);
                Gl.glDisable(Gl.GL_LIGHTING);
                Gl.glBegin(Gl.GL_QUADS);

                Gl.glTexCoord2d(0, 1);
                Gl.glVertex2i(minimapx, minimapy);

                Gl.glTexCoord2d(0, 1 - minimapwidth / (double)minimapsize);
                Gl.glVertex2i(minimapx, minimapy + minimapheight);

                Gl.glTexCoord2d(minimapwidth / (double)minimapsize, 1 - minimapheight / (double)minimapsize);
                Gl.glVertex2i(minimapx + minimapwidth, minimapy + minimapheight);

                Gl.glTexCoord2d(minimapwidth / (double)minimapsize, 1);
                Gl.glVertex2i(minimapx + minimapwidth, minimapy);

                Gl.glEnd();

                Gl.glEnable(Gl.GL_LIGHTING);

                //Gl.glMatrixMode(Gl.GL_PROJECTION);
                //Gl.glPopMatrix();
                //Gl.glMatrixMode(Gl.GL_MODELVIEW);
                //Gl.glPopMatrix();
            }
        }