Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Tutorial De Física...

Iniciado por Haddd, 10 de Noviembre de 2005, 12:26:09 AM

« anterior - próximo »

Haddd

 


#region Using directives

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;

using HadddEngine;
using HadddEngine.Video;
using HadddEngine.Core;
using HadddEngine.Maths;
using HadddEngine.Scene;
using HadddEngine.Scene.MD5;
using HadddEngine.Physics;
using HadddEngine.Particles;
using HadddEngine.Sound;
using HadddEngine.Tools;

#endregion

namespace Tutorial
{
   /// <summary>
   /// Este ejemplo nos enseñará a crear objetos y asignarle propiedades
   /// físicas
   /// Al no indicar cámara, el motor utiliza una cámara de primera persona por defecto
   /// que se maneja con las teclas WASD y el ratón.
   /// </summary>
   public class HTutorial : HMain
   {
       #region Campos

       /// <summary>Rigid body referido a la esfera</summary>
       HRigidBody sphereBody;

       #endregion

       #region Constructores

       public HTutorial()
       {
       }

       #endregion

       #region Init

       /// <summary>
       /// Este método se llama por el motor una vez que se ha creado el device
       /// </summary>
       /// <param name="form">Formulario que se utiliza como ventana</param>
       /// <returns>true si todo ha ido bien</returns>
       public override bool InitGame(Form form)
       {
           // Creamos los objetos de fuentes y texturas...
           Haddd.AfterCreateDevice();

           // Cargamos texturas, escenas, shaders...por defecto
           Haddd.LoadCreateInHouse();

           #region Objetos

           // Activamos el sistema de físicas de Newton
           Haddd.Physics.Create();

           CreateGround();

           CreateCubes();

           CreateSphere();

           #endregion

           #region Luz

           // Creo una luz omnidireccional

           HOmniLight light = (HOmniLight)Haddd.Scene.Lights.Create("light", HLightType.Omni);

           // Asigno su posición
           light.Position = new Vector3(0, 0, -2f);

           // La intensidad
           light.Multiplier = 1f;

           // La atenuación
           light.AttenuationType = HLightAttenuationType.DualRadius;

           // La distancia a la que empieza a atenuarse
           light.FarAttenuationStart = 1f;

           // La distancia a la que se atenua completamente
           light.FarAttenuationEnd = 2f;

           // El color
           light.Color = HColor.Yellow;

           // Si afecta al diffuse del material
           light.AffectDiffuse = true;

           // Si afecta al specular del material
           light.AffectSpecular = true;

           // Por ahora, que no genere sombras
           light.CastShadows = false;

           // Ponemos la luz ambiente
           Haddd.Scene.Lights.Ambient = new HColor(0.4f, 0.4f, 0.4f);

           #endregion

           Haddd.Scene.Camera.Position = new Vector3(0, 1f, -6f);

           // Una vez que todo está preparado, iniciamos el motor
           Haddd.Begin();

           // Creamos la clase input para gestionar el teclado y el ratón
           Haddd.CreateInput();

           return true;
       }

       #endregion

       #region Creation

       void CreateGround()
       {

           // Ahora crearé un cubo que nos hará de suelo

           Vector3 size = new Vector3(10f, 1f, 10f);

           HMesh groundMesh = HMesh.CreateCube24Vertices(size);

           HMeshObject ground = Haddd.Scene.MeshObjects.Create("ground", false);

           // Ahora asociamos el mesh con el meshobject

           ground.Mesh = groundMesh;

           // Creamos un array de materiales de 1 elemento.
           HMaterial[] material = new HMaterial[1];

           material[0] = new HMaterial();

           // Creamos la capa
           HMaterialLayer layer = new HMaterialLayer();

           // Lo añadimos al material
           material[0].AddLayer(layer);

           layer.DiffuseMap.Texture = Haddd.Video.Textures.Create2D("Pared_Difusse", true);

           // Asignamos a nuestros HMeshObject el materal que acabamos de crear
           ground.Material = material;

           // En Newton, primero debemos definir un objeto "colisionable" dando la
           // forma de este objeto.

           HCollision collision=new HCollision();

           // Tendrá el mismo tamaño que el utilizado al crear el mesh
           // PUESTO QUE EN NEWTON, LA UNIDAD ES EL METRO, IGUAL QUE EN HADDD
           collision.CreateBox(ref size);

           // Ahora creamos un Rigibd body que son los objetos que tienen
           // asociado el tipo de colision y todas las propiedades físicas
           // Asociamos el objeto collision creado y el HMeshObject
           HRigidBody body = Haddd.Physics.RigidBodies.Create(collision, ground);

           // Indicamos la masa
           // En Newton un objeto de masa 0 se considera un objeto estático
           // Es decir, se puede colisionar con él, pero este no reacciona a nada

           body.Mass=0f;

           // Necesitamos indicar una matriz. No utilizamos la misma que en
           // el meshobject porque podemos tener rigidbodies NO asociados a una
           // malla y por eso tenemos que asignar la matriz en lugar de utilizar
           // la del propio HMeshObject

           // En nuestro caso, queremos que la parte superior del suelo esté
           // en la y=0, por ello indicamos el centro y del objeto=-size.Y/2

           Matrix matrix = Matrix.Translation(new Vector3(0,-size.Y/2, 0));

           body.SetMatrix(ref matrix);

           // Ya no utilizamos más la colision así que la liberamos
           collision.Release();
       }

       void CreateCubes()
       {

           HMesh cubeMesh = HMesh.CreateCube24Vertices(0.5f, 0.5f, 0.5f);

           // En Newton, primero debemos definir un objeto "colisionable" dando la
           // forma de este objeto.
           // Como el objeto collision es el mismo para todos los cubos
           // basta crearlo aquí y reutilizarlo

           HCollision collision = new HCollision();

           // Tendrá el mismo tamaño que el utilizado al crear el mesh
           // PUESTO QUE EN NEWTON, LA UNIDAD ES EL METRO, IGUAL QUE EN HADDD
           collision.CreateBox(0.5f, 0.5f, 0.5f);

           float x, y;

           // Vamos a crear cubos apilados...
           for (int px = 0; px < 2; px++)
           {
               for (int py = 0; py < 6; py++)
               {
                   x = (float)px;
                   y = (float)py;

                   // Colocamos los cubos de forma que estén centrados
                   // y uno encima del otro
                   Vector3 pos = new Vector3(-0.25f + (x * 0.5f), 0.25f + (y * 0.5f), 0);

                   HMeshObject cube = Haddd.Scene.MeshObjects.Create("cube"+px.ToString()+py.ToString(), false);

                   // Ahora asociamos el mesh con el meshobject

                   cube.Mesh = cubeMesh;

                   HMaterial[] material = new HMaterial[1];

                   material[0] = new HMaterial();

                   // Creamos la capa
                   HMaterialLayer layer = new HMaterialLayer();

                   // Lo añadimos al material
                   material[0].AddLayer(layer);

                   layer.DiffuseMap.Texture = Haddd.Video.Textures.Create2D("brick_d", true);

                   // Asignamos a nuestros HMeshObject el materal que acabamos de crear
                   cube.Material = material;

                   // Ahora creamos un Rigibd body que son los objetos que tienen
                   // asociado el tipo de colision y todas las propiedades físicas
                   // Asociamos el objeto collision creado y el HMeshObject
                   HRigidBody body = Haddd.Physics.RigidBodies.Create(collision, cube);

                   // Indicamos la masa
                   body.Mass = 4f;

                   // Necesitamos indicar una matriz. No utilizamos la misma que en
                   // el meshobject porque podemos tener rigidbodies NO asociados a una
                   // malla y por eso tenemos que asignar la matriz en lugar de utilizar
                   // la del propio HMeshObject

                   Matrix matrix = Matrix.Translation(pos);

                   body.SetMatrix(ref matrix);
               }
           }

           collision.Release();
       }

       void CreateSphere()
       {

           // Ahora crearé una esfera

           float radius=0.3f;

           HMesh sphereMesh = HMesh.CreateSphere(radius, 20, 20);

           HMeshObject sphere = Haddd.Scene.MeshObjects.Create("sphere", false);

           // Ahora asociamos el mesh con el meshobject

           sphere.Mesh = sphereMesh;

           // Creamos un array de materiales de 1 elemento.
           HMaterial[] material = new HMaterial[1];

           material[0] = new HMaterial();

           // Creamos la capa
           HMaterialLayer layer = new HMaterialLayer();

           // Lo añadimos al material
           material[0].AddLayer(layer);

           layer.DiffuseMap.Texture = Haddd.Video.Textures.Create2D("mancha", true);

           // Asignamos a nuestros HMeshObject el materal que acabamos de crear
           sphere.Material = material;

           // En Newton, primero debemos definir un objeto "colisionable" dando la
           // forma de este objeto.

           HCollision collision = new HCollision();

           collision.CreateSphere(radius, radius, radius);

           // Ahora creamos un Rigibd body que son los objetos que tienen
           // asociado el tipo de colision y todas las propiedades físicas
           // Asociamos el objeto collision creado y el HMeshObject
           HRigidBody body = Haddd.Physics.RigidBodies.Create(collision, sphere);

           // Indicamos la masa

           body.Mass = 40f;

           // Indicamos poca resistencia al aire
           body.LinearDamping = 0.01f;

           // Especificamos la matriz
           Matrix matrix = Matrix.Translation(new Vector3(0,radius,-3.5f));

           body.SetMatrix(ref matrix);

           // Ya no utilizamos más la colision así que la liberamos
           collision.Release();

           // indicamos a la variable global que este es el body que utilizaremos
           sphereBody = body;
       }

       #endregion

       #region Action

       /// <summary>
       /// Este método se llama en cada fotograma y es donde debemos
       /// indicar los eventos de teclado y la acción del juego
       /// </summary>
       /// <returns>true=continuamos; false=salir de la aplicación</returns>
       public override bool Action()
       {
           // ¿Salimos de la aplicación?
           if (Haddd.Input.Keyboard.KeyPressed(Key.Escape)) return false;

           // Si pulsa la G activamos / desactivamos los gizmos
           // La diferencia entre KeyTouch y KeyPressed es que KeyTouch sólo devuelve
           // true cuando la tecla se presiona por primera vez, y no siempre que está presionada
           // como es el caso de KeyPressed

           // KeyTouch actuaría como un teclado normal, devolviendo true sólo 1 vez por
           // cada pulsación de tecla

           if (Haddd.Input.Keyboard.KeyTouch(Key.G))
               Haddd.Scene.ShowGizmos = !Haddd.Scene.ShowGizmos;
           
           // Si pulsa la P puede ver los objetos de colision de Newton
           if (Haddd.Input.Keyboard.KeyTouch(Key.P))
               Haddd.Physics.Debug = !Haddd.Physics.Debug;

           // Si pulsa la I añadimos impulso a la esfera
           if (Haddd.Input.Keyboard.KeyTouch(Key.I))
               sphereBody.AddImpulse(new Vector3(0, 0, 3f));

           // Todo correcto, continuamos
           return true;
       }

       #endregion

       #region Render

       /// <summary>
       /// Este es el método de renderización
       /// </summary>
       public override void Render()
       {
           // Preparamos la escena para que empiece el render
           Haddd.Scene.Begin();

           {

               // Renderizamos todos los elementos de la escena
               Haddd.Scene.Render();

               // Realizamos la postproducción
               Haddd.Scene.ProcessPostProduction();

               // Renderizamos diversas cosas de la escena (gizmos, Axes, Names...)
               Haddd.Scene.RenderMiscellaneous();

               // Obtenemos acceso a la 1ª fuente. El motor por defecto crea una fuente
               HFont fuente = Haddd.Video.Fonts[0];

               // Iniciamos la renderización de las fuentes
               Haddd.Video.Fonts.Begin();

               // Escribimos texto en la primera línea, posición x=0, color amarillo
               fuente.RenderLine(Haddd.Version + ". Press ESC to exit. Press G to Show/Hide Gizmos.", 0, HColor.Yellow);

               // Escribimos texto en la segunda línea, posición x=0, color amarillo
               fuente.RenderLine(Haddd.Video.Render.Stats.Fps + " FPS", 0, HColor.White);

               fuente.RenderLine("Press P to show Physic collisions", 0, HColor.Yellow);

               fuente.RenderLine("Press I to add impulse", 0, HColor.Yellow);

               // Finalizamos la renderización de las fuentes
               Haddd.Video.Fonts.End();

           }

           // Terminamos la escena

           Haddd.Scene.End();

       }

       #endregion
   }
}

Pogacha

 Pensaba que era un poco largo, pero lo lei detenidamente y tal vez soy yo que no me acostumbro a la sintaxys del C# ...  

Supongo que el IDE automaticamente resalta la region que se quiere ver, así en texto plano cuesta un poco darse cuenta de cuando se esta hablando de fisica y cuando de otra cosa.

Saludos.

 Muy bueno.

Por curiosidad, cuáles son las especificaciones del ordenador del cual surgió el snapshot con 70 FPS?

Haddd

 AMD 1800+ Radeon 9500.

El motor hace muchas pasadas para conseguir el resultado final. Evidentemente los fps bajan, sin embargo, no variaría mucho más si pusieras más luces y más polígonos. Es por la arquitectura que utilizamos  ;)  

Gambus

 Viendo estos tutoriales siento electricidad acumulada en las puntas de mis dedos, tengo ganas de empezar a utilizar vuestro motor para implementar unas cuantas ideas que tengo en mi mente saturada  (genial) (por ejemplo un modelador de personajes bipedos a base de parámetros, un entorno para programar bots "inteligentes" aplicando diferentes técnicas de IA (p.e. NEAT - Evolving Neural Networks Through Augmenting Topologies)
Enhorabuena!

Haddd

 Esa es la idea de Haddd. Que tu puedas programar de una forma sencilla, simplemente activando / desactivando flags. Aunque eso es a costa de FPS, yo creo que permite en muy poco tiempo tener resultados espectaculares. Espero también que la gente colabore a detectar los errores y a mejorar el motor, como pasó con Irrlitch y OGRE.

(ole)  

AgeR

 Pues acabo de adquirir una gf6200 (ya sé que no es gran cosa) así que por fin podré disfrutar de vuestro motor cuando lo saquéis, y podré trastear con él, que ya hay ganas  (uoh)

El tutorial, visto por encima parece sencillo y sobre todo corto. Me ha sorprendido que no haya que hacer muchas virguerías para aplicar las físicas  (ole)  

Kaneda

 Vaya pues si que era verdad que lo hicisteis facil para usar (ole) . Si teneis algo de tiempò  :ph34r:  estaria cojonudo que explicaseis un poco como hicisteis lo de la lampara del video 3 (musseum), a mi personalmanete me gusto mucho

Haddd

 Claro, haré un tutorial avanzado sobre ello ... :P  

Haddd

 Ya lo tienes, me falta pulirlo y documentarlo un poco mejor... :P




       #region Creation

       void CreateScene()
       {
           // Cargamos el eslabón. Necesitamos escalarlo al cargar
           float scale = 1f / 50f;

           HImport.Haddd.Load("link",scale);

           // Accedemos al 1er elemento de los objetos que se acaban de cargar
           linkMeshObject = HImport.Haddd.MeshObjects[0];

           // Construimos la cadena de la izquierda
           BuildChain(new Vector3(0,4,0),ref leftChain,"l");

           // Construimos la cadena de la derecha
           BuildChain(new Vector3(1, 4, 0), ref rightChain, "r");

           // El eslabon que se carga, lo ocultamos puesto que ya tenemos
           // las cadenas creadas y este nos sobra
           linkMeshObject.Hide = true;

           // Creamos la lámpara
           CreateLamp();

       }

       void BuildChain(Vector3 pos,ref chain chain,string prefix)
       {
           HRigidBody parent = null;

           float linkHeight = linkMeshObject.BoundingBox.Height;

           HCollision colision = new HCollision();

           // Creamos un objeto de colision del mismo tamaño que el eslabon
           colision.CreateSphere(linkMeshObject.BoundingBox.Width / 2f, linkHeight / 2f, linkMeshObject.BoundingBox.Depth / 2f);

           float rotacionY = 0;

           for (int i = 0; i < 10; i++)
           {
               // Clonamos el eslabón original, dándole un nuevo nombre
               HMeshObject mesh = linkMeshObject.Clone("link" + prefix + i.ToString());

               // asignamos la posición al nuevo meshobject
               mesh.Position = pos;

               // Creamos un RigidBody
               HRigidBody body = Haddd.Physics.RigidBodies.Create(colision, mesh);

               // Le damos una masa y la inercia
               body.SetMassMatrix(5f, 1f, 3f, 0.5f);

               // Creamos la matriz del body
               Matrix m = Matrix.RotationY(rotacionY) * Matrix.Translation(pos);

               body.SetMatrix(ref m);

               // Ahora crearemos un joint de tipo Ball and Socket
               HBallJoint joint;
               
               joint = new HBallJoint(
                   pos + new Vector3(0, linkHeight / 2, 0),  // Indicamos la posición donde se establece el punto de contacto EN COORDS MUNDO
                   new Vector3(0, 1f, 0),  // El eje de rotación
                   Geometry.DegreeToRadian(0), // Permitimos la rotación absoluta
                   0,      // Permitimos la rotación absoluta
                   body,   // El rigidBody que se asocia a este joint
                   parent  // el padre del que depende este joint
                   );

               // En el próximo joint, este será el padre
               parent = body;

               // bajamos un poco el eslabon pero no completamente para que así de la impresión
               // de que está unido

               pos.Y -= linkHeight * 0.8f;

               // Los eslabones van alternando su rotación para que parezca una cadena
               if (rotacionY == 0)
                   rotacionY = Geometry.DegreeToRadian(90f);
               else
                   rotacionY = 0f;

               // Guardamos los valores para después poder crear los joints de
               // la lámpara

               chain.linkBody = body;

               chain.finalPos = pos + new Vector3(0, linkHeight / 2, 0);

           }

           colision.Release();
       }

       void CreateLamp()
       {

           // Ahora crearé un cubo que nos hará de lámpara

           Vector3 size = new Vector3(2f, 0.4f, 1f);

           HMesh mesh = HMesh.CreateCube24Vertices(size);

           HMeshObject lamp = Haddd.Scene.MeshObjects.Create("lamp", false);

           // Ahora asociamos el mesh con el meshobject

           lamp.Mesh = mesh;

           // Creamos un array de materiales de 1 elemento.
           HMaterial[] material = new HMaterial[1];

           material[0] = new HMaterial();

           // Creamos la capa
           HMaterialLayer layer = new HMaterialLayer();

           // Lo añadimos al material
           material[0].AddLayer(layer);

           layer.DiffuseMap.Texture = Haddd.Video.Textures.Create2D("Pared_Difusse", true);

           // Asignamos a nuestros HMeshObject el materal que acabamos de crear
           lamp.Material = material;

           // En Newton, primero debemos definir un objeto "colisionable" dando la
           // forma de este objeto.

           HCollision collision=new HCollision();

           // Tendrá el mismo tamaño que el utilizado al crear el mesh
           // PUESTO QUE EN NEWTON, LA UNIDAD ES EL METRO, IGUAL QUE EN HADDD

           collision.CreateBox(ref size);

           // Ahora creamos un Rigibd body que son los objetos que tienen
           // asociado el tipo de colision y todas las propiedades físicas
           // Asociamos el objeto collision creado y el HMeshObject
           HRigidBody body = Haddd.Physics.RigidBodies.Create(collision, lamp);

           // Indicamos la masa

           body.Mass=50f;

           // Necesitamos indicar una matriz. No utilizamos la misma que en
           // el meshobject porque podemos tener rigidbodies NO asociados a una
           // malla y por eso tenemos que asignar la matriz en lugar de utilizar
           // la del propio HMeshObject

           Vector3 pos = leftChain.finalPos;
           pos.X=(rightChain.finalPos.X+leftChain.finalPos.X)/2f;
           pos.Y -= linkMeshObject.BoundingBox.Height / 2f;

           Matrix matrix = Matrix.Translation(pos);

           body.SetMatrix(ref matrix);

           // Ya no utilizamos más la colision así que la liberamos
           collision.Release();

           lampBody = body;

           // añadimos los joints
           HBallJoint joint = new HBallJoint(leftChain.finalPos, new Vector3(0, 1f, 0), 0, 0, body, leftChain.linkBody);
           HBallJoint joint2 = new HBallJoint(rightChain.finalPos, new Vector3(0, 1f, 0), 0, 0, body, rightChain.linkBody);


       }

       #endregion

zupervaca

 pregunta tonta: ¿donde actualizais los objetos segun los datos que os da newton?

Haddd

 En la clase Physics. De eso se encarga el motor, para que no tengas que hacerlo tu ;)  

zupervaca

 pregunta tonta 2: ¿hay la posibilidad de evitar que la clase Physics lo haga y lo hagamos nosotros? me refiero a obtener los valores de newton y adaptar el objeto segun queramos nosotros

Haddd

 claro...simplemente no uses la física de Haddd y hz tu tus propias clases de físcia en el motor.

arisdg

 Tiene, muy buena pinta. Como han dicho los demas, yo tambien quiero probar vuestro motor...

(ole)  






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.