/*
 * ZPG semestralni prace
 * Petr Vondras, A06564
 * 2007
 */
using System;
using System.IO;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;

namespace Zpg
{
	public struct Strom
	{
		public Vector2 pozice;
	};
	
    public class ZpgD3D : Form
    {
        #region Globalni konstanty
        /// <summary>Pocet bodu sceny v jednom smeru.</summary>
        private const short VELIKOST = 128;

        /// <summary>Vzdalenost bodu sceny v metrech.</summary>
        private const float ROZTEC = 2f;

        /// <summary>Rychlost pohybu v metrech za sekundu.</summary>
        private const float RYCHLOST = 3;

        /// <summary>Vyska postavy v metrech.</summary>
        private const float VYSKA = 1.85f;

        /// <summary>Delka dne (12h) v sekundach.</summary>
        private const float DEN = 60f;
        
        /// <summary>Pocet objektu.</summary>
        private const int OBJEKTU = 50;          
        
        /// <summary>Pocet stromu.</summary>
        private const int STROMU = 100;        
        
        /// <summary>Koeficient zpomaleni do kopce a zrychleni z kopce.
        /// hodnota 0 az 1 (0 = zadne zpomaleni).</summary>
        private const float ZPOMALENI = 0.5f;

        /// <summary>Citlivost mysi - vyssi cislo = mensi citlivost.</summary>
        private const float CITLIVOST = 500.0f;
        #endregion Globalni konstanty

        #region Globalni promenne
        /// <summary>Yaw - rotace podle vertikalni osy - rozhlizeni.</summary>
        public static float yaw = 0;

        /// <summary>Pitch - rotace podle horizontalni osy - koukani nahoru a dolu.</summary>
        private float pitch = 0;
        private bool inveratace = true;

        /// <summary>Pozastavit zobrazeni slunce (slunce dal obyha).</summary>
        private bool pohybSlunce = true;

        /// <summary>Zapnuti - vypnuti baterky.</summary>
        private bool baterka = true;

        /// <summary>Vektor posunu pozice stredu (moji pozice).</summary>
        private Vector3 posun = new Vector3(-(VELIKOST - 1) * ROZTEC / 2, -(VELIKOST - 1) * ROZTEC / 2, 0);

        /// <summary>Vektor pohybu vpred.</summary>
        private short pohybVpred = 0;

        /// <summary>Vektor pohybu bokem.</summary>
        private short pohybUkrok = 0;

        /// <summary>Direct3D device.</summary>		
        private Device device = null;

        /// <summary>Direct3D vertx buffer.</summary>
        private VertexBuffer vertexBuffer = null;

        /// <summary>Direct3D index buffer.</summary>
        private IndexBuffer indexBuffer = null;

        /// <summary>Direct3D mesh.</summary>
        private Mesh meshTeren = null;

        private Texture texturaTerenu = null;

        /// <summary>Vyskova mapa.</summary>
        private float[,] heightData;

        /// <summary>Pole vrcholu.</summary>
        private CustomVertex.PositionTextured[] verts;

        /// <summary>Pole trojuhelniku.</summary>
        private short[] indices;

        /// <summary>Pocet tiku od minuleho framu.</summary>
        private long tiku = 0;					//
        private int pocetTiku = 1;				//
        private int Frames = 0;					//
        private float snimkovaciFrekvence = 0;  // fps behem minule vteriny
        private D3D.Font text;					//
		
        // wireframe
        private bool sitTrojuhelniku = false;
        
        // skybox
        private Mesh meshSkyboxu;               
        private Material[] materialySkyboxu;
        private Texture[] texturySkyboxu;
        
        // objekty
        private bool objekty = true;
        private Mesh meshObjektu;
        private Texture texturaObjektu;
        private int[,] polohaObjektu;
        private int sebranoObjektu;
        
        // konzole
        private Sprite konzoleSprite;
        private Texture[] konzoleTextura;
        private int cisloKonzole = 1;
        private bool konzole = false;
        
        // teleporty
        private int[,] teleporty;
        private Mesh meshTeleportu;
        private Texture texturaTeleportu;
        private int teleportace = -1;
        private long pocatekTeleportace;
        
        // stromy
        System.Collections.ArrayList stromy = new System.Collections.ArrayList(); //pole stromu
        private Texture texturaStromu;
        private VertexBuffer stromVertexBuffer = null;

        #endregion Globalni promenne

        /// <summary>Konstruktor.</summary>
        public ZpgD3D()
        {
            // nastaveni parametru okna
            this.Size = new Size(800, 600);
            this.Text = "Petr Vondraš - ZPG Semestrální práce";

            // pohyb mysi - event handler
            this.MouseMove += new MouseEventHandler(PohybMysi);
            // stisk klavasy - event handler
            this.KeyDown += new KeyEventHandler(StiskKlavesy);
            // uvolneni klavasy - event handler
            this.KeyUp += new KeyEventHandler(UvolneniKlavesy);
        }

        /// <summary>Nacteni vyskovych dat.</summary>       
        private void NacistVysky()
        {
            heightData = new float[VELIKOST, VELIKOST];
            try
            {
                FileStream fs = new FileStream(Application.StartupPath + @"\mapa128x128.raw", FileMode.Open, FileAccess.Read);
                BinaryReader r = new BinaryReader(fs);

                for (int i = 0; i < VELIKOST; i++)
                {
                    for (int y = 0; y < VELIKOST; y++)
                    {
                        heightData[VELIKOST - 1 - y, VELIKOST - 1 - i] = r.ReadByte() / 10;
                    }
                }
                r.Close();
            }
            catch (System.IO.FileNotFoundException)
            {
                MessageBox.Show("Nelze nacist vyskovy soubor:\n" + 
                    Application.StartupPath  + @"\mapa128x128.raw" +
                    "\nNastavuji vysky na 0m", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                for (int i = 0; i < VELIKOST; i++)
                    for (int y = 0; y < VELIKOST; y++)
                        heightData[VELIKOST - 1 - y, VELIKOST - 1 - i] = 0;
            }
        }

        /// <summary>Nastaveni parametru grafiky.</summary>
        public bool InicializaceGrafiky()
        {
            try
            {
                #region nastaveni parametru
                PresentParameters presentParams = new PresentParameters();
                // neni fullscreen
                presentParams.Windowed = true;
                // vyradit framy
                presentParams.SwapEffect = SwapEffect.Discard;
                // zapnout depth stencil
                presentParams.EnableAutoDepthStencil = true;
                // nastavit stencil format
                presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                // zjisteni a nastaveni schopnosti graficke karty
                Caps caps = Manager.GetDeviceCaps(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware);
                CreateFlags flags;
                if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
                    flags = CreateFlags.HardwareVertexProcessing;
                else
                    flags = CreateFlags.SoftwareVertexProcessing;
                // vytvorit D3D device
                device = new Microsoft.DirectX.Direct3D.Device(0,
                    Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, flags, presentParams);
                #endregion nastaveni parametru

                #region nastaveni device
                // vypnout cull - uvidime predni i zadni cast trojuhelniku
                device.RenderState.CullMode = Cull.CounterClockwise;
                // zapnout ZBuffer
                device.RenderState.ZBufferEnable = true;
                device.RenderState.ZBufferWriteEnable = true;
                // zkontrolovat zapnuta svetla
                device.RenderState.Lighting = true;
                // pro pruhlednost
                device.RenderState.AlphaBlendEnable = true;
                device.RenderState.SourceBlend = Blend.SourceAlpha;
                device.RenderState.DestinationBlend = Blend.InvSourceAlpha;
                // priradit textury
                this.NastaveniTextur();
                // naplnit VB
                vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured), VELIKOST * VELIKOST,
                    device, Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default);
                this.VytvoritVertexBuffer(vertexBuffer);
                // naplnit IB
                indexBuffer = new IndexBuffer(typeof(short), (VELIKOST - 1) * (VELIKOST - 1) * 6,
                    device, Usage.WriteOnly, Pool.Default);
                this.VytvoritIndexBuffer(indexBuffer);
                this.VytvoritMesh();
                this.VytvoritSkybox();
                this.VytvoritObjekty();
                this.VytvoritTeleporty();
                this.VytvoritStromy();
				
                // sprite pro konzoli
                konzoleTextura = new Texture[4];
                this.konzoleTextura[0] = TextureLoader.FromFile(device, "konzole_tel.jpg");
                this.konzoleTextura[1] = TextureLoader.FromFile(device, "konzole1.jpg");
                this.konzoleTextura[2] = TextureLoader.FromFile(device, "konzole2.jpg");
                this.konzoleTextura[3] = TextureLoader.FromFile(device, "konzole3.jpg");
                this.konzoleSprite = new Sprite(device);
                #endregion nastaveni device

                return true;
            }
            catch (DirectXException)
            {
                MessageBox.Show("Selhala inicializace direct3D.", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }

        /// <summary>Nastaveni parametru textur.</summary>
        public void NastaveniTextur()
        {
            texturaTerenu = TextureLoader.FromFile(device, Application.StartupPath + @"\texturaTeren.jpg");
            device.SetTexture(0, texturaTerenu);
            // nastaveni vlastnosti textury
            device.TextureState[0].ColorOperation = TextureOperation.Modulate;
            device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
            device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;
            device.TextureState[0].AlphaOperation = TextureOperation.Disable;

            // pro teleporty - pruhlednost
            device.TextureState[0].AlphaOperation = TextureOperation.SelectArg1;
            device.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor;

            // vzorkovani textury
            Caps caps = Manager.GetDeviceCaps(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware);
            if (caps.TextureFilterCaps.SupportsMipMapLinear)
            {
                device.SamplerState[0].MinFilter = TextureFilter.Linear;
                device.SamplerState[0].MagFilter = TextureFilter.Linear;
                device.SamplerState[0].MipFilter = TextureFilter.Linear;
                texturaTerenu.GenerateMipSubLevels();
            }
            else if (caps.TextureFilterCaps.SupportsMipMapPoint)
            {
                device.SamplerState[0].MinFilter = TextureFilter.Point;
                device.SamplerState[0].MagFilter = TextureFilter.Point;
                device.SamplerState[0].MipFilter = TextureFilter.Point;
                texturaTerenu.GenerateMipSubLevels();
            }
            else if (caps.TextureFilterCaps.SupportsMagnifyAnisotropic)
            {
                device.SamplerState[0].MinFilter = TextureFilter.Anisotropic;
                device.SamplerState[0].MagFilter = TextureFilter.Anisotropic;
            }
            else if (caps.TextureFilterCaps.SupportsMagnifyLinear)
            {
                device.SamplerState[0].MinFilter = TextureFilter.Linear;
                device.SamplerState[0].MagFilter = TextureFilter.Linear;
            }
            else
            {
                device.SamplerState[0].MinFilter = TextureFilter.Point;
                device.SamplerState[0].MagFilter = TextureFilter.Point;
            }
            // zamaskovani linek prechodu mezi texturami
            device.SamplerState[0].AddressU = TextureAddress.Mirror;
            device.SamplerState[0].AddressV = TextureAddress.Mirror;
        }

        /// <summary>Naplneni vertex bufferu daty.</summary>
        public void VytvoritVertexBuffer(object sender)
        {
            int indexTextury;
            VertexBuffer vb = (VertexBuffer)sender;
            // zamknuti
            verts = (CustomVertex.PositionTextured[])vb.Lock(0, 0);
            // naplneni
            for (int x = 0; x < VELIKOST; x++)
            {
                for (int y = 0; y < VELIKOST; y++)
                {
                    verts[x + y * VELIKOST].X = ROZTEC * x;
                    verts[x + y * VELIKOST].Y = ROZTEC * y;
                    verts[x + y * VELIKOST].Z = heightData[x, y];
                    if (((double)x / 2) == (Math.Truncate((double)x / 2))) verts[x + y * VELIKOST].Tu = 0;
                    else verts[x + y * VELIKOST].Tu = 1;
                    indexTextury = (int)(Math.Truncate(heightData[x, y]));
                    if (((double)y / 2) == (Math.Truncate((double)y / 2)))
                    {
                        if ((y == 0) || (verts[x + (y - 1) * VELIKOST].Tv != indexTextury / 26f))
                        {
                            verts[x + y * VELIKOST].Tv = indexTextury / 26f;
                            verts[x + (y + 1) * VELIKOST].Tv = (indexTextury + 1) / 26f;
                        }
                        else
                        {
                            verts[x + y * VELIKOST].Tv = (indexTextury - 1) / 26f;
                            verts[x + (y + 1) * VELIKOST].Tv = (indexTextury) / 26f;
                        }
                    }
                }
            }
            // odemknuti
            vb.Unlock();
        }

        /// <summary>Naplneni index bufferu daty.</summary>
        public void VytvoritIndexBuffer(object sender)
        {
            IndexBuffer ib = (IndexBuffer)sender;
            // zamknuti
            indices = (short[])ib.Lock(0, 0);
            // naplneni
            for (int x = 0; x < VELIKOST - 1; x++)
            {
                for (int y = 0; y < VELIKOST - 1; y++)
                {
                    indices[(x + y * (VELIKOST - 1)) * 6] = (short)((x + 1) + (y + 1) * VELIKOST);
                    indices[(x + y * (VELIKOST - 1)) * 6 + 2] = (short)((x + 1) + y * VELIKOST);
                    indices[(x + y * (VELIKOST - 1)) * 6 + 1] = (short)(x + y * VELIKOST);


                    indices[(x + y * (VELIKOST - 1)) * 6 + 3] = (short)((x + 1) + (y + 1) * VELIKOST);
                    indices[(x + y * (VELIKOST - 1)) * 6 + 5] = (short)(x + y * VELIKOST);
                    indices[(x + y * (VELIKOST - 1)) * 6 + 4] = (short)(x + (y + 1) * VELIKOST);

                }
            }
            // odemknuti
            ib.Unlock();
        }

        /// <summary>Naplneni meshe stream daty.</summary>
        private void VytvoritMesh()
        {
            meshTeren = new Mesh((VELIKOST - 1) * (VELIKOST - 1) * 2, VELIKOST * VELIKOST, MeshFlags.Managed, CustomVertex.PositionTextured.Format, device);

            meshTeren.SetVertexBufferData(verts, LockFlags.None);
            meshTeren.SetIndexBufferData(indices, LockFlags.None);

            int[] adjac = new int[meshTeren.NumberFaces * 3];
            meshTeren.GenerateAdjacency(5.5f, adjac);
            meshTeren.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adjac);

            meshTeren = meshTeren.Clone(meshTeren.Options.Value, CustomVertex.PositionNormalTextured.Format, device);
            meshTeren.ComputeNormals();
        }

        /// <summary>Naplneni meshe skyboxu.</summary>
        private void VytvoritSkybox()
        {
            ExtendedMaterial[] poleMaterialu;
            meshSkyboxu = Mesh.FromFile("skybox.x", MeshFlags.Managed, device, out poleMaterialu);

            if ((poleMaterialu != null) && (poleMaterialu.Length > 0))
            {
                materialySkyboxu = new Material[poleMaterialu.Length];
                texturySkyboxu = new Texture[poleMaterialu.Length];

                for (int i = 0; i < poleMaterialu.Length; i++)
                {
                    materialySkyboxu[i] = poleMaterialu[i].Material3D;
                    materialySkyboxu[i].Ambient = materialySkyboxu[i].Diffuse;

                    if ((poleMaterialu[i].TextureFilename != null) && (poleMaterialu[i].TextureFilename != string.Empty))
                    {
                        texturySkyboxu[i] = TextureLoader.FromFile(device, poleMaterialu[i].TextureFilename);
                    }
                }
            }
            meshSkyboxu = meshSkyboxu.Clone(meshSkyboxu.Options.Value, CustomVertex.PositionNormalTextured.Format, device);
            meshSkyboxu.ComputeNormals();
        }
        
        /// <summary>Vytvoreni objektu.</summary>
        private void VytvoritObjekty()
        {
        	// objekty
            meshObjektu = Mesh.FromFile("zpg.x", MeshFlags.Managed, device);
            meshObjektu = meshObjektu.Clone(meshObjektu.Options.Value, CustomVertex.PositionNormal.Format, device);
            meshObjektu.ComputeNormals();
            
            texturaObjektu = TextureLoader.FromFile(device, Application.StartupPath + @"\objekt.dds");
            sebranoObjektu = 0;
            polohaObjektu = new int[OBJEKTU,3];
            Random random = new Random();
            for (int i = 0; i < OBJEKTU; i++)
            {
            	polohaObjektu[i, 0] = random.Next(5, VELIKOST - 5);
                polohaObjektu[i, 1] = random.Next(5, VELIKOST - 5);
            	polohaObjektu[i, 2] = 1;
            }         
        }

        /// <summary>Vytvoreni billboardu teleport.</summary>
        private void VytvoritTeleporty()
        {
            teleporty = new int[5,3];
            teleporty[0, 0] = 32; teleporty[0, 1] = 32; teleporty[0, 2] = 1;
            teleporty[1, 0] = 32; teleporty[1, 1] = 96; teleporty[1, 2] = 2;
            teleporty[2, 0] = 76; teleporty[2, 1] = 68; teleporty[2, 2] = 3;
            teleporty[3, 0] = 96; teleporty[3, 1] = 32; teleporty[3, 2] = 4;
            teleporty[4, 0] = 96; teleporty[4, 1] = 96; teleporty[4, 2] = 0;

            meshTeleportu = Mesh.FromFile("teleport.x", MeshFlags.Managed, device);
            meshTeleportu = meshTeleportu.Clone(meshTeleportu.Options.Value, CustomVertex.PositionNormal.Format, device);
            meshTeleportu.ComputeNormals();

            texturaTeleportu = TextureLoader.FromFile(device, Application.StartupPath + @"\teleport.dds"); 	
        }
         
        /// <summary>Vytvoreni billboardu strom.</summary>
        private void VytvoritStromy()
        {
            Random random = new Random(); 
			for (int i=0; i < STROMU; i++)
			{
				Strom strom = new Strom();
				// nove vytvoreny strom musi splnit nekolik predpokladu:
            	bool vyhovuje = false;
            	while (!vyhovuje)
            	{
            		strom.pozice.X = random.Next(5, VELIKOST - 5);
            		strom.pozice.Y = random.Next(5, VELIKOST - 5);
            		vyhovuje = true;
            		// nesmi byt moc vysoko
            		if (heightData[(int)strom.pozice.X,(int)strom.pozice.Y] > 5)
            			vyhovuje = false;
            		// nesmi byt moc blizko jiz vytvoreneho stromu
            		if (stromy.Count > 0)
            		{
            			for (int j=0; j < stromy.Count; j++)
						{
            				if ((Math.Pow(strom.pozice.X - ((Strom)stromy[j]).pozice.X, 2) +
            				     Math.Pow(strom.pozice.Y - ((Strom)stromy[j]).pozice.Y, 2)) < 4)
								vyhovuje = false;
						}
            		}
            		// ani objektu
            		for (int j=0; j < OBJEKTU; j++)
            		{
						if ((Math.Pow(strom.pozice.X - polohaObjektu[j, 0], 2) +
			     			 Math.Pow(strom.pozice.Y - polohaObjektu[j, 1], 2)) < 4)
							vyhovuje = false;
            		}
            		// ani teleportu
            		for (int j=0; j < teleporty.Length/3; j++)
            		{
						if ((Math.Pow(strom.pozice.X - teleporty[j, 0], 2) +
			     			 Math.Pow(strom.pozice.Y - teleporty[j, 1], 2)) < 4)
							vyhovuje = false;
            		}            		
            	}
            	stromy.Add(strom);
			}
            texturaStromu = TextureLoader.FromFile(device, Application.StartupPath + @"\strom.dds");
   			// naplneni vertex bufferu
            stromVertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured),
            	4, device, Usage.WriteOnly, CustomVertex.PositionNormalTextured.Format, Pool.Default);
			VertexBuffer vb = (VertexBuffer)stromVertexBuffer;
			CustomVertex.PositionNormalTextured[] v = new CustomVertex.PositionNormalTextured[4];
            System.Random rand = new System.Random();
			v[0].X = -2f; v[0].Y = 0f; v[0].Z = -3f;
			v[0].Nx = 0f; v[0].Ny = 1f; v[0].Nz = 0f;
			v[0].Tu = 0f; v[0].Tv = 1f;
			v[1].X = -2f; v[1].Y = 0f; v[1].Z = 3f;
			v[1].Nx = 0f; v[1].Ny = 1f; v[1].Nz = 0f;
			v[1].Tu = 0f; v[1].Tv = 0f;
			v[2].X = 2f; v[2].Y = 0f; v[2].Z = -3f;
			v[2].Nx = 0f; v[2].Ny = 1f; v[2].Nz = 0f;
			v[2].Tu = 1f; v[2].Tv = 1f;
			v[3].X = 2f; v[3].Y = 0f; v[3].Z = 3f;
			v[3].Nx = 0f; v[3].Ny = 1f; v[3].Nz = 0f;
			v[3].Tu = 1f; v[3].Tv = 0f;			
			vb.SetData(v, 0, 0);         	
        }        
        
        /// <summary>Vykresleni objektu.</summary>
        private void RenderObjekty()
        {
        	for (int i = 0; i < OBJEKTU; i++)
        	{
        		// pokud objekt dosud nebyl sebran
        		if (polohaObjektu[i,2] == 1)
        		{
        			// nastaveni umisteni a rotace objektu
   					device.Transform.World = Matrix.Identity;
            		device.Transform.World *= Matrix.RotationX((float)(Math.PI/2));
            		device.Transform.World *= Matrix.Translation(new Vector3(-0.8f,0,0));
            		device.Transform.World *= Matrix.RotationZ((float)(-Math.PI * Environment.TickCount / 4000));
            		device.Transform.World *= Matrix.Translation(posun);
            		device.Transform.World *= Matrix.Translation(polohaObjektu[i,0]*ROZTEC,
            		                                             polohaObjektu[i,1]*ROZTEC,
            		                                             1f+0.5f*(float)Math.Sin(Math.PI * Environment.TickCount / 4000)
            		                                             +heightData[polohaObjektu[i,0],polohaObjektu[i,1]]);
            		// vykresleni objektu
            		device.SetTexture(0, texturaObjektu);
            		for (int j = 0; j < meshObjektu.GetAttributeTable().Length; ++j)
            		{
                		meshObjektu.DrawSubset(j);
            		}
            		// pokud jsem dostatecne blizko
            		if ((Math.Abs(posun.X + polohaObjektu[i,0]*ROZTEC) < 1)&&
            		    (Math.Abs(posun.Y + polohaObjektu[i,1]*ROZTEC) < 1))
            		{
            			// seberu objekt
            			polohaObjektu[i,2] = 0;
            			sebranoObjektu += 1;
            		}
        		}
        	}
        }
        
        /// <summary>Vykresleni konzole.</summary>
		private void RenderKonzole()        
        {
        	konzoleSprite.Begin(SpriteFlags.AlphaBlend);
            konzoleSprite.Draw(konzoleTextura[cisloKonzole], 
                               new Rectangle(0, 0, 700, 500), 
                               new Vector3(0, 0, 0), new Vector3(50, 50, 0), 
                               Color.FromArgb(100, 255, 255, 255));
            konzoleSprite.End();
        }

        /// <summary>Vykresleni teleportu pripadne teleportace.</summary>
		private void RenderTeleport()        
        {
			// smycka na teleporty
			for (int i = 0; i < teleporty.Length/3; i++)
			{    
				// nastaveni pozice a natoceni
				device.Transform.World = Matrix.Identity;
                //device.Transform.World *= Matrix.RotationY((float)(-Math.PI * Environment.TickCount / 4000));
				if (-posun.X < teleporty[i,0]*ROZTEC)
					device.Transform.World *= Matrix.RotationZ((float)Math.Acos((-posun.Y-teleporty[i,1]*ROZTEC)
                        /Math.Sqrt(Math.Pow(posun.X+teleporty[i,0]*ROZTEC,2)+Math.Pow(posun.Y+teleporty[i,1]*ROZTEC,2))));
				else
					device.Transform.World *= Matrix.RotationZ((float)-Math.Acos((-posun.Y-teleporty[i,1]*ROZTEC)
                        /Math.Sqrt(Math.Pow(posun.X+teleporty[i,0]*ROZTEC,2)+Math.Pow(posun.Y+teleporty[i,1]*ROZTEC,2))));
            	device.Transform.World *= Matrix.Translation(posun);
            	device.Transform.World *= Matrix.Translation(teleporty[i,0]*ROZTEC,
            		                                         teleporty[i,1]*ROZTEC,
            		                                         0.25f+heightData[teleporty[i,0],teleporty[i,1]]);
				// vykresleni
                device.SetTexture(0, texturaTeleportu);
                for (int j = 0; j < meshTeleportu.GetAttributeTable().Length; ++j)
                {
                    meshTeleportu.DrawSubset(j);
                }
                // posunuti + druhe vykresleni
                device.Transform.World *= Matrix.Translation(0, 0, 2.5f);
                for (int j = 0; j < meshTeleportu.GetAttributeTable().Length; ++j)
                {
                    meshTeleportu.DrawSubset(j);
                }
                // pokud prave neprobiha teleportace
                if (teleportace == -1)
                {
                    // a pokud jsem dostatecne blizko
                    if ((Math.Abs(posun.X + teleporty[i, 0] * ROZTEC) < 1) &&
                        (Math.Abs(posun.Y + teleporty[i, 1] * ROZTEC) < 1))
                    {
                        // dojde k teleportaci
                        teleportace = i;
                        pocatekTeleportace = Environment.TickCount;
                    }
                }
			}
        }

        /// <summary>Animace teleportace.</summary>
        private void Teleportace()
        {
            if (teleportace != -1)
            {
                int intenzita = (int)(Environment.TickCount - pocatekTeleportace) / 5;
                if (intenzita < 256)
                {
                    konzoleSprite.Begin(SpriteFlags.AlphaBlend);
                    konzoleSprite.Draw(konzoleTextura[0],
                                       new Rectangle(0, 0, 800, 600),
                                       new Vector3(0, 0, 0), new Vector3(0, 0, 0),
                                       Color.FromArgb(intenzita, 255, 255, 255));
                    konzoleSprite.End();
                }
                else if (intenzita < 512)
                {
                    konzoleSprite.Begin(SpriteFlags.AlphaBlend);
                    konzoleSprite.Draw(konzoleTextura[0],
                                       new Rectangle(0, 0, 800, 600),
                                       new Vector3(0, 0, 0), new Vector3(0, 0, 0),
                                       Color.FromArgb(511 - intenzita, 255, 255, 255));
                    konzoleSprite.End();
                    // posun na pozici noveho teleportu 
                    // + kousek dal ve smeru pohledu, abych nespustil hned dalsi teleportaci
                    posun.X = -(float)(2 * Math.Sin(yaw)) - teleporty[teleporty[teleportace, 2], 0] * ROZTEC;
                    posun.Y = -(float)(2 * Math.Cos(yaw)) - teleporty[teleporty[teleportace, 2], 1] * ROZTEC;
                    posun.Z = -VYSKA - PocatecniVyska(posun.X, posun.Y);
                }
                else teleportace = -1;
            }
        }

        /// <summary>Vykresleni teleportu pripadne teleportace.</summary>
		private void RenderStromu()        
        {
            device.SetStreamSource(0, stromVertexBuffer, 0);
            device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
            device.SetTexture(0, texturaStromu);
            // serazeni stromu
            stromy.Sort(new RazeniStromu());
			// smycka na stromy
			foreach (Strom strom in stromy)
			{    
				// nastaveni pozice a natoceni
				device.Transform.World = Matrix.Identity;
				if (-posun.X < strom.pozice.X*ROZTEC)
					device.Transform.World *= Matrix.RotationZ((float)Math.Acos((-posun.Y-strom.pozice.Y*ROZTEC)
                        /Math.Sqrt(Math.Pow(posun.X+strom.pozice.X*ROZTEC,2)+Math.Pow(posun.Y+strom.pozice.Y*ROZTEC,2))));
				else
					device.Transform.World *= Matrix.RotationZ((float)-Math.Acos((-posun.Y-strom.pozice.Y*ROZTEC)
                        /Math.Sqrt(Math.Pow(posun.X+strom.pozice.X*ROZTEC,2)+Math.Pow(posun.Y+strom.pozice.Y*ROZTEC,2))));
            	device.Transform.World *= Matrix.Translation(posun);
            	device.Transform.World *= Matrix.Translation(strom.pozice.X*ROZTEC,
            		                                         strom.pozice.Y*ROZTEC,
            		                                         3f+heightData[(int)strom.pozice.X,(int)strom.pozice.Y]);
				// vykresleni
				device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
				// detekce kolize pri 
				// pokud se dostanu blize nez 1m ke kmeni, jsme vracen na
				// vzdalenost 1m ve smeru od stromu pres pozici
				// vysledkem je posun po kruhu (o polomeru 1m) kolem kmenu
				Vector2 odStromuKPozici = new Vector2(posun.X+strom.pozice.X*ROZTEC, posun.Y+strom.pozice.Y*ROZTEC);
				if (odStromuKPozici.Length() < 1)
				{
					odStromuKPozici.Normalize();
					//odStromuKPozici.Multiply(1);
					posun.X = -strom.pozice.X*ROZTEC + odStromuKPozici.X;
					posun.Y = -strom.pozice.Y*ROZTEC + odStromuKPozici.Y;
				}
			}
        }		
		
		/// <summary>Vykresleni trojuhelnikove site.</summary>
		private void RenderSitTrojuhelniku()        
        {
			device.Lights[0].Enabled = false;
            device.Lights[1].Enabled = false;   
            D3D.Material staryMtrl = new D3D.Material();
            staryMtrl.Diffuse = device.Material.Diffuse;
            staryMtrl.Ambient = device.Material.Ambient;
            D3D.Material novyMtrl = new D3D.Material();
            novyMtrl.Diffuse = Color.Black;
            novyMtrl.Ambient = Color.Black;           
            device.Material = novyMtrl;
            device.RenderState.FillMode = FillMode.WireFrame;         
            device.SetStreamSource(0, vertexBuffer, 0);
            device.VertexFormat = CustomVertex.PositionOnly.Format;
            device.Indices = indexBuffer;			
           	device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, VELIKOST*VELIKOST, 0, (VELIKOST-1)*(VELIKOST-1)*2);
           	// vratit na plne vykreslovani
            device.RenderState.FillMode = FillMode.Solid;
            device.Material = staryMtrl;
			device.Lights[0].Enabled = true;
            device.Lights[1].Enabled = true;              
        }        
		
        /// <summary>Nastaveni matic projekce, sveta a kamery.</summary>
        private void NastaveniMatic()
        {
            // posun sveta dle nasi pozice
            device.Transform.World = Matrix.Translation(posun);

            // nastaveni kamery a natoceni pohledu
            device.Transform.View = Matrix.LookAtLH(new Vector3(0, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1));
            device.Transform.View *= Matrix.RotationY(yaw);
            device.Transform.View *= Matrix.RotationX(pitch);

            // perspektivni transformace
            // + nastaveni hlobky viditelnosti
            float aspect = (float)this.ClientSize.Width / (float)this.ClientSize.Height;
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, aspect, 0.1f, 200.0f);
        }

        /// <summary>Nastaveni vlastnosti svetel a materialu.</summary>
        private void NastaveniSvetel()
        {
            System.Drawing.Color col = System.Drawing.Color.White;
            // nastavit material terenu.
            // v jednom case muze byt pouzit pouze jeden material
            D3D.Material mtrl = new D3D.Material();
            mtrl.Diffuse = col;
            mtrl.Ambient = col;
            device.Material = mtrl;

            // nastavit slunce.
            // v nastaveni grafiky je treba enablovat D3DRS_LIGHTING
            if (pohybSlunce)
            {
                device.Lights[0].Type = LightType.Directional;
                device.Lights[0].Diffuse = System.Drawing.Color.White;
                device.Lights[0].Direction =
                    new Vector3((float)Math.Cos((Math.PI * Environment.TickCount) / (1000 * DEN)),
                            0.0f, -(float)Math.Sin((Math.PI * Environment.TickCount) / (1000 * DEN)));
                // dat svetlo na vedomi grafice - pouze do verze 9.0b
                // device.Lights[0].Commit();
                // zapnout svetlo				
            }
            device.Lights[0].Enabled = true;

            // nasavit baterku.
            if (baterka)
            {
                device.Lights[1].Type = LightType.Spot;
                device.Lights[1].Diffuse = System.Drawing.Color.White;
                //device.Lights[1].Ambient = System.Drawing.Color.White;
                device.Lights[1].Position = new Vector3((float)Math.Cos(pitch) * (float)Math.Sin(yaw)*(-10),
                                                         (float)Math.Cos(pitch) * (float)Math.Cos(yaw) * (-10),
                                                         (float)Math.Sin(pitch) * (-10));
                device.Lights[1].Direction = new Vector3((float)Math.Cos(pitch) * (float)Math.Sin(yaw),
                                                         (float)Math.Cos(pitch) * (float)Math.Cos(yaw),
                                                         (float)Math.Sin(pitch));
                device.Lights[1].InnerConeAngle = 0.5f;
                device.Lights[1].OuterConeAngle = 1f;
                device.Lights[1].Falloff = 5.0f;
                device.Lights[1].Attenuation0 = 0.5f;
                device.Lights[1].Attenuation1 = 0f;
                device.Lights[1].Attenuation2 = 0f;
                device.Lights[1].Range = 110f;
                // device.Lights[1].Commit();
                device.Lights[1].Enabled = true;
            }
            else device.Lights[1].Enabled = false;
        }

        /// <summary>Vypocet posunu po terenu.</summary>
        private void Pohyb()
        {
            // vypocet uplynuleho casu
            long noveTiku = Environment.TickCount;
            float uplynulyCas = noveTiku - tiku;
            tiku = noveTiku;
            // pokud prave nedochazi k teleportaci
            if (teleportace == -1)
            {
                if ((pohybVpred != 0) || (pohybUkrok != 0))
                {
                    Vector3 V1, V2, V3;
                    /// <summary>Vektor smeru pohybu.</summary>
                    Vector3 pohyb = new Vector3((float)Math.Sin(yaw) * pohybVpred - (float)Math.Cos(yaw) * pohybUkrok,
                                                (float)Math.Cos(yaw) * pohybVpred + (float)Math.Sin(yaw) * pohybUkrok,
                                                0);
                    #region Posun ve vodorovnem smeru
                    pohyb.Normalize();
                    pohyb.Multiply(RYCHLOST * uplynulyCas / 1000);

                    // kontrola hranic
                    if ((posun.X - pohyb.X) > -5) pohyb.X = posun.X + 5;
                    if ((posun.X - pohyb.X) < (5 - (VELIKOST - 1) * ROZTEC)) pohyb.X = posun.X - (5 - (VELIKOST - 1) * ROZTEC);
                    if ((posun.Y - pohyb.Y) > -5) pohyb.Y = posun.Y + 5;
                    if ((posun.Y - pohyb.Y) < (5 - (VELIKOST - 1) * ROZTEC)) pohyb.Y = posun.Y - (5 - (VELIKOST - 1) * ROZTEC);
                    #endregion Posun ve vodorovnem smeru

                    #region Vypocet vysky
                    // vrcholy trojuhelnika nad kterym se nachazime
                    Vector3 newPosun = new Vector3(posun.X - pohyb.X, posun.Y - pohyb.Y, 0);
                    int pomocna = (int)(Math.Truncate(-newPosun.X / ROZTEC) + 1 + (Math.Truncate(-newPosun.Y / ROZTEC) + 1) * VELIKOST);
                    V1 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                    if ((-newPosun.X / 2 - Math.Truncate(-newPosun.X / ROZTEC)) < (-newPosun.Y / ROZTEC - Math.Truncate(-newPosun.Y / ROZTEC)))
                    {
                        pomocna = (int)(Math.Truncate(-newPosun.X / ROZTEC) + Math.Truncate(-newPosun.Y / ROZTEC) * VELIKOST);
                        V2 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                        pomocna = (int)(Math.Truncate(-newPosun.X / ROZTEC) + (Math.Truncate(-newPosun.Y / ROZTEC) + 1) * VELIKOST);
                        V3 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                    }
                    else
                    {
                        pomocna = (int)(Math.Truncate(-newPosun.X / ROZTEC) + 1 + Math.Truncate(-newPosun.Y / ROZTEC) * VELIKOST);
                        V2 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                        pomocna = (int)(Math.Truncate(-newPosun.X / ROZTEC) + Math.Truncate(-newPosun.Y / ROZTEC) * VELIKOST);
                        V3 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                    }

                    // vypocet z - souradnice pruseciku plochy trojuhelnika
                    // s rovnobezkou s osou z prochazejici nasi pozici
                    // a zvednuti o vyku postavy nad teren
                    newPosun.Z = -VYSKA - V1.Z
                        - (((V2.Y - V1.Y) * (V3.Z - V1.Z) - (V2.Z - V1.Z) * (V3.Y - V1.Y)) * (V1.X + newPosun.X)
                        + ((V3.X - V1.X) * (V2.Z - V1.Z) - (V3.Z - V1.Z) * (V2.X - V1.X)) * (V1.Y + newPosun.Y))
                        / ((V2.X - V1.X) * (V3.Y - V1.Y) - (V2.Y - V1.Y) * (V3.X - V1.X));
                    pohyb.Z = posun.Z - newPosun.Z;
                    #endregion Vypocet vysky

                    pohyb.Normalize();
                    // zpomaleni do kopce zrychleni z kopce
                    pohyb.Multiply((float)Math.Pow(1 - pohyb.Z, ZPOMALENI));
                    // rychlost
                    pohyb.Multiply(RYCHLOST * uplynulyCas / 1000);
                    posun.Subtract(pohyb);
                }
            }
        }

        /// <summary>Vypocet vysky.</summary>
        private float PocatecniVyska(float X, float Y)
        {
            Vector3 V1, V2, V3;
            // vrcholy trojuhelnika nad kterym se nachazime
            int pomocna = (int)(Math.Truncate(-X / ROZTEC) + 1 + (Math.Truncate(-Y / ROZTEC) + 1) * VELIKOST);
            V1 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
            if ((-X / 2 - Math.Truncate(-X / ROZTEC)) < (-Y / ROZTEC - Math.Truncate(-Y / ROZTEC)))
            {
                pomocna = (int)(Math.Truncate(-X / ROZTEC) + Math.Truncate(-Y / ROZTEC) * VELIKOST);
                V2 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                pomocna = (int)(Math.Truncate(-X / ROZTEC) + (Math.Truncate(-Y / ROZTEC) + 1) * VELIKOST);
                V3 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
            }
            else
            {
                pomocna = (int)(Math.Truncate(-X / ROZTEC) + 1 + Math.Truncate(-Y / ROZTEC) * VELIKOST);
                V2 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
                pomocna = (int)(Math.Truncate(-X / ROZTEC) + Math.Truncate(-Y / ROZTEC) * VELIKOST);
                V3 = new Vector3(verts[pomocna].X, verts[pomocna].Y, verts[pomocna].Z);
            }

            // vypocet z - souradnice pruseciku plochy trojuhelnika
            // s rovnobezkou s osou z prochazejici nasi pozici
            // a zvednuti o vyku postavy nad teren
            return (V1.Z
                + (((V2.Y - V1.Y) * (V3.Z - V1.Z) - (V2.Z - V1.Z) * (V3.Y - V1.Y)) * (V1.X + posun.X)
                + ((V3.X - V1.X) * (V2.Z - V1.Z) - (V3.Z - V1.Z) * (V2.X - V1.X)) * (V1.Y + posun.Y))
                    / ((V2.X - V1.X) * (V3.Y - V1.Y) - (V2.Y - V1.Y) * (V3.X - V1.X)));
        }

        /// <summary>Vykresleni sceny.</summary>
        private void Render()
        {
            if (device == null) return;
            // vycistit backbuffer na modro a nastavit depth buffer
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1f, 0);
            // zacatek sestaveni sceny
            device.BeginScene();
            // skybox
            device.Lights[0].Enabled = false;
            device.Lights[1].Enabled = false;
            device.Transform.World = Matrix.Translation(0, 0, 0);
            short barva = (short)Math.Truncate(255 * Math.Sin((Math.PI * Environment.TickCount) / (1000 * DEN)));
            if (barva < 0) barva = 0;
            device.RenderState.Ambient = Color.FromArgb(barva, barva, barva);
            for (int i = 0; i < materialySkyboxu.Length; i++)
            {
                device.SetTexture(0, texturySkyboxu[i]);
                meshSkyboxu.DrawSubset(i);
            }
            // nastavit svetla a materialy
            NastaveniSvetel();
            // posun po terenu
            Pohyb();
            // nastavit matice sveta, pohledu a projekce  
            NastaveniMatic();
            // vykreslit mesh terenu
            device.SetTexture(0, texturaTerenu);
            int numSubSets = meshTeren.GetAttributeTable().Length;
            for (int i = 0; i < numSubSets; ++i)
            {
                meshTeren.DrawSubset(i);
            }
            // trojuhelnikova mapa
            if (this.sitTrojuhelniku) RenderSitTrojuhelniku();
            // sbirani objektu
            if (objekty) RenderObjekty();
            // vykresleni teleportu
            RenderTeleport();
            // vykresleni stromu
            RenderStromu();
            // zobrazit framerate - nakonec, aby byl navrch
            FrameRate();
            //animace teleportace
            Teleportace();
            // konzole
            if (konzole) RenderKonzole();
            // konec sestaveni
            device.EndScene();
            // zobrazeni vysledku
            device.Present();
        }

        /// <summary>Vypocet framerate - ulozeno do promenne snimkovaci frekvence.</summary>
        private void FrameRate()
        {
            Frames++;
            if (Math.Abs(Environment.TickCount - pocetTiku) > 1000)
            {
                snimkovaciFrekvence = (float)Frames * 1000 / Math.Abs(Environment.TickCount - pocetTiku);
                pocetTiku = Environment.TickCount;
                Frames = 0;
            }
 			text.DrawText(null, string.Format("Framerate : {0:0.00} fps\nBody: {1:00} Max: {2:00}", 
                snimkovaciFrekvence, sebranoObjektu, OBJEKTU),
            	new Rectangle(10, 10, 400, 100), Microsoft.DirectX.Direct3D.DrawTextFormat.Left, Color.Yellow);            
        }

        /// <summary>Stisk klavasy - event handler.</summary>
        private void StiskKlavesy(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Escape:
                    // ukonci aplikaci
                    this.Close();
                    break;
                case Keys.W:
                    // pohyb vpred
                    pohybVpred = 1;
                    break;
                case Keys.S:
                    // pohyb vzad
                    pohybVpred = -1;
                    break;
                case Keys.D:
                    // pohyb vpravo
                    pohybUkrok = 1;
                    break;
                case Keys.A:
                    // pohyb vlevo
                    pohybUkrok = -1;
                    break;
                case Keys.U:
                    if (inveratace) inveratace = false;
                    else inveratace = true;
                    break;
                case Keys.B:
                    // baterka
                    if (baterka) baterka = false;
                    else baterka = true;
                    break;
                case Keys.K:
                    // zobrazit konzoli
                    if (konzole) konzole = false;
                    else konzole = true;
                    break;
                case Keys.Up:
                    // zmena volby na konzoli
                    if (cisloKonzole == 1) cisloKonzole = 3;
                    else cisloKonzole--;
                    break;
                case Keys.Down:
                    // zmena volby na konzoli
                    if (cisloKonzole == 3) cisloKonzole = 1;
                    else cisloKonzole++;
                    break;
                case Keys.Enter:
                    // potvrzeni volby konzole
                    if (konzole)
                    {
                    	if (cisloKonzole == 1)
            			{
                    		if (baterka) baterka = false;
                    		else baterka = true;
                    	}
						else if (cisloKonzole == 2)
						{
                    		if (objekty) objekty = false;
                    		else objekty = true;							
						}
						else
						{
                    		if (sitTrojuhelniku) sitTrojuhelniku = false;
                    		else sitTrojuhelniku = true;							
                    	}
                    };
                    break;
                case Keys.Space:
                    // nastavit vychozi hodnoty
                    posun.X = -VELIKOST / 2 * ROZTEC;
                    posun.Y = -VELIKOST / 2 * ROZTEC;
                    posun.Z = -VYSKA - PocatecniVyska(posun.X,posun.Y);
                    yaw = 0;
                    pitch = 0;
                    break;
                default:
                    break;
            }
        }

        /// <summary>Uvolneni klavasy - event handler.</summary>
        private void UvolneniKlavesy(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.W:
                    // pohyb vpred
                    pohybVpred = 0;
                    break;
                case Keys.S:
                    // pohyb vzad
                    pohybVpred = 0;
                    break;
                case Keys.D:
                    // pohyb vpravo
                    pohybUkrok = 0;
                    break;
                case Keys.A:
                    // pohyb vlevo
                    pohybUkrok = 0;
                    break;
                default:
                    break;
            }
        }

        /// <summary>Pohyb mysi - event handler.</summary>
        private void PohybMysi(object sender, MouseEventArgs e)
        {
            // vypocet stredu okna
            Size s = this.ClientSize;
            int cx = s.Width / 2;
            int cy = s.Height / 2;

            // kontrola zda se pohnulo s mysi
            if (e.X == cx && e.Y == cy) return;

            // aktualizace uhlu
            yaw += (float)(cx - e.X) / CITLIVOST;
            if (inveratace)
                pitch += (float)(cy - e.Y) / CITLIVOST;
            else pitch -= (float)(cy - e.Y) / CITLIVOST;

            // kontrola rozsahu uhlu
            if (yaw > 2 * Math.PI) yaw -= 2 * (float)Math.PI;
            if (pitch > Math.PI / 2) pitch = (float)Math.PI / 2;
            else if (pitch < -Math.PI / 2) pitch = -(float)Math.PI / 2;

            // vypocitat pozici stredu rel. k desktopu z pozice stredu rel. k soucasnemu oknu
            Point center = PointToScreen(new Point(cx, cy));
            // vycentrovat kurzor
            Cursor.Position = center;
        }

        /// <summary>Vlastni spusteni.</summary>
        private void Run()
        {
            // schovat kurzor mysi
            Cursor.Hide();
            // zjistit velikost okna a spocitat stred
            Size s = this.ClientSize;
            int cx = s.Width / 2;
            int cy = s.Height / 2;
            // vypocitat pozici stredu rel. k desktopu z pozice stredu rel. k soucasnemu oknu
            Point center = PointToScreen(new Point(cx, cy));
            // vycentrovat kurzor
            Cursor.Position = center;
            // nastavit cas
            tiku = Environment.TickCount;
            // priprava zobrazovaneho textu - musi byt po inicializaci device
            System.Drawing.Font systemfont = new System.Drawing.Font("Arial", 12f, FontStyle.Bold);
            text = new D3D.Font(device, systemfont);
            // vypocet pocatecni vysky
            posun.Z = -VYSKA - PocatecniVyska(posun.X,posun.Y);

            // behova smycka
            while (this.Created)
            {
                Render();
                Application.DoEvents(); // melo by byt posledni ve smycce
            }

            // zobrazit kurzor mysi
            Cursor.Show();
            // zrusit zinicializovane
            if (vertexBuffer != null) vertexBuffer.Dispose();
            if (indexBuffer != null) indexBuffer.Dispose();
            if (device != null) device.Dispose();
        }

        /// <summary>Start aplikace.</summary>
        static void Main()
        {
            // vytvoreni okna aplikace
            ZpgD3D form = new ZpgD3D();
            // zobrazeni okna
            form.Show();
            // nacteni vyskove mapy
            form.NacistVysky();
            // inicializace Direct 3D
            form.Text += " - Načítáni dat.";
            if (form.InicializaceGrafiky())
            {
                // vlastni spusteni
                form.Text = "Petr Vondraš - ZPG Semestrální práce";
                form.Run();
            }
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // ZpgD3D
            // 
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Name = "ZpgD3D";
            this.Load += new System.EventHandler(this.ZpgD3D_Load);
            this.ResumeLayout(false);

        }

        private void ZpgD3D_Load(object sender, EventArgs e)
        {

        }
    }
    
/// <summary>
/// trida pro serazeni stromu od vzdalenych k blizkym
/// </summary>
public class RazeniStromu : System.Collections.IComparer
{
	/// <summary>
	/// porovnat dva stromy
	/// </summary>
	public int Compare(object jeden, object druhy)
	{
		Strom strom1 = (Strom)jeden;
		Strom strom2 = (Strom)druhy;

		double vzdalenost1 = strom1.pozice.X * Math.Sin(ZpgD3D.yaw) + strom1.pozice.Y * Math.Cos(ZpgD3D.yaw);
		double vzdalenost2 = strom2.pozice.X * Math.Sin(ZpgD3D.yaw) + strom2.pozice.Y * Math.Cos(ZpgD3D.yaw);
		if (vzdalenost1 == vzdalenost2)
			return 0;
		if (vzdalenost1 < vzdalenost2)
			return +1;
		else
			return -1;
	}
}    
}
