Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





C-sharp Y Directx, Lo Mas Basico

Iniciado por zupervaca, 19 de Julio de 2005, 01:47:24 AM

« anterior - próximo »

zupervaca

 os pongo un codigo muy basico de c-sharp con windows form y directx que controla el reseteo del device y permite intercambiar a modo ventana y pantalla completa con alt+enter, estoy seguro que mas de uno se sorprendera de lo facil que es programar con c-sharp y directx managed y a otros este codigo les ayudara ;)

using System;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Threading;

namespace DemoDirectX
{
   public partial class Form1 : Form
   {
       // Device de directx y variable para saber si se perdio o no el device
       private Device Device;
       private bool IsDeviceLost;

       // Constructor
       public Form1()
       {
           InitializeComponent();
           // Indicar que queremos recibir todo los mensajes relacionados con el dibujado del control en el
           //  mensaje paint e indicar que la ventana no tiene color de fondo (transparente la parte cliente)
           this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
           // Configurar estructura para crear el device
           PresentParameters pp = new PresentParameters();
           pp.Windowed = true;
           pp.AutoDepthStencilFormat = DepthFormat.D24S8;
           pp.BackBufferCount = 2;
           pp.BackBufferFormat = Format.A8R8G8B8;
           pp.BackBufferWidth = 1280;
           pp.BackBufferHeight = 1024;
           pp.DeviceWindow = this;
           pp.DeviceWindowHandle = this.Handle;
           pp.EnableAutoDepthStencil = true;
           pp.ForceNoMultiThreadedFlag = false;
           pp.MultiSample = MultiSampleType.None;
           pp.MultiSampleQuality = 0;
           pp.PresentationInterval = PresentInterval.Immediate;
           pp.PresentFlag = PresentFlag.None;
           pp.SwapEffect = SwapEffect.Discard;
           if (pp.Windowed)
           {
               // En modo ventana no se puede indicar el refresco
               pp.FullScreenRefreshRateInHz = 0;
           }
           else
           {
               // A pantalla completa ponemos un refresco y le quitamos el borde
               pp.FullScreenRefreshRateInHz = 60;
               this.FormBorderStyle = FormBorderStyle.None;
           }
           // Creamos el device
           this.Device = new Device(0, DeviceType.Hardware, this.Handle, CreateFlags.MixedVertexProcessing, new PresentParameters(pp));
           // Agregamos eventos
           this.Device.DeviceLost += new EventHandler(OnDeviceLost);
           this.Device.DeviceReset += new EventHandler(OnDeviceReset);
           // Llamamos a esta funcion para inicializar la escena
           OnDeviceReset(null, null);
       }

       // Pintamos el formulario
       private int k = 0;
       private void Form1_Paint(object sender, PaintEventArgs e)
       {
           // Hacemos que cambie de color el fondo del formulario
           k++;
           this.Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, new ColorValue(k, k * 2, k * 3).ToArgb(), 1.0f, 0);
           this.Device.BeginScene();
           // ...
           this.Device.EndScene();
           this.Present();
           Invalidate();
       }

       // Esta funcion actualiza el formulario con lo que hayamos renderizado, si el device se pierde por lo que
       //  se lo intentara recuperar siempre que se llame
       public void Present()
       {
           if (!IsDeviceLost)
           {
               try
               {
                   // Actualizar
                   this.Device.Present();
               }
               catch (DeviceLostException)
               {
               }
           }
           else
           {
               // Intentar recuperar el device
               try
               {
                   // Intentar recuperar el device cada cierto tiempo sin quemar la cpu
                   Thread.Sleep(500);
                   this.Device.TestCooperativeLevel();
               }
               catch (DeviceLostException)
               {
               }
               catch (DeviceNotResetException)
               {
                   try
                   {
                       this.Device.Reset(this.Device.PresentationParameters);
                   }
                   catch (DeviceLostException)
                   {
                   }
               }
           }
       }

       // El device se ha perdido
       void OnDeviceLost(object sender, EventArgs e)
       {
           IsDeviceLost = true;
           // ...
       }

       // Si el device se pierde resetea se llamara a esta funcion
       void OnDeviceReset( object sender, EventArgs e )
       {
           IsDeviceLost = false;
           // ...
       }

       // Comprobamos el teclado
       //  this.KeyPreview = true; Esto nos permite chequear el teclado sin importar donde este el foco
       //  podemos hacerlo a mano o desde el editor de formularios
       private void Form1_KeyDown(object sender, KeyEventArgs e)
       {
           if (e.KeyCode == Keys.Escape)
           {
               // Sefini
               Close();
           }
           // Mirar si queremos pasar a modo pantalla completa
           if (e.Alt && e.KeyCode == Keys.Enter)
           {
               if (this.Device.PresentationParameters.Windowed)
               {
                   // Pasar a modo pantalla completa
                   this.Device.PresentationParameters.Windowed = false;
                   this.Device.PresentationParameters.FullScreenRefreshRateInHz = 60;
                   this.FormBorderStyle = FormBorderStyle.None;
                   this.Device.Reset(this.Device.PresentationParameters);
               }
               else
               {
                   // Pasar a modo ventana
                   this.Device.PresentationParameters.Windowed = true;
                   this.Device.PresentationParameters.FullScreenRefreshRateInHz = 0;
                   this.FormBorderStyle = FormBorderStyle.Sizable;
                   this.Device.Reset(this.Device.PresentationParameters);
               }
           }
       }
   }
}


para que rule todo solo teneis que crear un proyecto normal y corriente y agregar los eventos de pintado y teclado, si teneis alguna duda ya sabeis

saludos

[EX3]

 Estan geniales estas aportaciones. A mi en su dia precise ayuda con el tema del reseteo de D3D desde VB y gracias a un codigo de Helius en C++ de unos de sus proyectos que me mostro en el foro pude solventar mi problema aparte de añadir funcionalidades como lo del cambio de modo con Alt-Enter, como tu has aplicado, y mediante el API de Windows modificar las caracteristicas de la ventana (que VB te las bloquea en tiempo de ejecucion) tales como el borde, barra de titulos, etc... Seguro que mas de uno agradecera este codigo ;)

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

vincent

 Ayer me empezé a mirar el tema de C# con el link que puso Zupervaca en el post de links.

Seguro que este código me sirve de mucha ayuda en un futuro. Gracias!

Por cierto, que el hombre de Codesampler está pasando sus ejemplos a C# también, y hay un colaborador que envia códigos de VB con DX.

Saludos!
Desarrollo en .Net y metodologías http://devnettips.blogspot.com

Haddd

 No es correcto utilizar el OnPaint.


  public abstract class Main
   {
 Form frm;

 private void OnApplicationIdle(object sender, EventArgs e)
 {
  while (AppStillIdle)
  {
   // Render a frame during idle time (no messages are waiting)
   Haddd.BeginUpdate();

   // Si la función devuelve false indica que queremos salir de la aplicación
   bool continuar = Action();

   Haddd.EndUpdate();

   if (continuar)
   {
    Render();

   }
   else
   {
    // Debemos liberar los recursos de forma controlada
    Haddd.End();

    frm.Close();
   }
  }
 }

 private bool AppStillIdle
 {
  get
  {
   Message msg;
   return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
  }
 }


 [StructLayout(LayoutKind.Sequential)]
 public struct Message
 {
  public IntPtr hWnd;
  public uint msg;
  public IntPtr wParam;
  public IntPtr lParam;
  public uint time;
  public System.Drawing.Point p;
 }

 [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
 [DllImport("User32.dll", CharSet = CharSet.Auto)]
 public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

 public virtual void BuclePrincipal()
 {
  Haddd.Tools.Configuration.GetConfiguration();

  Haddd.Video.Adapter.Initialization.ReadFromConfig();
/*
  Formulario f = new Formulario();
  f.IsMdiContainer = true;
  f.Show();
*/
  using (frm = new Formulario())
  {
/*
   frm.MdiParent = f;
   frm.Size = new System.Drawing.Size(100, 100);

   Haddd.Form = frm;

   Haddd.Handle = f.Handle;
*/
   Haddd.Form = frm;
   Haddd.Handle = frm.Handle;

   Haddd.Video.Adapter.CreateDevice(frm);

   if (!InitGame(frm))
   {
    MessageBox.Show("Can't init the game.");
    return;
   }

   // Más información sobre esta forma de tratar los msg en
   // http://blogs.msdn.com/tmiller/archive/category/2068.aspx

   Application.Idle += new EventHandler(OnApplicationIdle);
   Application.Run(frm);

  }
 }
 public virtual bool InitGame(Form form)
       {
  return false;
       }
       public virtual bool Action()
       {
           return false;
       }
       public virtual void Render()
       {
       }
   }


Este es nuestro bucle principal. Como veis utilizamos el OnIdle. Esta es la última recomendación de Tom Miller.

Saludos y gracias por el código  ;)  

zupervaca

 es raro, yo la primera vez lo probe con idle me bajaron los fps muchos incluso a veces se paraba en seco, aunque esto fue con el p3 1200, si no os gusta el mensaje paint del formulario podeis utilizar este otro sistema poniendolo en program.cs:

       static void Main()
       {
           Application.EnableVisualStyles();
           Form1 form = new Form1();
           form.Show();
           while (form.Created)
           {
               form.DrawScene();
               Application.DoEvents();
           }
       }


la funcion DrawScene() es la que renderizaria la escena, lo que hace es el clasico bucle de mensajes al estilo c++

tamat

 hablando de bucles principales, alguien me podría decir como sacar los ticks transcurridos? he probdo usando DateTime pero algo debo hacer mal. invoco al DateTime.Now para sacar los ticks actuales y restarlos a los almacendos previamente pero la cifra siempre es muy grande, no es coherente.

Yo estoy usando CSGL y tampoco se muy bien como pasar a fullscreen, por si alguien lo había tocado...
Por un stratos menos tenso

Haddd

 Sobre esto del bucle principial Tom Miller ha probado bastantes cosas. Dice que esta última es la mejor y de hecho es la que dice que ha utilizado para los ejemplos de MDX. Pero bueno, nosotros utilizábamos la otra y no se veía perdida....

zupervaca

 para obtener el tick del reloj actual puedes usar en c-sharp enviroment.tickcount, despues es lo de siempre, mirar si es mayor a 1000 restar etc, cuando llegue a casa pongo el codigo de una clase que hice en c-sharp llamada FPS que sirve para obtener los fps actuales, minimos y maximos

saludos

zupervaca

 lo prometido es deuda

using System;

namespace dib.DirectX
{
   /// <summary> Clase para calcular Imagenes por segundo </summary>
   public class dibFPS
   {
       #region Metodos

       /// <summary> Cada vez que se llama a esta funcion se calcula el tiempo que ha tardado desde la ultima vez </summary>
       /// <returns> Imagenes por segundo </returns>
       public long CalculateFPS()
       {
           // Mirar si ha pasado un segundo
           long Current = Environment.TickCount;
           if (Current - this.Last > 1000)
           {
               // Indicar imagenes por segundo
               this.Last = Current;
               this.fps = this.FPSTemp;
               this.FPSTemp = 0;
               if (this.maxFPS < this.fps)
               {
                   this.maxFPS = this.fps;
               }
               else
               if (this.minFPS > this.fps)
               {
                   this.minFPS = this.fps;
               }
           }
           else
           {
               // Incrementar las imagenes por segundo que sucede
               this.FPSTemp++;
           }
           // Retornar los FPS actuales
           return this.fps;
       }

       #endregion

       #region Propiedades

       /// <summary> Obtener las imagenes por segundo </summary>
       public long FPS
       {
           get
           {
               return this.fps;
           }
       }

       /// <summary> Obtener las imagenes por segundo maximas alcanzadas </summary>
       public long MaxFPS
       {
           get
           {
               return this.maxFPS;
           }
       }

       /// <summary> Obtener las imagenes por segundo minimas alcanzadas </summary>
       public long MinFPS
       {
           get
           {
               return this.minFPS;
           }
       }

       #endregion

       #region Miembros privados

       // Miembros de la clase
       private long fps = 0, maxFPS = 0, minFPS = 99999, FPSTemp = 0, Last = 0;

       #endregion

   }
}


saludos

tamat

 Gracias zupervaca, el Environment.TickCount es lo que buscaba.
Por un stratos menos tenso

Haddd

 Veo que haces uso intensivo de propiedades. Eso está bien, pero te diré que por lo visto el rendimiento baja un 10% si usas propiedades. Y luego tienen otro problema, si haces esto:



public Vector3 Position
{
get
{
   return position;
}

set
{
  position=value;
}
}



Podrás acceder así:

ClaseEjemplo.Posicion=new Vector3(1,1,1);

Pero no esto:

ClaseEjemplo.Posicion.X+=5f;

Y creo que tampoco puedes pasarlo como referencia.

Esto sólo funciona con clases, no con estructuras. Por desgracia, MDX utiliza muuchas estructuras.

Otro "problema" es que al depurar te salta al código que define la Propiedad y por tanto das un salto más.

Nosotros tenemos el tema poco claro. Yo prefiero no utilizar propiedades, pero los demás sí. Es más cómodo no utilizarlas, pero menos seguro. En fin...el problema de siempre...Ganas con unas cosas y pierdes con otras....

zupervaca

 es logico que no funcione Position.X += 5f ya que la propiedad hace referencia a la variable position y no a los valores de la estructura, para modificar directamente el valor de X se deberia de crear esto:
       public float PosX
       {
           get
           {
               return position.X;
           }
           set
           {
               position.X = value;
           }
       }


las estructuras en c-sharp se tratan como variables que al igualar unas a otras se copian los datos internos de ellas con lo que al retornar la estructura Position realmente es como si retornaras un valor int y por eso no puedes acceder a sus valores internos, a mi particularmente me gusta este sistema ya que en c++ las estructuras y las clases son exactamente lo mismo salvo que las estructuras por defecto tienen los valores publicos y la clases privados, la verdad es que se me hace raro criticar al c++ ya que llevo muchos años dandole caña, pero la verdad es que el c-sharp es mucho mejor, sobre todo para el diseño de componentes y sin olvidar que puede que sea un lenguaje que valga para varias plataformas y servir paginas webs  :lol:, eso si, no hay que olvidar que el c-sharp se vuelve algo mas lento, pero creo que merece la pena perder 10 fps por un desarrollo mucho mas rapido

sobre el tema de velocidad de las propieades indicar que el clr pone las propiedades como inline si lo ve oportuno al final de esta pagina por lo menos es lo que dicen http://www.programacion.com/tutorial/csharp/10/, este tutorial es muy bueno ya que ademas enseña lo que hace el c-sharp para convertir propiedades, atributos, etc

estoy previsualizando este mensaje y me hace gracia ya que cuando salio el lenguaje c-sharp lo primero que dije fue: eso es una @#~€!  :lol:

Haddd







Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.