tag:blogger.com,1999:blog-4645133923358276912024-03-04T23:47:47.621-08:00El despacho de los jorgesJorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.comBlogger72125tag:blogger.com,1999:blog-464513392335827691.post-5154766644856833212011-04-24T02:47:00.000-07:002011-04-24T03:36:39.374-07:00OpenGL-ES2.0 y Monotouch IV (Cube3D parte2)En esta segunda parte veremos dos técnicas que mejorarán el ejemplo anterior. La primera será crear una estructura para todas las propiedades de nuestros vértices. Si nos damos cuenta de cada vértice cada vez vamos a necesitar más información, empezamos por la posición, el color, luego añadimos las coordenadas de textura y para técnicas de iluminación o normal map necesitaremos la normal, la binomial y la tangente por lo que tendríamos que tener un motón de arrays que pasar al VertexShader. <br /><br />La forma más elegante de resolver esto es usar una estructura básica para nuestros vértices. Para nuestro ejemplo en principio sólo necesitamos conocer la posición y las coordenadas de textura por lo que nuestra estructura será:<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>struct VertexType{<br /> public Vector3 position;<br /> public Vector2 textureCoordinates;<br />}<br /></code></pre><br /><br />Una vez tenemos nuestra estructura vamos a definir nuestro cubo. En la primera parte de este tutorial usamos para dibujar la función GL.DrawArrays de OpenGL lo cual nos provocaba tener que especificarle los 3 vértices de cada triángulo y teníamos muchos vértices repetidos en el array de vértices que le pasábamos a nuestro VertexShader. <br /><br />Esto se soluciona usando el método de dibujado <span style="font-weight:bold;">GL.DrawElements</span> ya que nos permite especificarle los triangulos a través de un array de índices. De esta forma nosotros ahora sólo tenemos que especificar los 8 vértices que tiene nuestro cubo y luego pasar el array de índices que cuenta con combinaciones de estos vértices.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEds8-sXZwJqavx40rMlHvVT3OcOjD-5Oqz9Vq5VNSNDJnzin-vjHs3Sp_uI-LlBORTZvrn26aUqsxQ5mv9XEstWDZCsrQGlFWWEZzWWzDRertecaHivPJsKLtLXXnvUiR4WVmENnyajI/"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 161px; height: 155px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEds8-sXZwJqavx40rMlHvVT3OcOjD-5Oqz9Vq5VNSNDJnzin-vjHs3Sp_uI-LlBORTZvrn26aUqsxQ5mv9XEstWDZCsrQGlFWWEZzWWzDRertecaHivPJsKLtLXXnvUiR4WVmENnyajI/" border="0" alt="" /></a><br /><br />por lo que especificaremos los 8 vértices:<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>vertexArray = new VertexType[8];<br />vertexArray[0].position = new Vector3(-0.5f,-0.5f,0.0f);<br />vertexArray[0].textureCoordinates = new Vector2(0,1);<br />vertexArray[1].position = new Vector3(0.5f,-0.5f,0.0f);<br />vertexArray[1].textureCoordinates = new Vector2(1,1);<br />vertexArray[2].position = new Vector3(0.5f,0.5f,0.0f);<br />vertexArray[2].textureCoordinates = new Vector2(1,0);<br />vertexArray[3].position = new Vector3(-0.5f,0.5f,0.0f);<br />vertexArray[3].textureCoordinates = new Vector2(0,0);<br />vertexArray[4].position = new Vector3(-0.5f,-0.5f,1.0f);<br />vertexArray[4].textureCoordinates = new Vector2(0,1);<br />vertexArray[5].position = new Vector3(0.5f,-0.5f,1.0f);<br />vertexArray[5].textureCoordinates = new Vector2(1,1);<br />vertexArray[6].position = new Vector3(0.5f,0.5f,1.0f);<br />vertexArray[6].textureCoordinates = new Vector2(1,0);<br />vertexArray[7].position = new Vector3(-0.5f,0.5f,1.0f);<br />vertexArray[7].textureCoordinates = new Vector2(0,0);<br /></code></pre><br /><br />y ahora vamos a especificar los índices o lo que es lo mismo como combinar dichos 8 vértices para construir los triángulos de nuestro cubo.<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>ushort[] indexes = {0,1,2,0,2,3,<br /> 4,5,1,4,1,0,<br /> 4,0,3,4,3,7,<br /> 1,5,6,1,6,2,<br /> 6,7,3,6,3,2,<br /> 5,4,7,5,7,6};<br /></code></pre><br /><br /><span style="font-style:italic;">(Nota. Como podeís observar en el array de índices, cada fila sería una cara de nuestro cubo. Ej. la cara frontal estaría compuesta por 2 triángulos formados por los vértices 0-1-2 y 0-2-3)</span><br /><br />Bien pues ahora a nuestra función de dibujado GL.DrawElements le pasamos:<br /><br /><span style="font-weight:bold;">GL.DrawElements(All mode, int count, All type, IntPtr indices)</span><br />- mode puede valer (All.Points/All.Lines/All.LineStrip,/All.LineLoop/ All.Triangles/ All.TriangleStrip / All.TriangleFan)<br />- count es el número total de índices que vamos a pasarle.<br />- type especifica el tipo de dato que alberga el array de índices, (All.UnsignedShort para nuestro ejemplo)<br />- indices que será el array de índices.<br /><br />Una cosa importante en este punto es detectar que le tenemos que enviar un puntero a nuestra estructura y no nuestra propia estructura para ellos haremos uso de la clase <span style="font-weight:bold;">GCHandle</span>.<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>GCHandle indexesHandle = GCHandle.Alloc(indexes, GCHandleType.Pinned);<br /></code></pre><br /><br />De esta forma tenemos un puntero a nuestro array de índices y para llamar al GL.DrawElements haremos:<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>GL.DrawElements(All.Triangles, indexes.Length, All.UnsignedShort, indexesHandle.AddrOfPinnedObject());<br /></code></pre><br /><br />Bien pues una cosa similar vamos a hacer con nuestra estructura de vértices para poder pasarsela a nuestro VertexShader, lo primero creamos un puntero a nuestra estructura.<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>GCHandle vertexArrayHandle = GCHandle.Alloc(vertexArray, GCHandleType.Pinned);<br /></code></pre><br /><br />Y para poder inyectar los vértices en el VertexShader:<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>int size = sizeof(float)*3+sizeof(float)*2;<br />GL.VertexAttribPointer(attributePosition,3,All.Float,false,size,vertexArrayHandle.AddrOfPinnedObject());<br />GL.VertexAttribPointer(attributeTexCoord,2,All.Float,false,size,(IntPtr)((uint)vertexArrayHandle.AddrOfPinnedObject()+(uint)(sizeof(float)*3)));<br /></code></pre><br /><br />Donde size es la variable que calcula el tamaño de nuestra estructura compuesta por un Vector3 y un Vector2 de ahí el sizeof(float)*3 + sizeof(float)*2 que nos será útil pasar al VertexAttribPointer para que conozca la unidad básica o stride que es como se conoce el parámetro. También añadir que para las coordenadas de texturas hemos tenido que sumar un offset a nuestro puntero que salte el Vector3 de la posición de los vértices y empiece siempre con las coordenadas de textura. Si tuvieramos más atributos para nuestros vértices tendríamos que ir realizando diferentes offset.<br /><br />Bueno pues con la creación de estructuras y la utilización del método de pintado GL.DrawElements el código final de nuestra clase EAGLView.cs nos queda:<br /><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>#define OPENGLES2<br /><br /><br />using System;<br />using OpenTK.Platform.iPhoneOS;<br />using MonoTouch.CoreAnimation;<br />using OpenTK;<br /> <br />using OpenTK.Graphics.ES20;<br /><br />using MonoTouch.Foundation;<br />using MonoTouch.ObjCRuntime;<br />using MonoTouch.OpenGLES;<br />using System.Text;<br />using System.Drawing;<br />using OpenTK.Platform;<br />using MonoTouch.CoreGraphics;<br />using MonoTouch.UIKit;<br />using System.Runtime.InteropServices;<br /><br />namespace Cube3D2<br />{<br /> public partial class EAGLView : iPhoneOSGameView<br /> {<br /> int viewportWidth, viewportHeight;<br /> int program;<br /> <br /> struct VertexType{<br /> public Vector3 position;<br /> public Vector2 textureCoordinates;<br /> }<br /> <br /> VertexType[] vertexArray;<br /> GCHandle vertexArrayHandle;<br /> <br /> ushort[] indexes = {0,1,2,0,2,3,<br /> 4,5,1,4,1,0,<br /> 4,0,3,4,3,7,<br /> 1,5,6,1,6,2,<br /> 6,7,3,6,3,2,<br /> 5,4,7,5,7,6};<br /> GCHandle indexesHandle;<br /> <br /> //Texture<br /> int textureId;<br /> <br /> IntPtr data;<br /> <br /> Matrix4 matWorldViewProjection, matProjection, matView, matWorld;<br /> int uniformMat, uniformSampler;<br /> int attributePosition = 0;<br /> int attributeTexCoord = 1;<br /><br /> [Export("layerClass")]<br /> static Class LayerClass ()<br /> {<br /> return iPhoneOSGameView.GetLayerClass ();<br /> }<br /><br /> [Export("initWithCoder:")]<br /> public EAGLView (NSCoder coder) : base(coder)<br /> {<br /> LayerRetainsBacking = false;<br /> LayerColorFormat = EAGLColorFormat.RGBA8;<br /> }<br /> <br /> protected override void CreateFrameBuffer ()<br /> {<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;<br /> base.CreateFrameBuffer();<br /> Initialize();<br /> } <br /> <br /> private bool Initialize()<br /> {<br /> viewportHeight = Size.Height; <br /> viewportWidth = Size.Width;<br /> <br /> //Structure<br /> vertexArray = new VertexType[8];<br /> vertexArray[0].position = new Vector3(-0.5f,-0.5f,0.0f);<br /> vertexArray[0].textureCoordinates = new Vector2(0,1);<br /> vertexArray[1].position = new Vector3(0.5f,-0.5f,0.0f);<br /> vertexArray[1].textureCoordinates = new Vector2(1,1);<br /> vertexArray[2].position = new Vector3(0.5f,0.5f,0.0f);<br /> vertexArray[2].textureCoordinates = new Vector2(1,0);<br /> vertexArray[3].position = new Vector3(-0.5f,0.5f,0.0f);<br /> vertexArray[3].textureCoordinates = new Vector2(0,0);<br /> vertexArray[4].position = new Vector3(-0.5f,-0.5f,1.0f);<br /> vertexArray[4].textureCoordinates = new Vector2(0,1);<br /> vertexArray[5].position = new Vector3(0.5f,-0.5f,1.0f);<br /> vertexArray[5].textureCoordinates = new Vector2(1,1);<br /> vertexArray[6].position = new Vector3(0.5f,0.5f,1.0f);<br /> vertexArray[6].textureCoordinates = new Vector2(1,0);<br /> vertexArray[7].position = new Vector3(-0.5f,0.5f,1.0f);<br /> vertexArray[7].textureCoordinates = new Vector2(0,0);<br /> vertexArrayHandle = GCHandle.Alloc(vertexArray, GCHandleType.Pinned);<br /> <br /> indexesHandle = GCHandle.Alloc(indexes, GCHandleType.Pinned);<br /><br /> // Vertex and fragment shaders<br /> string vertexShaderSrc = @"uniform mat4 uMVPMatrix;<br /> attribute vec4 aPosition; <br /> attribute vec2 aTexCoord;<br /> varying vec2 vTexCoord;<br /> void main() <br /> { <br /> vTexCoord = aTexCoord;<br /> gl_Position = uMVPMatrix * aPosition; <br /> }"; <br /> <br /> string fragmentShaderSrc = @"precision mediump float;<br /> varying vec2 vTexCoord;<br /> uniform sampler2D sTexture;<br /> void main() <br /> { <br /> //gl_FragColor = vec4(1.0,0.0,0.0,1.0);<br /> gl_FragColor = texture2D(sTexture, vTexCoord);<br /> }";<br /><br /> int vertexShader = LoadShader (All.VertexShader, vertexShaderSrc );<br /> int fragmentShader = LoadShader (All.FragmentShader, fragmentShaderSrc );<br /> program = GL.CreateProgram();<br /> if (program == 0)<br /> throw new InvalidOperationException ("Unable to create program");<br /><br /> GL.AttachShader (program, vertexShader);<br /> GL.AttachShader (program, fragmentShader);<br /> <br /> //Set position<br /> GL.BindAttribLocation (program, attributePosition, "aPosition");<br /> GL.BindAttribLocation (program, attributeTexCoord, "aTexCoord");<br /> <br /> <br /> GL.LinkProgram (program);<br /><br /> int linked = 0;<br /> GL.GetProgram (program, All.LinkStatus, ref linked);<br /> if (linked == 0) {<br /> // link failed<br /> int length = 0;<br /> GL.GetProgram (program, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetProgramInfoLog (program, length, ref length, log);<br /> Console.WriteLine ("GL2", "Couldn't link program: " + log.ToString ());<br /> return false;<br /> }<br /><br /> GL.DeleteProgram (program);<br /> throw new InvalidOperationException ("Unable to link program");<br /> }<br /> <br /> //View<br /> matWorld = Matrix4.Identity;<br /> float aspectRatio = (float) (Size.Width) / (float)(Size.Height);<br /> matProjection = Matrix4.CreatePerspectiveFieldOfView(((float)(Math.PI) / 180.0f)*45.0f, aspectRatio, 1.0f,20.0f);<br /> matView = Matrix4.CreateRotationX(0.5f)*Matrix4.CreateTranslation(0,0,-8);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /> <br /> GetUniformVariables();<br /> <br /> LoadTexture();<br /> <br /> return true;<br /> }<br /> <br /> private int LoadShader ( All type, string source )<br /> {<br /> int shader = GL.CreateShader(type);<br /><br /> if ( shader == 0 )<br /> throw new InvalidOperationException("Unable to create shader"); <br /> <br /> // Load the shader source<br /> int length = 0;<br /> GL.ShaderSource(shader, 1, new string[] {source}, (int[])null);<br /> <br /> // Compile the shader<br /> GL.CompileShader( shader );<br /> <br /> int compiled = 0;<br /> GL.GetShader (shader, All.CompileStatus, ref compiled);<br /> if (compiled == 0) {<br /> length = 0;<br /> GL.GetShader (shader, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetShaderInfoLog (shader, length, ref length, log);<br /> Console.WriteLine("GL2", "Couldn't compile shader: " + log.ToString ());<br /> }<br /><br /> GL.DeleteShader (shader);<br /> throw new InvalidOperationException ("Unable to compile shader of type : " + type.ToString ());<br /> }<br /><br /> return shader;<br /> <br /> }<br /> <br /> private void LoadTexture()<br /> {<br /> <br /> UIImage ui = UIImage.FromFile("mario3.jpg");<br /> CGImage image = ui.CGImage;<br /> <br /> CGColorSpace colorspace = CGColorSpace.CreateDeviceRGB();<br /> data = Marshal.AllocHGlobal(image.Height*image.Width*4);<br /> CGContext context = new CGBitmapContext(data,image.Width,image.Height,8,4*image.Width, colorspace,CGImageAlphaInfo.NoneSkipLast);<br /> colorspace.Dispose();<br /> context.ClearRect(new RectangleF(0,0,image.Width,image.Height));<br /> context.DrawImage(new RectangleF(0,0,image.Width,image.Height),image);<br /> <br /> //Generate a texture object<br /> GL.GenTextures(1,ref textureId);<br /> <br /> //Bind the texture object<br /> GL.ActiveTexture(All.Texture0);<br /> GL.BindTexture(All.Texture2D, textureId);<br /> GL.Uniform1(textureId,uniformSampler);<br /> <br /> //Load the texture<br /> GL.TexImage2D(All.Texture2D, 0, (int)All.Rgba, image.Width, image.Height, 0 ,All.Rgba, All.UnsignedByte, data);<br /> <br /> //Set the filtering mode<br /> GL.TexParameter(All.Texture2D, All.TextureMinFilter, (float)All.Nearest);<br /> GL.TexParameter(All.Texture2D, All.TextureMagFilter, (float)All.Nearest);<br /> <br /> //Wrap setting<br /> GL.TexParameter(All.Texture2D, All.TextureWrapS,(int)All.Repeat);<br /> GL.TexParameter(All.Texture2D, All.TextureWrapT,(int)All.Repeat);<br /> <br /> context.Dispose();<br /> Marshal.FreeHGlobal(data);<br /> }<br /><br /> protected override void ConfigureLayer (CAEAGLLayer eaglLayer)<br /> {<br /> eaglLayer.Opaque = true;<br /> } <br /> <br /> private void GetUniformVariables()<br /> {<br /> uniformMat = GL.GetUniformLocation(program, "uMVPMatrix");<br /> uniformSampler = GL.GetUniformLocation(program, "sTexture");<br /> }<br /> <br /> private void SetUniformMatrix4(int location, bool transpose, ref Matrix4 matrix)<br /> {<br /> unsafe<br /> {<br /> fixed (float* matrix_ptr = &matrix.Row0.X)<br /> {<br /> GL.UniformMatrix4(location,1,transpose,matrix_ptr);<br /> }<br /> }<br /> }<br /> <br /> float rotateY = 0;<br /> <br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame (e);<br /> <br /> MakeCurrent();<br /><br /> GL.ClearColor (0.7f, 0.7f, 0.7f, 1);<br /> GL.Clear ((int)All.ColorBufferBit);<br /> <br /> GL.FrontFace(All.Ccw);<br /> GL.CullFace(All.Front);<br /> GL.Enable(All.CullFace);<br /> <br /><br /> GL.Viewport (0, 0, viewportWidth, viewportHeight);<br /> GL.UseProgram (program);<br /> <br /> matWorld = Matrix4.CreateRotationY((float)e.Time + rotateY) * Matrix4.CreateTranslation(Vector3.Zero);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /> <br /> SetUniformMatrix4(uniformMat,false,ref matWorldViewProjection);<br /> <br /> //Active params<br /> GL.EnableVertexAttribArray (attributePosition);<br /> GL.EnableVertexAttribArray (attributeTexCoord);<br /> <br /> //Set params<br /> int size = sizeof(float)*3+sizeof(float)*2;<br /> GL.VertexAttribPointer(attributePosition,3,All.Float,false,size,vertexArrayHandle.AddrOfPinnedObject());<br /> GL.VertexAttribPointer(attributeTexCoord,2,All.Float,false,size,(IntPtr)((uint)vertexArrayHandle.AddrOfPinnedObject()+(uint)(sizeof(float)*3)));<br /><br /> //GL.DrawArrays (All.Triangles, 0, vertexArray);<br /> GL.DrawElements(All.Triangles, indexes.Length, All.UnsignedShort, indexesHandle.AddrOfPinnedObject());<br /> <br /> rotateY += (float)Math.PI / 270;<br /> <br /> //Swapped buffer (Front and Back buffer)<br /> SwapBuffers ();<br /> }<br /> <br /> protected override void OnDisposed (EventArgs e)<br /> {<br /> base.OnDisposed (e);<br /> Marshal.FreeHGlobal (data);<br /> if (textureId != 0)<br /> GL.DeleteTextures(1, ref textureId);<br /> }<br /> <br /> }<br />}<br /></code></pre>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-1576498857665692772011-04-23T07:40:00.001-07:002011-04-23T09:57:56.178-07:00OpenGL-ES2.0 y Monotouch IV (Cube3D parte1)Siguiendo con el aprendizaje de OpenGLES2.0 hoy voy a escribir un tutorial de como crear un cubo en 3D y texturizarlo. Esta tutorial es muy facil de entender si habéis leido los anteriores.<br /><br />Lo primero que necesitamos es definir nuestro cubo 3D, para ellos tenemos que entender como OpenGLES2.0 pinta objetos o mejor dicho triangulos en pantalla. OpenGLES2.0 tiene dos métodos para pintar:<br /><br /><span style="font-style:italic;">GL.DrawArrays</span><br /><span style="font-style:italic;">GL.DrawElements</span><br /><br />En los tutoriales anteriores hemos usado siempre DrawArrays pero a la hora de pintar un objeto más complejo que un quad es mucho más interesante usar DrawElements. De todas formas en esta primera parte vamos a usar el método DrawArrays y ya en la segunda parte veremos las ventajas que nos plantea usar DrawElements.<br /><br />Bueno como ya sabéis el método DrawArrays tiene la siguiente declaración:<br /><br /><span style="font-weight:bold;">GL.DrawArrays(All mode, int first, int count);</span><br /><br /> <span style="font-style:italic;">- mode -> (All.Points/All.Lines/All.LineStrip,/All.LineLoop/ All.Triangles/ All.TriangleStrip / All.TriangleFan)<br /> - first -> Indica el índice del primer vertices a pintar<br /> - count -> Indica el número total de vertices a pintar</span><br /><br />Por lo tanto para pintar nuestro cubo necesitaremos descomponerlo en triangulos. Un cubo tiene 6 caras y cada cara se puede descomponer en 2 triangulos que son 3 vértices, por lo que tendremos un total de 36 vertices.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirRCIs6ARPaays-n844rXMDazdKG0Az6aTjTgce0inn_bOhuke_w0B0o9yb2LCGZYdjsxXURrFjCVu5bOFODmXJcxWpsSeMuRcfntWIPSNggKpFdNzySRoDg2xxURp3_y1vSdNrrD7bWw/s1600/cubo.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 161px; height: 155px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirRCIs6ARPaays-n844rXMDazdKG0Az6aTjTgce0inn_bOhuke_w0B0o9yb2LCGZYdjsxXURrFjCVu5bOFODmXJcxWpsSeMuRcfntWIPSNggKpFdNzySRoDg2xxURp3_y1vSdNrrD7bWw/s320/cubo.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598795890787606610" /></a><br /><br /><pre class="source-code"><code><br />float[] squareVertices = { //Front<br />    -0.5f,-0.5f,0.0f, //0<br />    0.5f,-0.5f,0.0f, //1<br />    0.5f,0.5f,0.0f, //2<br />    -0.5f,-0.5f,0.0f, //0<br />    0.5f,0.5f,0.0f, //2<br />    -0.5f,0.5f,0.0f, //3<br />   //Botton<br /> -0.5f,-0.5f,1.0f, //4  <br />            0.5f,-0.5f,1.0f, //5<br />        0.5f,-0.5f,0.0f, //1<br />    -0.5f,-0.5f,1.0f, //4<br />    0.5f,-0.5f,0.0f, //1<br /> -0.5f,-0.5f,0.0f, //0<br /> //Left<br />    -0.5f,-0.5f,1.0f, //4<br />        -0.5f,-0.5f,0.0f, //0<br />    -0.5f,0.5f,0.0f, //3<br />    -0.5f,-0.5f,1.0f, //4<br />    -0.5f,0.5f,0.0f, //3<br />    -0.5f,0.5f,1.0f, //7<br /> <br /> //Right<br />    0.5f,-0.5f,0.0f, //1<br />    0.5f,-0.5f,1.0f, //5<br />    0.5f,0.5f,1.0f, //6<br />    0.5f,-0.5f,0.0f, //1<br />    0.5f,0.5f,1.0f, //6<br />    0.5f,0.5f,0.0f, //2<br />    //Top<br /> 0.5f,0.5f,1.0f, //6<br /> -0.5f,0.5f,1.0f, //7<br /> -0.5f,0.5f,0.0f, //3<br /> 0.5f,0.5f,1.0f, //6<br /> -0.5f,0.5f,0.0f, //3<br />    0.5f,0.5f,0.0f, //2<br /><br /> //Back<br /> 0.5f,-0.5f,1.0f, //5<br /> -0.5f,-0.5f,1.0f, //4<br /> -0.5f,0.5f,1.0f, //7 <br /> 0.5f,-0.5f,1.0f, //5<br /> -0.5f,0.5f,1.0f, //7<br /> 0.5f,0.5f,1.0f //6    <br /> };<br /></code></pre><br /><br />Es importante el orden en el que especificáis los vértices ya que indican hacia donde apunta la normal del triangulo o lo que es lo mismo desde donde se va a ver el triangulo. Yo uso el sentido antihorario que es el estandar también llamado en física "la regla de la mano derecha" donde siguiendo el sentido en el que los dedos de la mano derecha rodearía en triangulo el pulgar sería el vector normal del triangulo.<br /><br />De la misma forma necesitamos también especificar las coordenadas de textura para cada vértice por lo tanto tendremos que construir el array de coordenadas de texturas:<br /><br /><pre class="source-code"><code><br />flfloat[] texCoords = new float[] { 0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f,<br /> <br />   0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f,<br /> <br />   0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f,<br /> <br />   0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f,<br /> <br />   0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f,<br /> <br />   0.0f,1.0f,<br />   1.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,1.0f,<br />   1.0f,0.0f,<br />   0.0f,0.0f<br /> };<br /></code></pre><br /><br /><span style="font-style:italic;">(Nota. Seguro que ya empezáis a notar que debe haber una forma más cómoda y menos repetitiva de definir un cubo, bien lo veremos en la segunda parte)</span><br /><br />Bien ahora una de las cosas que vamos a añadirle a este tutorial es que el cubo este girando, el motivo es que si no, no podríamos verlo desde todas sus caras y no podríamos comprobar si está correctamente construido y todas sus caras están correctamente texturizadas.<br /><br />Para ello vamos a definir las típicas 3 matrices (World / View / Projection):<br /><br /><pre class="source-code"><code><br /> matWorld = Matrix4.Identity;<br /> float aspectRatio = (float) (Size.Width) / (float)(Size.Height);<br /> matProjection = Matrix4.CreatePerspectiveFieldOfView(((float)(Math.PI) / 180.0f)*45.0f, aspectRatio, 1.0f,20.0f);<br /> matView = Matrix4.CreateRotationX(0.5f)*Matrix4.CreateTranslation(0,0,-8);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /></code></pre><br /><br />Bien pues modificando la matrix del mundo podremos hacer que nuestro cubo 3d esté rotando.<br /><br /><pre class="source-code"><code><br />float rotateY = 0;<br /> <br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br />                        ...<br /> <br /> matWorld = Matrix4.CreateRotationY((float)e.Time + rotateY) * Matrix4.CreateTranslation(Vector3.Zero);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /></code></pre><br /><br /><span style="font-style:italic;">(Nota. Por motivos de eficiencia vamos a pasarle al VertexShader la multiplicación de las 3 matrices ya hecha de manera que <br />no sea necesario que el la haga por cada vertice. Por lo que no se puede olvidar modificar la matriz <span style="font-weight:bold;">matWorldViewProjection</span> cada vez que modifiquemos la matriz del mundo.)</span><br /><br />Nuestro VertexShader tendrá pues una variable de tipo <span style="font-weight:bold;">uniform</span> que albergará la matriz resultado de la multiplicación de las 3 matrices. Por lo que nuestro VertexShader será:<br /><br /><pre class="source-code"><code><br />string vertexShaderSrc =  @"uniform mat4 uMVPMatrix;<br /> attribute vec4 aPosition; <br /> attribute vec2 aTexCoord;<br /> varying vec2 vTexCoord;<br />     void main()                  <br />     {                         <br />    vTexCoord = aTexCoord;<br />        gl_Position = uMVPMatrix * aPosition; <br />     }"; <br /></code></pre><br /><br />Bien ahora el problema siguiente es setear el valor de la variable uniform del VertexShader. Para ello existen unas funciones dependiendo del tipo de estructura que quieras pasar llamadas <span style="font-weight:bold;">GL.UniformX</span> y concretamente para setear una variable de tipo matrix4x4 tenemos GL.UniformMatrix4. El problema es que dicha función no admite un objeto de tipo Matrix4 que es lo que tenemos sino que se le tenemos que pasar un puntero a nuestra matriz.<br /><br />Para ello yo me he creado el siguiente método:<br /><br /><pre class="source-code"><code><br />private void SetUniformMatrix4(int location, bool transpose, ref Matrix4 matrix)<br /> {<br /> unsafe<br /> {<br /> fixed (float* matrix_ptr = &matrix.Row0.X)<br /> {<br /> GL.UniformMatrix4(location,1,transpose,matrix_ptr);<br /> }<br /> }<br /> }<br /></code></pre><br /><br /><span style="font-style:italic;">(Nota. Como podéis observar el método usa código no seguro "unsafe" por lo que para que esto es funcione deberéis activar la posibilidad de ejecutar código no seguro en vuestro IDE).</span><br /><br />Para terminar en nuestro método de dibujado deberemos especificar el sentido en el que vamos a dibujar los triángulos, a favor de las agujas del reloj o de forma antihoraria esto es conocido como el cullmode. En OpenGLES2.0 existen 3 funciones necesarias para especificar el cullmode:<br /><br /><span style="font-weight:bold;">GL.FrontFace(All type)</span> -> (Donde type puede ser All.Cw (horario) o All.Ccw (antihorario)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRMJUgVEYO1g_knSsvELhZOH6zqNHCynMdkd5EM8MvUH8FCT0mUIIH-4uTgW2ni0B5tsscgiAH4LZeKeRI38Pfgd_ka6q9mKf9i2AMZUCb9W5eRUQpswCTR_AuDks9fEveDjocIwP51gE/s1600/cullmode.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 133px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRMJUgVEYO1g_knSsvELhZOH6zqNHCynMdkd5EM8MvUH8FCT0mUIIH-4uTgW2ni0B5tsscgiAH4LZeKeRI38Pfgd_ka6q9mKf9i2AMZUCb9W5eRUQpswCTR_AuDks9fEveDjocIwP51gE/s320/cullmode.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598803024626217426" /></a><br /><br />Nosotros como explicamos anteriormente hemos especificado los triángulos de forma antihoraria por lo que usaremos All.Ccw.<br /><br /><span style="font-weight:bold;">GL.CullFace(All mode)</span> -> (Donde mode puede ser All.Front / All.Back / All.FrontAndBack)<br /><br />Donde indicamos cual de las caras del triangulo vamos a dibujar, la delantera, la trasera o las dos. El valor por defecto es las caras traseras por lo que nosotros setearemos esta a All.Front.<br /><br /><span style="font-weight:bold;">GL.Enable(All.CullFace)</span><br /><br />Y por último y para que todo esto tenga efecto activaremos el cullmode de la misma forma que la mayoría de parámetros en OpenGL, llamando al método GL.Enable.<br /><br />Como siempre os pongo el código completo de la clase <span style="font-weight:bold;">EAGLView.cs</span> de la plantilla de monotouch.<br /><br /><pre class="source-code"><code>Paste your text here.#define OPENGLES2<br /><br /><br />using System;<br />using OpenTK.Platform.iPhoneOS;<br />using MonoTouch.CoreAnimation;<br />using OpenTK;<br /> <br />using OpenTK.Graphics.ES20;<br /><br />using MonoTouch.Foundation;<br />using MonoTouch.ObjCRuntime;<br />using MonoTouch.OpenGLES;<br />using System.Text;<br />using System.Drawing;<br />using OpenTK.Platform;<br />using MonoTouch.CoreGraphics;<br />using MonoTouch.UIKit;<br />using System.Runtime.InteropServices;<br /><br />namespace Cube3D<br />{<br /> public partial class EAGLView : iPhoneOSGameView<br /> {<br /> int viewportWidth, viewportHeight;<br /> int program;<br /><br /> float[] squareVertices = { //Front<br /> -0.5f,-0.5f,0.0f, //0<br /> 0.5f,-0.5f,0.0f, //1<br /> 0.5f,0.5f,0.0f, //2<br /> -0.5f,-0.5f,0.0f, //0<br /> 0.5f,0.5f,0.0f, //2<br /> -0.5f,0.5f,0.0f, //3<br /> //Botton<br /> -0.5f,-0.5f,1.0f, //4 <br /> 0.5f,-0.5f,1.0f, //5<br /> 0.5f,-0.5f,0.0f, //1<br /> -0.5f,-0.5f,1.0f, //4<br /> 0.5f,-0.5f,0.0f, //1<br /> -0.5f,-0.5f,0.0f, //0<br /> //Left<br /> -0.5f,-0.5f,1.0f, //4<br /> -0.5f,-0.5f,0.0f, //0<br /> -0.5f,0.5f,0.0f, //3<br /> -0.5f,-0.5f,1.0f, //4<br /> -0.5f,0.5f,0.0f, //3<br /> -0.5f,0.5f,1.0f, //7<br /> <br /> //Right<br /> 0.5f,-0.5f,0.0f, //1<br /> 0.5f,-0.5f,1.0f, //5<br /> 0.5f,0.5f,1.0f, //6<br /> 0.5f,-0.5f,0.0f, //1<br /> 0.5f,0.5f,1.0f, //6<br /> 0.5f,0.5f,0.0f, //2<br /> //Top<br /> 0.5f,0.5f,1.0f, //6<br /> -0.5f,0.5f,1.0f, //7<br /> -0.5f,0.5f,0.0f, //3<br /> 0.5f,0.5f,1.0f, //6<br /> -0.5f,0.5f,0.0f, //3<br /> 0.5f,0.5f,0.0f, //2<br /><br /> //Back<br /> 0.5f,-0.5f,1.0f, //5<br /> -0.5f,-0.5f,1.0f, //4<br /> -0.5f,0.5f,1.0f, //7 <br /> 0.5f,-0.5f,1.0f, //5<br /> -0.5f,0.5f,1.0f, //7<br /> 0.5f,0.5f,1.0f //6 <br /> };<br />// int[] indices = { 0,1,2,0,2,3,<br />// 0,3,4,0,4,5,<br />// 0,5,6,0,6,1,<br />// 7,6,1,7,1,2,<br />// 7,4,5,7,5,6,<br />// 7,2,3,7,3,4<br />// };<br /> <br /> //Texture<br /> int textureId;<br /> float[] texCoords = new float[] { 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f,<br /> <br /> 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f,<br /> <br /> 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f,<br /> <br /> 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f,<br /> <br /> 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f,<br /> <br /> 0.0f,1.0f,<br /> 1.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,1.0f,<br /> 1.0f,0.0f,<br /> 0.0f,0.0f<br /> };<br /> <br /> IntPtr data;<br /> <br /> Matrix4 matWorldViewProjection, matProjection, matView, matWorld;<br /> int uniformMat, uniformSampler;<br /> int attributePosition = 0;<br /> int attributeTexCoord = 1;<br /><br /> [Export("layerClass")]<br /> static Class LayerClass ()<br /> {<br /> return iPhoneOSGameView.GetLayerClass ();<br /> }<br /><br /> [Export("initWithCoder:")]<br /> public EAGLView (NSCoder coder) : base(coder)<br /> {<br /> LayerRetainsBacking = false;<br /> LayerColorFormat = EAGLColorFormat.RGBA8;<br /> }<br /> <br /> protected override void CreateFrameBuffer ()<br /> {<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;<br /> base.CreateFrameBuffer();<br /> Initialize();<br /> } <br /> <br /> private bool Initialize()<br /> {<br /> viewportHeight = Size.Height; <br /> viewportWidth = Size.Width;<br /><br /> // Vertex and fragment shaders<br /> string vertexShaderSrc = @"uniform mat4 uMVPMatrix;<br /> attribute vec4 aPosition; <br /> attribute vec2 aTexCoord;<br /> varying vec2 vTexCoord;<br /> void main() <br /> { <br /> vTexCoord = aTexCoord;<br /> gl_Position = uMVPMatrix * aPosition; <br /> }"; <br /> <br /> string fragmentShaderSrc = @"precision mediump float;<br /> varying vec2 vTexCoord;<br /> uniform sampler2D sTexture;<br /> void main() <br /> { <br /> //gl_FragColor = vec4(1.0,0.0,0.0,1.0);<br /> gl_FragColor = texture2D(sTexture, vTexCoord);<br /> }";<br /><br /> int vertexShader = LoadShader (All.VertexShader, vertexShaderSrc );<br /> int fragmentShader = LoadShader (All.FragmentShader, fragmentShaderSrc );<br /> program = GL.CreateProgram();<br /> if (program == 0)<br /> throw new InvalidOperationException ("Unable to create program");<br /><br /> GL.AttachShader (program, vertexShader);<br /> GL.AttachShader (program, fragmentShader);<br /> <br /> //Set position<br /> GL.BindAttribLocation (program, attributePosition, "aPosition");<br /> GL.BindAttribLocation (program, attributeTexCoord, "aTexCoord");<br /> <br /> <br /> GL.LinkProgram (program);<br /><br /> int linked = 0;<br /> GL.GetProgram (program, All.LinkStatus, ref linked);<br /> if (linked == 0) {<br /> // link failed<br /> int length = 0;<br /> GL.GetProgram (program, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetProgramInfoLog (program, length, ref length, log);<br /> Console.WriteLine ("GL2", "Couldn't link program: " + log.ToString ());<br /> return false;<br /> }<br /><br /> GL.DeleteProgram (program);<br /> throw new InvalidOperationException ("Unable to link program");<br /> }<br /> <br /> //View<br /> matWorld = Matrix4.Identity;<br /> float aspectRatio = (float) (Size.Width) / (float)(Size.Height);<br /> matProjection = Matrix4.CreatePerspectiveFieldOfView(((float)(Math.PI) / 180.0f)*45.0f, aspectRatio, 1.0f,20.0f);<br /> matView = Matrix4.CreateRotationX(0.5f)*Matrix4.CreateTranslation(0,0,-8);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /> <br /> GetUniformVariables();<br /> <br /> LoadTexture();<br /> <br /> return true;<br /> }<br /> <br /> private int LoadShader ( All type, string source )<br /> {<br /> int shader = GL.CreateShader(type);<br /><br /> if ( shader == 0 )<br /> throw new InvalidOperationException("Unable to create shader"); <br /> <br /> // Load the shader source<br /> int length = 0;<br /> GL.ShaderSource(shader, 1, new string[] {source}, (int[])null);<br /> <br /> // Compile the shader<br /> GL.CompileShader( shader );<br /> <br /> int compiled = 0;<br /> GL.GetShader (shader, All.CompileStatus, ref compiled);<br /> if (compiled == 0) {<br /> length = 0;<br /> GL.GetShader (shader, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetShaderInfoLog (shader, length, ref length, log);<br /> Console.WriteLine("GL2", "Couldn't compile shader: " + log.ToString ());<br /> }<br /><br /> GL.DeleteShader (shader);<br /> throw new InvalidOperationException ("Unable to compile shader of type : " + type.ToString ());<br /> }<br /><br /> return shader;<br /> <br /> }<br /> <br /> private void LoadTexture()<br /> {<br /> <br /> UIImage ui = UIImage.FromFile("mario3.jpg");<br /> CGImage image = ui.CGImage;<br /> <br /> CGColorSpace colorspace = CGColorSpace.CreateDeviceRGB();<br /> data = Marshal.AllocHGlobal(image.Height*image.Width*4);<br /> CGContext context = new CGBitmapContext(data,image.Width,image.Height,8,4*image.Width, colorspace,CGImageAlphaInfo.NoneSkipLast);<br /> colorspace.Dispose();<br /> context.ClearRect(new RectangleF(0,0,image.Width,image.Height));<br /> context.DrawImage(new RectangleF(0,0,image.Width,image.Height),image);<br /> <br /> //Generate a texture object<br /> GL.GenTextures(1,ref textureId);<br /> <br /> //Bind the texture object<br /> GL.ActiveTexture(All.Texture0);<br /> GL.BindTexture(All.Texture2D, textureId);<br /> GL.Uniform1(textureId,uniformSampler);<br /> <br /> //Load the texture<br /> GL.TexImage2D(All.Texture2D, 0, (int)All.Rgba, image.Width, image.Height, 0 ,All.Rgba, All.UnsignedByte, data);<br /> <br /> //Set the filtering mode<br /> GL.TexParameter(All.Texture2D, All.TextureMinFilter, (float)All.Nearest);<br /> GL.TexParameter(All.Texture2D, All.TextureMagFilter, (float)All.Nearest);<br /> <br /> //Wrap setting<br /> GL.TexParameter(All.Texture2D, All.TextureWrapS,(int)All.Repeat);<br /> GL.TexParameter(All.Texture2D, All.TextureWrapT,(int)All.Repeat);<br /> <br /> context.Dispose();<br /> Marshal.FreeHGlobal(data);<br /> }<br /><br /> protected override void ConfigureLayer (CAEAGLLayer eaglLayer)<br /> {<br /> eaglLayer.Opaque = true;<br /> } <br /> <br /> private void GetUniformVariables()<br /> {<br /> uniformMat = GL.GetUniformLocation(program, "uMVPMatrix");<br /> uniformSampler = GL.GetUniformLocation(program, "sTexture");<br /> }<br /> <br /> private void SetUniformMatrix4(int location, bool transpose, ref Matrix4 matrix)<br /> {<br /> unsafe<br /> {<br /> fixed (float* matrix_ptr = &matrix.Row0.X)<br /> {<br /> GL.UniformMatrix4(location,1,transpose,matrix_ptr);<br /> }<br /> }<br /> }<br /> <br /> float rotateY = 0;<br /> <br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame (e);<br /> <br /> MakeCurrent();<br /><br /> GL.ClearColor (0.7f, 0.7f, 0.7f, 1);<br /> GL.Clear ((int)All.ColorBufferBit);<br /> <br /> GL.FrontFace(All.Ccw);<br /> GL.CullFace(All.Front);<br /> GL.Enable(All.CullFace);<br /> <br /><br /> GL.Viewport (0, 0, viewportWidth, viewportHeight);<br /> GL.UseProgram (program);<br /> <br /> matWorld = Matrix4.CreateRotationY((float)e.Time + rotateY) * Matrix4.CreateTranslation(Vector3.Zero);<br /> matWorldViewProjection = matWorld * matView * matProjection;<br /> <br /> SetUniformMatrix4(uniformMat,false,ref matWorldViewProjection);<br /> <br /> //Active params<br /> GL.EnableVertexAttribArray (attributePosition);<br /> GL.EnableVertexAttribArray (attributeTexCoord);<br /> <br /> //Set params<br /> GL.VertexAttribPointer(attributePosition,3,All.Float,false,3*sizeof(float),squareVertices);<br /> GL.VertexAttribPointer(attributeTexCoord,2,All.Float,false,2*sizeof(float),texCoords);<br /><br /> GL.DrawArrays (All.Triangles, 0, 36);<br /> //GL.DrawElements(All.Triangles, indices.Length, All.Int, indices);<br /> <br /> rotateY += (float)Math.PI / 270;<br /> <br /> //Swapped buffer (Front and Back buffer)<br /> SwapBuffers ();<br /> }<br /> <br /> protected override void OnDisposed (EventArgs e)<br /> {<br /> base.OnDisposed (e);<br /> Marshal.FreeHGlobal (data);<br /> if (textureId != 0)<br /> GL.DeleteTextures(1, ref textureId);<br /> }<br /> <br /> }<br />}<br /></code></pre><br /><br /><br />Y el resultado en pantalla sería algo así:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkgn9qxzsJSEnAB7hql5l6hF2rS_Veur8MYTuMVX0UqLCMSM1rVlrLUXsE05os1VsgXe3guPlG2k0jxEGgt3qqdvCNVLyy67NaUSm71XBh6JgR7zaPwy_NRhH4Vg3pnApk5xE3H14HkDo/s1600/tutorial4.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 223px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkgn9qxzsJSEnAB7hql5l6hF2rS_Veur8MYTuMVX0UqLCMSM1rVlrLUXsE05os1VsgXe3guPlG2k0jxEGgt3qqdvCNVLyy67NaUSm71XBh6JgR7zaPwy_NRhH4Vg3pnApk5xE3H14HkDo/s320/tutorial4.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598805390094714002" /></a>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-35358482965003779912011-04-22T00:18:00.000-07:002011-04-23T09:51:16.140-07:00OpenGL-ES2.0 y Monotouch III (AlphaTest y Scissor)En este tercer tutotrial muestro como realizar AlphaTest y Scissor.<br /><br /><span style="font-weight:bold;">AlphaTest</span><br /><br />En la mayoría de motores 3D como sabéis existen dos técnicas a la hora de trabajar con el canal Alpha. Estas son AlphaTest o AlphaBlending.<br /><br />AlphaTest es la técnica más eficiente y consiste en marcar un umbral para el canal Alpha, si este lo supero el pixel se mostrará y si no lo supera el pixel no se pintará y por lo tanto la imagen será transparente en ese pixel.<br /><br />AlphaBlending esta técnica es mucho más sofisticada y al mismo tiempo menos eficiente, pero consigue implementar diferentes niveles de alpha de forma que un pixel con alpha 0.7 será más opaco que uno con alpha 0.2.<br /><br />En este tutorial explico como realizar AlphaTest a través de Fragment shader de OpenGLES2.0.<br /><br /><pre class="source-code"><code>string fragmentShaderSrc = @"precision mediump float;<br /> varying vec2 vTexCoord;<br /> uniform sampler2D sTexture;<br /> void main() <br /> { <br /> vec4 baseColor = texture2D(sTexture, vTexCoord);<br /> <br /> if (baseColor.a < 0.5)<br /> {<br /> discard;<br /> }<br /> else<br /> {<br /> gl_FragColor = vec4(baseColor.xyz,1.0);<br /> }<br /> }";<br /></code></pre><br /><br />Lo que hacemos es coger el valor alpha de la textura (que tendrá que ser una textura que albergue canal alpha como PNG) y le hacemos un alphaTest diciendo que si supera nuestro umbral fijado en 0.5f (el canal alpha esta comprendido entre 0 y 1) se le manda al glFragColor y si no se llama a la función discard que descarta el pixel y no lo envia para pintar.<br /><br />El resultado sería:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQZV76tD7HDRiukjMhSL7Sb6ImIB7zXKAChWgEZMmMu3JqmYw7vRSJedfP1xUpkOMpn1MIRKGuBi7mc0dlE5f1tqJeiW8JSmk4GqnlEvu_DnLoZWqZPhndXpUy7iM7bHqlOQox_Uq1Sk/s1600/tutorial3_1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 222px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQZV76tD7HDRiukjMhSL7Sb6ImIB7zXKAChWgEZMmMu3JqmYw7vRSJedfP1xUpkOMpn1MIRKGuBi7mc0dlE5f1tqJeiW8JSmk4GqnlEvu_DnLoZWqZPhndXpUy7iM7bHqlOQox_Uq1Sk/s320/tutorial3_1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598307697446250514" /></a><br /><br /><br /><span style="font-weight:bold;">Scissor</span><br /><br />Scissor es una técnica por la cual le podemos decir al render que no pinte toda la pantalla sino un rectángulo de ella. Un ejemplo sería, imaginaros los tipicos juegos donde tenemos nuestra escena 3d pero esta está rodeada de controles o botones que nos permiten cambiar cosas de la escena. Bien pues la escena 3d no tiene que ocupar toda la pantalla sólo el rectangulo que queda libre dentro del marco de botones.<br /><br />Para esta técnica sólo necesitamos añadir dos funciones al método de dibujado que nos proporciona la clase iPhoneOSGameView llamado <span style="font-style:italic;">OnRenderFrame</span>.<br /><br />Primero llamaremos al método <span style="font-style:italic;">GL.Scissor</span> de OpenglES para especificar el rectangulo que limitará el pintado. Y lo segundo que debemos hacer es decirle a OpenglES que active la fase de ScissorTest en el pipeline para que tenga efecto.<br /><br /><pre class="source-code"><code><br />...<br />GL.Scissor(0,0,viewportWidth/2,viewportHeight);<br />GL.Enable(All.ScissorTest);<br />...<br /></code></pre><br /><br /><span style="font-style:italic;">(Nota. Tener en cuenta que la coordenada (0,0) sería la esquina inferior izquierda de nuestra pantalla)</span><br /><br />Resultado sería:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkT9D9M2uyyocsCrot8-ggq-4aC8yW0q5FMRKS1XEOsg4zgK-cecUKL8M6Yy28EjJ2ghJffMTsI2E08vc29kyTBmGMnjazBLjE-g-bqDHuZDfu7Uk5kUX29isp4n8zp0GAkVBkRhRJqbc/s1600/tutorial3_2.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 220px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkT9D9M2uyyocsCrot8-ggq-4aC8yW0q5FMRKS1XEOsg4zgK-cecUKL8M6Yy28EjJ2ghJffMTsI2E08vc29kyTBmGMnjazBLjE-g-bqDHuZDfu7Uk5kUX29isp4n8zp0GAkVBkRhRJqbc/s320/tutorial3_2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598315894240187410" /></a>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-16531158153843877982011-04-17T04:09:00.000-07:002011-04-23T09:53:35.208-07:00OpenGL-ES2.0 y Monotouch II (textures)Hoy publico un nuevo ejemplo de cómo cargar y renderizar texturas con Monotouch y OpenGL ES2.0. En el anterior artículo publiqué como crear un poligono en pantalla el ejemplo de hoy es un poco más complejo aunque sólo amplía el código del artículo anterior, por lo que si no lo has leido te recomiendo que lo hagas "<a href="http://eldespachodelosjorges.blogspot.com/2011/04/opengles20-y-monotouch.html">OpenGLES2.0 y Monotouch I</a>".<br /><br />Lo siguiente a cuando ya sabemos crear polígonos es poder aplicarles texturas, para ello es muy importante tener algunos conceptos claros.<br /><br />Lo primero vamos a crear un square en pantalla esto sería:<br /><br /><pre class="source-code"><code><br />public float[] squareVertices = { -0.5f, -0.5f,<br />    0.5f, -0.5f,<br />    -0.5f, 0.5f,<br />    0.5f, 0.5f };<br /></code></pre><br /><br />Con esto definimos los cuatro vertices que tendrá nuestro cuadrado.<br />A la hora de texturizarlo es importante conocer lo que son las coordenadas de texturas. Estas permiten a los polígonos posicionar la textura sobre ellos. Estas suelen llamarse (u,v) o (s,t) y es un vector de 2 float en el que sus valores estan entre [0-1] por cada vertice de nuestro poligono.<br /><br />Para que la textura cubra totalmente nuestro square debemos definir un array de 4 vectores (u,v) que son las coordenadas de textura de cada vertice.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://lh5.ggpht.com/_QLwms0mVa4w/Shm2PNCrsBI/AAAAAAAAAUE/OpJPups2zlM/texture_coords.png?imgmax=800"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 401px; height: 318px;" src="http://lh5.ggpht.com/_QLwms0mVa4w/Shm2PNCrsBI/AAAAAAAAAUE/OpJPups2zlM/texture_coords.png?imgmax=800" border="0" alt="" /></a><br /><br />Las definimos como:<br /><pre class="source-code"><code><br />float[] texCoords = new float[] { 0.0f,1.0f,<br />   1.0f,1.0f,<br />   0.0f,0.0f,<br />   1.0f,0.0f<br /> };<br /></code></pre><br /><br />Ahora Vamos a definir el Vertex shader y el Fragment shader:<br /><span style="font-weight:bold;"><br />Vertex Shader</span><br /><br /><pre class="source-code"><code><br />string vertexShaderSrc =  @"attribute vec4 aPosition; <br /> attribute vec2 aTexCoord;<br /> varying vec2 vTexCoord;<br />     void main()                  <br />     {                        <br />    vTexCoord = aTexCoord;<br />        gl_Position = aPosition;<br />     }";     <br /></code></pre><br /><br />Definimos el atributo aTexCoord que es dónde cargaremos las coordenadas de textura del vertice que en ese momento estemos procesando. Pero como la única salida de un Vertex Shader es a través de variables de tipo <span style="font-style:italic;">varying</span> definimos también vTexCoord al que se le copiaran los valores.<br /><br />Y ahora el <span style="font-weight:bold;">Fragment Shader</span><br /><br /><pre class="source-code"><code><br />string fragmentShaderSrc = @"precision mediump float;<br /> varying vec2 vTexCoord;<br /> uniform sampler2D sTexture;<br />             void main()                                <br />             {                                         <br />               gl_FragColor = texture2D(sTexture,vTexCoord); <br />             }";<br /></code></pre><br /><br />En el fragment shader tenemos como variable de entrada vTexCoord que fue pasada por el vertex shader y se define un variable de tipo Uniform que es la que albergará los pixeles de la textura. Finalmente para aplicar la textura llamamos a la función texture2D y el pasamos la textura y las coordenadas de textura que le corresponden y esta pintará correctamente la textura sobre el polígono.<br /><br />Ahora necesitamos cargar la textura. Para ello desde código OpenGLES2 puro escribimos:<br /><br /><pre class="source-code"><code><br /> //Generate a texture object<br /> GL.GenTextures(1,ref textureId);<br /> <br /> //Bind the texture object<br /> GL.ActiveTexture(All.Texture0);<br /> GL.BindTexture(All.Texture2D, textureId);<br /> GL.Uniform1(textureId,0);<br /></code></pre><br />Creamos un objeto textura con la función <span style="font-style:italic;">GenTextures</span><br /><br />Y luego muy importante activamos el canal de textura0 que tiene OpenGL-ES, básicamente le estamos diciendo que vamos a trabajar sobre el primer canal le decimos también que es una textura 2D y no una textura cúbica con la función <span style="font-style:italic;">BindTexture</span> y le decimos que la textura cargada será enlazada contra la primera variable de tipo uniform con <span style="font-style:italic;">Uniform1</span>.<br /><br />Para cargar la textura usaremos las clases que wrapea monotouch sobre IPhone, que es la clase UIImage.<br /><br /><pre class="source-code"><code><br />UIImage ui = UIImage.FromFile("mario3.jpg");<br /> CGImage image = ui.CGImage;<br /> <br /> CGColorSpace colorspace = CGColorSpace.CreateDeviceRGB();<br /> data = Marshal.AllocHGlobal(image.Height*image.Width*4);<br /> CGContext context = new CGBitmapContext(data,image.Width,image.Height,8,4*image.Width, colorspace,CGImageAlphaInfo.NoneSkipLast);<br /> colorspace.Dispose();<br /> context.ClearRect(new RectangleF(0,0,image.Width,image.Height));<br /> context.DrawImage(new RectangleF(0,0,image.Width,image.Height),image);<br /></code></pre><br /><br />(Nota. La imagen debe ser multiplo de 2 para que la cargue opengl-es, es decir, 2-4-8-16-32-64... En otro tutorial explicaré como adaptar nuestra imagen dada la resolución que sea).<br /><br />Tener en cuenta que la imagen que estoy cargando es un retrato de Mario Bros en formato PNG que como sabeis tiene formato RGBA. de hay que el espacio reservado sea 4(bytes) por image.width para cada fila de nuestra variable <span style="font-weight:bold;">data</span> y tantas columnas como image.height.<br /><br />Una vez la imagen cargada para enlazarla a nuestro objeto textura escribiremos:<br /><br /><pre class="source-code"><code><br />GL.TexImage2D(All.Texture2D, 0, (int)All.Rgba, image.Width, image.Height, 0 ,All.Rgba, All.UnsignedByte, data);<br /></code></pre><br /><br />Con esto le decimos que es una imagen de tipo RGBA (con canal alpha) y que el contenido son bytes sin signos.<br /><br />Una vez cargada le aplicamos los filtros y el wrap a la textura:<br /><br /><pre class="source-code"><code><br />//Set the filtering mode<br /> GL.TexParameter(All.Texture2D, All.TextureMinFilter, (float)All.Nearest);<br /> GL.TexParameter(All.Texture2D, All.TextureMagFilter, (float)All.Nearest);<br /> <br /> //Wrap setting<br /> GL.TexParameter(All.Texture2D, All.TextureWrapS,(int)All.Repeat);<br /> GL.TexParameter(All.Texture2D, All.TextureWrapT,(int)All.Repeat);<br /></code></pre><br /><br />En otro tutorial explicaré los filtros posibles, así como los 3 modos de wrap que soporta OpenGL-ES2.0.<br /><br />Y por último y no menos importante liberar el espacio reservado:<br /><pre class="source-code"><code><br /> context.Dispose();<br /> Marshal.FreeHGlobal(data);<br /></code></pre><br /><br />En el método draw aplicaremos al vertex shader los vértices y las coordenadas de textura de la siguiente forma:<br /><pre class="source-code"><code><br />//Active params<br /> GL.EnableVertexAttribArray (0);<br /> GL.EnableVertexAttribArray (1);<br /> <br /><br /> //Set params<br /> GL.VertexAttribPointer(0,2,All.Float,false,2*sizeof(float),squareVertices);<br /> GL.VertexAttribPointer(1,2,All.Float,false,2*sizeof(float),texCoords);<br /></code></pre><br /><br />Y listo, si quereis probarlo sólo necesitais tener monodevelop con Monotouch instalado en vuestro MacOSX. Crear un proyecto de tipo Monotouch OpenGL y modificar el código de la clase llamada <span style="font-weight:bold;">EAGLView.cs</span> por:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJjz-f8pc3UesvPTx93OJA2T3Z2W9XYfAyc7ZliKjq9_EBwb6uz4Xst0avQGgj78NYNuPSpKxypc__hajE8gimmsYEUpjxeOYWUX0aDYV9ymNXpwKTt1Nfg1VtM9JOtGOAEVGzBVSlirI/s1600/tutorial2.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 220px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJjz-f8pc3UesvPTx93OJA2T3Z2W9XYfAyc7ZliKjq9_EBwb6uz4Xst0avQGgj78NYNuPSpKxypc__hajE8gimmsYEUpjxeOYWUX0aDYV9ymNXpwKTt1Nfg1VtM9JOtGOAEVGzBVSlirI/s320/tutorial2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598303368162479810" /></a><br /><br /><pre class="source-code"><code><br />#define OPENGLES2<br /><br /><br />using System;<br />using OpenTK.Platform.iPhoneOS;<br />using MonoTouch.CoreAnimation;<br />using OpenTK;<br /><br />#if OPENGLES2<br /> using OpenTK.Graphics.ES20;<br />#else<br /> using OpenTK.Graphics.ES11;<br />#endif<br />using MonoTouch.Foundation;<br />using MonoTouch.ObjCRuntime;<br />using MonoTouch.OpenGLES;<br />using System.Text;<br />using System.Drawing;<br />using OpenTK.Platform;<br />using MonoTouch.CoreGraphics;<br />using MonoTouch.UIKit;<br />using System.Runtime.InteropServices;<br /><br />namespace Texturing<br />{<br /> public partial class EAGLView : iPhoneOSGameView<br /> {<br /> int viewportWidth, viewportHeight;<br /> int program;<br /> <br /> float[] squareVertices = { -0.5f,-0.5f,<br />    0.5f,-0.5f,<br />    -0.5f,0.5f,<br />    0.5f,0.5f<br /> };<br />// byte[] pixels = {255,0,0, //Red<br />// 0,255,0, //Green<br />//          0,0,255, //Blue<br />// 255,255,0 //Yellow<br />// };<br /> <br /> //Texture<br /> int textureId;<br /> float[] texCoords = new float[] { 0.0f,1.0f,<br />   1.0f,1.0f,<br />   0.0f,0.0f,<br />   1.0f,0.0f<br /> };<br /> <br /> IntPtr data;<br /> <br /><br /> [Export("layerClass")]<br /> static Class LayerClass ()<br /> {<br /> return iPhoneOSGameView.GetLayerClass ();<br /> }<br /><br /> [Export("initWithCoder:")]<br /> public EAGLView (NSCoder coder) : base(coder)<br /> {<br /> LayerRetainsBacking = false;<br /> LayerColorFormat = EAGLColorFormat.RGBA8;<br /> }<br /> <br /> protected override void CreateFrameBuffer ()<br /> {<br />#if OPENGLES2<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;<br /> base.CreateFrameBuffer();<br /> Initialize();<br />#else<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;<br /> base.CreateFrameBuffer();<br />#endif <br /> }<br /> <br />                                 <br /> <br />#if OPENGLES2 <br /> <br /> private bool Initialize()<br /> {<br /> viewportHeight = Size.Height; <br /> viewportWidth = Size.Width;<br /><br /> // Vertex and fragment shaders<br /> string vertexShaderSrc =  @"attribute vec4 aPosition; <br /> attribute vec2 aTexCoord;<br /> varying vec2 vTexCoord;<br />     void main()                  <br />     {                         <br />    vTexCoord = aTexCoord;<br />        gl_Position = aPosition; <br />     }";                           <br /> <br /> string fragmentShaderSrc = @"precision mediump float;<br /> varying vec2 vTexCoord;<br /> uniform sampler2D sTexture;<br />             void main()                                <br />             {                                         <br />               gl_FragColor = texture2D(sTexture,vTexCoord); <br />             }";<br /><br /> int vertexShader = LoadShader (All.VertexShader, vertexShaderSrc );<br /> int fragmentShader = LoadShader (All.FragmentShader, fragmentShaderSrc );<br /> program = GL.CreateProgram();<br /> if (program == 0)<br /> throw new InvalidOperationException ("Unable to create program");<br /><br /> GL.AttachShader (program, vertexShader);<br /> GL.AttachShader (program, fragmentShader);<br /> <br /> //Set position<br /> GL.BindAttribLocation (program, 0, "aPosition");<br /> GL.BindAttribLocation (program, 1, "aTexCoord");<br /> <br /> GL.LinkProgram (program);<br /><br /> int linked = 0;<br /> GL.GetProgram (program, All.LinkStatus, ref linked);<br /> if (linked == 0) {<br /> // link failed<br /> int length = 0;<br /> GL.GetProgram (program, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetProgramInfoLog (program, length, ref length, log);<br /> Console.WriteLine ("GL2", "Couldn't link program: " + log.ToString ());<br /> return false;<br /> }<br /><br /> GL.DeleteProgram (program);<br /> throw new InvalidOperationException ("Unable to link program");<br /> }<br /> <br /> LoadTexture();<br /> <br /> return true;<br /> }<br /> <br /> private int LoadShader ( All type, string source )<br /> {<br />    int shader = GL.CreateShader(type);<br /><br />    if ( shader == 0 )<br />    throw new InvalidOperationException("Unable to create shader");      <br /> <br />    // Load the shader source<br />    int length = 0;<br /> GL.ShaderSource(shader, 1, new string[] {source}, (int[])null);<br />    <br />    // Compile the shader<br />    GL.CompileShader( shader );<br /> <br />    int compiled = 0;<br /> GL.GetShader (shader, All.CompileStatus, ref compiled);<br /> if (compiled == 0) {<br /> length = 0;<br /> GL.GetShader (shader, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetShaderInfoLog (shader, length, ref length, log);<br /> Console.WriteLine("GL2", "Couldn't compile shader: " + log.ToString ());<br /> }<br /><br /> GL.DeleteShader (shader);<br /> throw new InvalidOperationException ("Unable to compile shader of type : " + type.ToString ());<br /> }<br /><br /> return shader;<br /> <br /> }<br /> <br /> private void LoadTexture()<br /> {<br /> <br /> UIImage ui = UIImage.FromFile("mario3.jpg");<br /> CGImage image = ui.CGImage;<br /> <br /> CGColorSpace colorspace = CGColorSpace.CreateDeviceRGB();<br /> data = Marshal.AllocHGlobal(image.Height*image.Width*4);<br /> CGContext context = new CGBitmapContext(data,image.Width,image.Height,8,4*image.Width, colorspace,CGImageAlphaInfo.NoneSkipLast);<br /> colorspace.Dispose();<br /> context.ClearRect(new RectangleF(0,0,image.Width,image.Height));<br /> context.DrawImage(new RectangleF(0,0,image.Width,image.Height),image);<br /> <br /> <br /> //Use tightly packed data<br /> //GL.PixelStore(All.UnpackAlignment, 1);<br /> <br /> //Generate a texture object<br /> GL.GenTextures(1,ref textureId);<br /> <br /> //Bind the texture object<br /> GL.ActiveTexture(All.Texture0);<br /> GL.BindTexture(All.Texture2D, textureId);<br /> GL.Uniform1(textureId,0);<br /> <br /> //Load the texture<br /> GL.TexImage2D(All.Texture2D, 0, (int)All.Rgba, image.Width, image.Height, 0 ,All.Rgba, All.UnsignedByte, data);<br /> //GL.TexImage2D(All.Texture2D,0, (int)All.Rgb,2,2,0, All.Rgb,All.UnsignedByte,pixels);<br /> <br /> //Set the filtering mode<br /> GL.TexParameter(All.Texture2D, All.TextureMinFilter, (float)All.Nearest);<br /> GL.TexParameter(All.Texture2D, All.TextureMagFilter, (float)All.Nearest);<br /> <br /> //Wrap setting<br /> GL.TexParameter(All.Texture2D, All.TextureWrapS,(int)All.Repeat);<br /> GL.TexParameter(All.Texture2D, All.TextureWrapT,(int)All.Repeat);<br /> <br /> context.Dispose();<br /> Marshal.FreeHGlobal(data);<br /> }<br /> <br /> <br />#endif<br /><br /> protected override void ConfigureLayer (CAEAGLLayer eaglLayer)<br /> {<br /> eaglLayer.Opaque = true;<br /> } <br /> <br /> <br />#if OPENGLES2<br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame (e);<br /> <br /> MakeCurrent();<br /><br /> GL.ClearColor (0.7f, 0.7f, 0.7f, 1);<br /> GL.Clear ((int)All.ColorBufferBit);<br /><br /> GL.Viewport (0, 0, viewportWidth, viewportHeight);<br /> GL.UseProgram (program);<br /> <br /> //Active params<br /> GL.EnableVertexAttribArray (0);<br /> GL.EnableVertexAttribArray (1);<br /> <br /> //Set params<br /> GL.VertexAttribPointer(0,2,All.Float,false,2*sizeof(float),squareVertices);<br /> GL.VertexAttribPointer(1,2,All.Float,false,2*sizeof(float),texCoords);<br /><br /> GL.DrawArrays (All.TriangleStrip, 0, 4);<br /> <br /> //Swapped buffer (Front and Back buffer)<br /> SwapBuffers ();<br /> }<br /> <br /> protected override void OnDisposed (EventArgs e)<br /> {<br /> base.OnDisposed (e);<br /> Marshal.FreeHGlobal (data);<br /> if (textureId != 0)<br /> GL.DeleteTextures(1, ref textureId);<br /> }<br /> <br />#else<br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame(e);<br /><br /> float[] squareVertices = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f };<br /> byte[] squareColors = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0,<br /> 0, 0, 255, 0, 255, 255 };<br /> <br /> MakeCurrent ();<br /> GL.Viewport (0, 0, Size.Width, Size.Height);<br /> <br /> GL.MatrixMode (All.Projection);<br /> GL.LoadIdentity ();<br /> GL.Ortho (-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);<br /> GL.MatrixMode (All.Modelview);<br /> GL.Rotate (3.0f, 0.0f, 0.0f, 1.0f);<br /> <br /> GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);<br /> GL.Clear ((uint)All.ColorBufferBit);<br /> <br /> GL.VertexPointer (2, All.Float, 0, squareVertices);<br /> GL.EnableClientState (All.VertexArray);<br /> GL.ColorPointer (4, All.UnsignedByte, 0, squareColors);<br /> GL.EnableClientState (All.ColorArray);<br /> <br /> GL.DrawArrays (All.TriangleStrip, 0, 4);<br /> <br /> SwapBuffers ();<br /> }<br />#endif<br /> }<br />}<br /><br /><br /></code></pre>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-53843198828569784222011-04-12T07:42:00.000-07:002011-04-23T09:53:57.241-07:00OpenGLES2.0 y Monotouch I (Poligon)Actualmente me encuentro trabajando con OpenGLES2.0 en el proyecto <a href="http://monogame.codeplex.com/">MonoGame</a> y una de las cosas que más trabajo me ha costado encontrar<br />es un ejemplo de monotouch usando openGL-ES2.0. Por ello he decido escribir uno para muchos como yo que estuvierán perdidos.<br /><br />Bien pues si teneís instalado Monotouch en vuestro MacOSX (actualmente monotouch sólo funciona sobre MacOSX) y usáis Monodevelop podréis ver una plantilla de Monotouch y OpenGL. <br /><br />Creamos un proyecto de este tipo y veremos que tenemos dos ficheros (Main.cs y EAGLView.cs) además del componente ventana (fichero xib). <br /><br /><span style="font-weight:bold;">Main.cs</span>: Es la clase que contiene el método main y que llama a UIApplication.Main que es el método principal al trabajar en IOS.<br /><br /><span style="font-weight:bold;">EAGLView.cs</span>: Es la clase de ejemplo que nos crea Monotouch usando OpenGL-ES1.1 con fixed pipeline. Que basicamente significa que no podemos modificar el pipeline de dibujado y por lo tanto no podemos usar shader. Los shader nos permiten tener un control más pontente del pipeline de dibujado y por lo tanto mayor flexibilidad. Otra de las mejoras al usar OpenGL-ES2.0 es que en vez de limitarnos a texturas de 1024x1024 en la versión ES1.1 en la segunda versión ya puedes usar texturas de 2048x2048.<br /><br />Bueno en resumen por estos motivos y muchos más es muy interesante migrarnos a OpenGL-ES2.0 pero con la salvedad de que en la actualidad aún todos los terminales no soportan ES2.0, así que si el dispositivo no detecta esto deberemos volver a la versión 1.1<br /><br />Para usar OpenGLES2.0 sobre este ejemplo sólo vamos a tocar la clase EAGLView.cs por lo que es la única que os muestro:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSAHE6WCdjONGqX7Ap8UoiczyLooJbyXybhTLTrxEGLmd1S8lBhdoWHRXM5rkPTs4PtUcFetStwGqhg5EwKo7mmcSnSs4aTrmvh7ou5QCP_OhWQnUpcGdMltk0cO4SEIas6no3EYBOFRs/s1600/tutorial1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 220px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSAHE6WCdjONGqX7Ap8UoiczyLooJbyXybhTLTrxEGLmd1S8lBhdoWHRXM5rkPTs4PtUcFetStwGqhg5EwKo7mmcSnSs4aTrmvh7ou5QCP_OhWQnUpcGdMltk0cO4SEIas6no3EYBOFRs/s320/tutorial1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5598302990255144098" /></a><br /><br /><span style="font-weight:bold;">EAGLView.cs</span><br /><br /><pre class="source-code"><code><br />#define OPENGLES2<br /><br /><br />using System;<br />using OpenTK.Platform.iPhoneOS;<br />using MonoTouch.CoreAnimation;<br />using OpenTK;<br /><br />#if OPENGLES2<br /> using OpenTK.Graphics.ES20;<br />#else<br /> using OpenTK.Graphics.ES11;<br />#endif<br />using MonoTouch.Foundation;<br />using MonoTouch.ObjCRuntime;<br />using MonoTouch.OpenGLES;<br />using System.Text;<br />using System.Drawing;<br />using OpenTK.Platform;<br /><br />namespace OpenGLES<br />{<br /> public partial class EAGLView : iPhoneOSGameView<br /> {<br /> int viewportWidth, viewportHeight;<br /> int program;<br /> float [] vertices = new float [] {0.0f, 0.5f, 0.0f,<br />   -0.5f, -0.5f, 0.0f,<br />   0.5f, -0.5f, 0.0f<br />      };<br /><br /> [Export("layerClass")]<br /> static Class LayerClass ()<br /> {<br /> return iPhoneOSGameView.GetLayerClass ();<br /> }<br /><br /> [Export("initWithCoder:")]<br /> public EAGLView (NSCoder coder) : base(coder)<br /> {<br /> LayerRetainsBacking = false;<br /> LayerColorFormat = EAGLColorFormat.RGBA8;<br /> }<br /> <br /> protected override void CreateFrameBuffer ()<br /> {<br />#if OPENGLES2<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;<br /> base.CreateFrameBuffer();<br /> Initialize();<br />#else<br /> ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;<br /> base.CreateFrameBuffer();<br />#endif <br /> }<br /> <br />                                 <br /> <br />#if OPENGLES2 <br />// protected override void OnLoad(EventArgs e)<br />// {<br />// Initialize();<br />// } <br /> <br /> private bool Initialize()<br /> {<br /> viewportHeight = Size.Height; <br /> viewportWidth = Size.Width;<br /><br /> // Vertex and fragment shaders<br /> string vertexShaderSrc =  @"attribute vec4 aPosition;  <br />     void main()                  <br />     {                         <br />        gl_Position = aPosition; <br />     }";                           <br /> <br /> string fragmentShaderSrc = @"precision mediump float;<br /> varying vec4 vcolor;<br />             void main()                                <br />             {                                         <br />               gl_FragColor = vec4(1.0,0.0,0.0,1.0); <br />             }";<br /><br /> int vertexShader = LoadShader (All.VertexShader, vertexShaderSrc );<br /> int fragmentShader = LoadShader (All.FragmentShader, fragmentShaderSrc );<br /> program = GL.CreateProgram();<br /> if (program == 0)<br /> throw new InvalidOperationException ("Unable to create program");<br /><br /> GL.AttachShader (program, vertexShader);<br /> GL.AttachShader (program, fragmentShader);<br /> <br /> //Set position<br /> GL.BindAttribLocation (program, 0, "aPosition");<br /> <br /> GL.LinkProgram (program);<br /><br /> int linked = 0;<br /> GL.GetProgram (program, All.LinkStatus, ref linked);<br /> if (linked == 0) {<br /> // link failed<br /> int length = 0;<br /> GL.GetProgram (program, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetProgramInfoLog (program, length, ref length, log);<br /> Console.WriteLine ("GL2", "Couldn't link program: " + log.ToString ());<br /> return false;<br /> }<br /><br /> GL.DeleteProgram (program);<br /> throw new InvalidOperationException ("Unable to link program");<br /> }<br /> <br /> return true;<br /> }<br /> <br /> private int LoadShader ( All type, string source )<br /> {<br />    int shader = GL.CreateShader(type);<br /><br />    if ( shader == 0 )<br />    throw new InvalidOperationException("Unable to create shader");      <br /> <br />    // Load the shader source<br />    int length = 0;<br /> GL.ShaderSource(shader, 1, new string[] {source}, (int[])null);<br />    <br />    // Compile the shader<br />    GL.CompileShader( shader );<br /> <br />    int compiled = 0;<br /> GL.GetShader (shader, All.CompileStatus, ref compiled);<br /> if (compiled == 0) {<br /> length = 0;<br /> GL.GetShader (shader, All.InfoLogLength, ref length);<br /> if (length > 0) {<br /> var log = new StringBuilder (length);<br /> GL.GetShaderInfoLog (shader, length, ref length, log);<br /> Console.WriteLine("GL2", "Couldn't compile shader: " + log.ToString ());<br /> }<br /><br /> GL.DeleteShader (shader);<br /> throw new InvalidOperationException ("Unable to compile shader of type : " + type.ToString ());<br /> }<br /><br /> return shader;<br /> <br /> }<br />#endif<br /><br /> protected override void ConfigureLayer (CAEAGLLayer eaglLayer)<br /> {<br /> eaglLayer.Opaque = true;<br /> } <br /> <br /> <br />#if OPENGLES2<br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame (e);<br /> <br /> MakeCurrent();<br /><br /> GL.ClearColor (0.7f, 0.7f, 0.7f, 1);<br /> GL.Clear ((int)All.ColorBufferBit);<br /><br /> GL.Viewport (0, 0, viewportWidth, viewportHeight);<br /> GL.UseProgram (program);<br /> <br /> GL.EnableVertexAttribArray (0);<br /> <br /> GL.VertexAttribPointer (0, 3, All.Float, false, 0, vertices);<br /><br /> GL.DrawArrays (All.Triangles, 0, 3);<br /><br /> SwapBuffers ();<br /> }<br /> <br />#else<br /> protected override void OnRenderFrame (FrameEventArgs e)<br /> {<br /> base.OnRenderFrame(e);<br /><br /> float[] squareVertices = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f };<br /> byte[] squareColors = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0,<br /> 0, 0, 255, 0, 255, 255 };<br /> <br /> MakeCurrent ();<br /> GL.Viewport (0, 0, Size.Width, Size.Height);<br /> <br /> GL.MatrixMode (All.Projection);<br /> GL.LoadIdentity ();<br /> GL.Ortho (-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);<br /> GL.MatrixMode (All.Modelview);<br /> GL.Rotate (3.0f, 0.0f, 0.0f, 1.0f);<br /> <br /> GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);<br /> GL.Clear ((uint)All.ColorBufferBit);<br /> <br /> GL.VertexPointer (2, All.Float, 0, squareVertices);<br /> GL.EnableClientState (All.VertexArray);<br /> GL.ColorPointer (4, All.UnsignedByte, 0, squareColors);<br /> GL.EnableClientState (All.ColorArray);<br /> <br /> GL.DrawArrays (All.TriangleStrip, 0, 4);<br /> <br /> SwapBuffers ();<br /> }<br />#endif<br /> }<br />}<br /><br /></code></pre><br /><br /><br />Nota. Para terminar si comentas la primera linea es decir "#define OPENGLES2" se renderizará usando ES1.1 y si no<br />usará ES2.0 con shaders.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-89110670653122866322011-04-12T07:37:00.000-07:002011-04-12T07:42:03.929-07:00Abrir dos instancias de la misma aplicación en MacOSXActualmente estoy trabajando con Monodevelop en MacOSX y uno de los primeros incovenientes que te encuentras es el no poder abrir varias instancias de Monodevelop con lo necesario que esto es para un programador. En principio MacOSX no te deja hacerlo desde su interfaz gráfico pero esto no quiere decir que no se pueda. Así que hoy os cuento otro tip sobre MacOSX que a mi me facilitó mucho la tarea al trabajar con Monodevelop.<br /><br /><br />Abrimos un terminal y ponemos<br /><br /><span style="font-style:italic;">/Applications/MonoDevelop.app/Contents/MacOS/monodevelop &</span><br /><br />Dentro del directorio Applications se encuentran todas las aplicaciones que tenemos instaladas por lo que supongo que esto seguro que os sirve para otros programas.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-6339260649398178772011-04-12T07:26:00.000-07:002011-04-12T07:36:10.594-07:00Mostrar ficheros ocultos en MacOSXHoy escribo un pequeño truco que me reportó Javier fernandez de Syderis para poder ver en una ventana del finder en MacOSX los ficheros y directorios ocultos. Esto es un poco más rebuscado que en sistemas como Windows y no tenemos una propiedad fácilmente modificable. Necesitamos modificar la variable AppleShowAllFiles que es un bool y ponerla a true ya que por defecto viene a false, para ello:<br /><br />Abrimos un terminal y ponemos:<br /><br />Mostrar ficheros ocultos:<br /><span style="font-style:italic;">defaults write com.apple.finder AppleShowAllFiles TRUE <br />killall Finder</span><br /><br />Ocultar ficheros ocultos:<br /><span style="font-style:italic;">defaults write com.apple.finder AppleShowAllFiles FALSE<br /> killall Finder</span>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-77706399935283210122011-03-22T01:00:00.000-07:002011-03-22T01:09:10.563-07:00Crear un fichero de cualquier tamañoHoy estamos configurando el NAS y necesitamos ficheros de tamaños determinados para probar cuotas de espacio en disco. Para esto es muy util poder crear un fichero de un fichero determinado. Para esto en sistemas deribados de UNIX como MacOSX o la famila Linux tenemos el comando dd que nos ayudará a esta tarea. Dejo este pequeño tip que espero le sea de utilidad a muchos de vosotros.<br /><br />Nos posicionamos en la carpeta donde queramos dejar el fichero<br /><span style="font-weight:bold;">cd /tmp</span><br /><br />Creamos un fichero de 50MGs<br /><span style="font-weight:bold;">dd if=/dev/zero of=archivo50megas.txt bs=1024 count=51200</span><br /><br /><span style="font-style:italic;">if - archivo de entrada (cogemos basura del dispositivo zero)<br />of - archivo de salida (el nombre de nuestro fichero)<br />bs - tamaño de bloque bytes (1024 indica bloques de 1k)<br />count - Número de bloques (51200 = 50*1024(Mg)) </span>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-8703192485601569622010-11-19T03:41:00.000-08:002010-11-19T05:54:32.876-08:00Lanzar aplicaciones con retraso al iniciar windows 7Como todos sabéis Windows tiene varios mecanismos para lanzar aplicaciones en el arranque del sistema operativo. La fórmula más conocida es colocar un acceso directo en la carpeta "Inicio". Pero hoy me encuentro con el problema de lanzar una aplicación que hace uso de la tarjeta gráfica y el problema consiste es que al poner la aplicación en la carpeta de Inicio esta se lanza antes de que se cargue el servicio de DirectX por lo que la aplicación da un fallo de no estar aún enable dichas funcionalidades. La manera de resolver el problema ha sido construir un pequeño script en bacth (lenguaje de script de MS-DOS) para que espero lo necesario hasta que los servicios gráficos hayan sido cargados. <br /><br />Os dejo el script:<br /><code><br />---------------------<br />@echo off<br />timeout 30<br />start chrome<br />------------------<br /><span style="font-weight:bold;">"@echo off"</span> -> no imprima la salida de los comandos por la consola<br /><span style="font-weight:bold;">"timeout 30"</span> -> espere 30 segundos (tiempo suficiente para el arranque de todos los servicios de windows 7<br /><span style="font-weight:bold;">"start chrome"</span> -> arrancar la aplicación<br /><br />Lo guardais como loquequerais.bat y lo colocais en la carpeta de "Inicio"<br /><br /><br />Sintasis del comando start<br />Syntax<br />      START "title" [/Dpath] [options] "command" [parameters]<br /><br />Key:<br />   title      : Text for the CMD window title bar (required)<br />   path       : Starting directory<br />   command    : The command, batch file or executable program to run<br />   parameters : The parameters passed to the command<br /><br />Options:<br />   /MIN       : Minimized<br />   /MAX       : Maximized<br />   /WAIT      : Start application and wait for it to terminate<br />   /LOW       : Use IDLE priority class<br />   /NORMAL    : Use NORMAL priority class<br />   /HIGH      : Use HIGH priority class<br />   /REALTIME  : Use REALTIME priority class<br /><br />   /B         : Start application without creating a new window. In this case<br />                ^C will be ignored - leaving ^Break as the only way to <br />                interrupt the application<br />   /I         : Ignore any changes to the current environment.<br /><br />   Options for 16-bit WINDOWS programs only<br /><br />   /SEPARATE   Start in separate memory space (more robust)<br />   /SHARED     Start in shared memory space (default)<br /></code>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-64357027171280186832010-10-27T07:24:00.000-07:002010-10-28T04:46:15.959-07:00Mi primera DLLHoy he decidido grabarme un video construyendo una dll básica en c++ y consumiendola desde una aplicación de consola en c++ porque es una tarea util cuando necesitas hacer un wrapper de una librería en c o c++ y usarla desde otros lenguajes como c#. Aunque es algo muy fácil me cuesta muchas veces recordar la nomenglatura de ahí la idea de este tutorial espero que os sea útil para arrancar vuestros proyectos.<br /><br /><object style="height: 260px; width: 426px"><param name="movie" value="http://www.youtube.com/v/tjE0JyO0D0o?version=3"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"><embed src="http://www.youtube.com/v/tjE0JyO0D0o?version=3" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="426" height="260"></embed><br /></object><br /><br />El codigo fuente de la dll sería:<br /><br /><code><br />#include "stdafx.h"<br /><br />#define DLL_EXPORT extern "C" __declspec(dllexport)<br /><br />DLL_EXPORT double Sum(double a, double b);<br /><br />double Sum(double a, double b)<br />{<br /> return a+b;<br />}<br /><br /></code><br /><br />y el de la clase en c++ que lo consume sería:<br /><br /><code><br />#include "stdafx.h"<br /><br />#include <iostream><br /><br />extern "C" __declspec(dllimport)double Sum(double a, double b);<br /><br />using namespace std;<br /><br />int main(){<br /> cout << "hola dll" << endl;<br /> cout << "La suma de 2 + 3 es:" << Sum(2,3);<br /><br /> getchar();<br /> return 0;<br />}<br /></code><br /><br />Y ahora en este segundo video creo un proyecto de consola de c# desde<br />el cual también consumiremos los métodos de nuestra DLL.<br /><br /><object style="height: 260px; width: 426px"><param name="movie" value="http://www.youtube.com/v/8Fn-yXVFc8M?version=3"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"><embed src="http://www.youtube.com/v/8Fn-yXVFc8M?version=3" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="426" height="260"></embed></object><br /><br />El código de la clase Wrapper:<br /><br /><code><br />namespace UseDLLCSharp<br />{<br />    class WrapperDLL<br />    {<br />        [DllImport("DLL.dll")]<br />        public static extern double Sum(double a, double b);<br />    }<br />}<br /><br /></code><br /><br />El código de la clase principal de C#:<br /><br /><code><br />namespace UseDLLCSharp<br />{<br />    class Program<br />    {<br />        static void Main(string[] args)<br />        {<br />            Console.WriteLine("hola mundo");<br />            Console.WriteLine("La suma de 3 + 2 es: " + WrapperDLL.Sum(3, 2));<br /><br />            Thread.Sleep(5000);<br />        }<br />    }<br />}<br /></code>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com2tag:blogger.com,1999:blog-464513392335827691.post-68690772703659252672010-10-19T23:47:00.000-07:002010-10-19T23:57:20.443-07:00La evolución de un programadorHoy encontré un artículo super divertido sobre la evolución que sufre una programador al ir ascendiendo de cargo o rango con los años, no es perdáis el final.<br /><br /><code><br /><span style="font-weight:bold;">High School/Jr.High</span><br /><br />  10 PRINT "HELLO WORLD"<br />  20 END<br /><br /><span style="font-weight:bold;">First year in College</span><br /><br />  program Hello(input, output)<br />    begin<br />      writeln('Hello World')<br />    end.<br /><br /><span style="font-weight:bold;">Senior year in College</span><br /><br />  (defun hello<br />    (print<br />      (cons 'Hello (list 'World))))<br /><br /><span style="font-weight:bold;">New professional</span><br /><br />  #include <stdio.h><br />  void main(void)<br />  {<br />    char *message[] = {"Hello ", "World"};<br />    int i;<br /> <br />    for(i = 0; i < 2; ++i)<br />      printf("%s", message[i]);<br />    printf("\n");<br />  }<br /><br /><span style="font-weight:bold;">Seasoned professional</span><br /><br />  #include <iostream.h><br />  #include <string.h><br /> <br />  class string<br />  {<br />  private:<br />    int size;<br />    char *ptr;<br /> <br />  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }<br /> <br />    string(const string &s) : size(s.size)<br />    {<br />      ptr = new char[size + 1];<br />      strcpy(ptr, s.ptr);<br />    }<br /> <br />    ~string()<br />    {<br />      delete [] ptr;<br />    }<br /> <br />    friend ostream &operator <<(ostream &, const string &);<br />    string &operator=(const char *);<br />  };<br /> <br />  ostream &operator<<(ostream &stream, const string &s)<br />  {<br />    return(stream << s.ptr);<br />  }<br /> <br />  string &string::operator=(const char *chrs)<br />  {<br />    if (this != &chrs)<br />    {<br />      delete [] ptr;<br />     size = strlen(chrs);<br />      ptr = new char[size + 1];<br />      strcpy(ptr, chrs);<br />    }<br />    return(*this);<br />  }<br /> <br />  int main()<br />  {<br />    string str;<br /> <br />    str = "Hello World";<br />    cout << str << endl;<br /> <br />    return(0);<br />  }<br /><br /><span style="font-weight:bold;">Master Programmer</span><br /><br />  [<br />  uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)<br />  ]<br />  library LHello<br />  {<br />      // bring in the master library<br />      importlib("actimp.tlb");<br />      importlib("actexp.tlb");<br /> <br />      // bring in my interfaces<br />      #include "pshlo.idl"<br /> <br />      [<br />      uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)<br />      ]<br />      cotype THello<br />   {<br />   interface IHello;<br />   interface IPersistFile;<br />   };<br />  };<br /> <br />  [<br />  exe,<br />  uuid(2573F890-CFEE-101A-9A9F-00AA00342820)<br />  ]<br />  module CHelloLib<br />  {<br /> <br />      // some code related header files<br />      importheader(<windows.h>);<br />      importheader(<ole2.h>);<br />      importheader(<except.hxx>);<br />      importheader("pshlo.h");<br />      importheader("shlo.hxx");<br />      importheader("mycls.hxx");<br /> <br />      // needed typelibs<br />      importlib("actimp.tlb");<br />      importlib("actexp.tlb");<br />      importlib("thlo.tlb");<br /> <br />      [<br />      uuid(2573F891-CFEE-101A-9A9F-00AA00342820),<br />      aggregatable<br />      ]<br />      coclass CHello<br />   {<br />   cotype THello;<br />   };<br />  };<br /> <br /> <br />  #include "ipfix.hxx"<br /> <br />  extern HANDLE hEvent;<br /> <br />  class CHello : public CHelloBase<br />  {<br />  public:<br />      IPFIX(CLSID_CHello);<br /> <br />      CHello(IUnknown *pUnk);<br />      ~CHello();<br /> <br />      HRESULT  __stdcall PrintSz(LPWSTR pwszString);<br /> <br />  private:<br />      static int cObjRef;<br />  };<br /> <br /> <br />  #include <windows.h><br />  #include <ole2.h><br />  #include <stdio.h><br />  #include <stdlib.h><br />  #include "thlo.h"<br />  #include "pshlo.h"<br />  #include "shlo.hxx"<br />  #include "mycls.hxx"<br /> <br />  int CHello::cObjRef = 0;<br /> <br />  CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)<br />  {<br />      cObjRef++;<br />      return;<br />  }<br /> <br />  HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)<br />  {<br />      printf("%ws<br />", pwszString);<br />      return(ResultFromScode(S_OK));<br />  }<br /> <br /> <br />  CHello::~CHello(void)<br />  {<br /> <br />  // when the object count goes to zero, stop the server<br />  cObjRef--;<br />  if( cObjRef == 0 )<br />      PulseEvent(hEvent);<br /> <br />  return;<br />  }<br /> <br />  #include <windows.h><br />  #include <ole2.h><br />  #include "pshlo.h"<br />  #include "shlo.hxx"<br />  #include "mycls.hxx"<br /> <br />  HANDLE hEvent;<br /> <br />   int _cdecl main(<br />  int argc,<br />  char * argv[]<br />  ) {<br />  ULONG ulRef;<br />  DWORD dwRegistration;<br />  CHelloCF *pCF = new CHelloCF();<br /> <br />  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);<br /> <br />  // Initialize the OLE libraries<br />  CoInitializeEx(NULL, COINIT_MULTITHREADED);<br /> <br />  CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,<br />      REGCLS_MULTIPLEUSE, &dwRegistration);<br /> <br />  // wait on an event to stop<br />  WaitForSingleObject(hEvent, INFINITE);<br /> <br />  // revoke and release the class object<br />  CoRevokeClassObject(dwRegistration);<br />  ulRef = pCF->Release();<br /> <br />  // Tell OLE we are going away.<br />  CoUninitialize();<br /> <br />  return(0); }<br /> <br />  extern CLSID CLSID_CHello;<br />  extern UUID LIBID_CHelloLib;<br /> <br />  CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */<br />      0x2573F891,<br />      0xCFEE,<br />      0x101A,<br />      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }<br />  };<br /> <br />  UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */<br />      0x2573F890,<br />      0xCFEE,<br />      0x101A,<br />      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }<br />  };<br /> <br />  #include <windows.h><br />  #include <ole2.h><br />  #include <stdlib.h><br />  #include <string.h><br />  #include <stdio.h><br />  #include "pshlo.h"<br />  #include "shlo.hxx"<br />  #include "clsid.h"<br /> <br />  int _cdecl main(<br />  int argc,<br />  char * argv[]<br />  ) {<br />  HRESULT  hRslt;<br />  IHello        *pHello;<br />  ULONG  ulCnt;<br />  IMoniker * pmk;<br />  WCHAR  wcsT[_MAX_PATH];<br />  WCHAR  wcsPath[2 * _MAX_PATH];<br /> <br />  // get object path<br />  wcsPath[0] = '\0';<br />  wcsT[0] = '\0';<br />  if( argc > 1) {<br />      mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);<br />      wcsupr(wcsPath);<br />      }<br />  else {<br />      fprintf(stderr, "Object path must be specified\n");<br />      return(1);<br />      }<br /> <br />  // get print string<br />  if(argc > 2)<br />      mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);<br />  else<br />      wcscpy(wcsT, L"Hello World");<br /> <br />  printf("Linking to object %ws\n", wcsPath);<br />  printf("Text String %ws\n", wcsT);<br /> <br />  // Initialize the OLE libraries<br />  hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);<br /> <br />  if(SUCCEEDED(hRslt)) {<br /> <br /> <br />      hRslt = CreateFileMoniker(wcsPath, &pmk);<br />      if(SUCCEEDED(hRslt))<br />   hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);<br /> <br />      if(SUCCEEDED(hRslt)) {<br /> <br />   // print a string out<br />   pHello->PrintSz(wcsT);<br /> <br />   Sleep(2000);<br />   ulCnt = pHello->Release();<br />   }<br />      else<br />   printf("Failure to connect, status: %lx", hRslt);<br /> <br />      // Tell OLE we are going away.<br />      CoUninitialize();<br />      }<br /> <br />  return(0);<br />  }<br /><br /><span style="font-weight:bold;">Apprentice Hacker</span><br /><br />  #!/usr/local/bin/perl<br />  $msg="Hello, world.\n";<br />  if ($#ARGV >= 0) {<br />    while(defined($arg=shift(@ARGV))) {<br />      $outfilename = $arg;<br />      open(FILE, ">" . $outfilename) || die "Can't write $arg: $!\n";<br />      print (FILE $msg);<br />      close(FILE) || die "Can't close $arg: $!\n";<br />    }<br />  } else {<br />    print ($msg);<br />  }<br />  1;<br /><br /><span style="font-weight:bold;">Experienced Hacker</span><br /><br />  #include <stdio.h><br />  #define S "Hello, World\n"<br />  main(){exit(printf(S) == strlen(S) ? 0 : 1);}<br /><br /><span style="font-weight:bold;">Seasoned Hacker</span><br /><br />  % cc -o a.out ~/src/misc/hw/hw.c<br />  % a.out<br /><br /><span style="font-weight:bold;">Guru Hacker</span><br /><br />  % echo "Hello, world."<br /><br /><span style="font-weight:bold;">New Manager</span><br /><br />  10 PRINT "HELLO WORLD"<br />  20 END<br /><br /><span style="font-weight:bold;">Middle Manager</span><br /><br />  mail -s "Hello, world." bob@b12<br />  Bob, could you please write me a program that prints "Hello, world."?<br />  I need it by tomorrow.<br />  ^D<br /><br /><span style="font-weight:bold;">Senior Manager</span><br /><br />  % zmail jim<br />  I need a "Hello, world." program by this afternoon.<br /><br /><span style="font-weight:bold;">Chief Executive</span><br /><br />  % letter<br />  letter: Command not found.<br />  % mail<br />  To: ^X ^F ^C<br />  % help mail<br />  help: Command not found.<br />  % damn!<br />  !: Event unrecognized<br />  % logout<br /><br /></code><br /><br />(Lo encontre en <a href="http://www.ariel.com.au/jokes/The_Evolution_of_a_Programmer.html">fuente</a>)Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-80184850118559618862010-08-03T09:31:00.001-07:002010-08-03T09:36:27.883-07:00Renombrar ficheros desde JavaHoy tuve que hacer un programita muy tonto pero que me quitó muchas horas<br />de trabajo de monos, así que os dejo el código por si a alguien le sirve <br />del mismo modo que a mí.<br /><br />El programa lo que hace es renombrar todos los ficheros de un directorio<br />desde java. Este directorio estaba lleno de imagenes en formato png y lo <br />que tuve que hacer es añadir al final del nombre la coletilla "_thumb" que<br />viene de thumbnail.<br /><br /><span style="font-weight:bold;">Código</span><br /><br /><code><br />package renombradodeficheros;<br /><br />import java.io.BufferedReader;<br />import java.io.File;<br />import java.io.FileReader;<br />import java.io.IOException;<br /><br />/**<br /> *<br /> * @author Jorge<br /> */<br />public class Main {<br /><br />    /**<br />     * @param args the command line arguments<br />     */<br />    public static void main(String[] args) {<br /><br />        <br />        String path = "resources/directory/";<br /><br />        File directory = new File(path);<br />        String[] files = directory.list();<br />        File f1,f2;<br />        String filename, filenameModif;<br />        int cutindex;<br />        for (int i = 0; i < files.length; i++) {<br /><br />            filename = files[i];<br />            f1 = new File(path+"/"+filename);<br />            cutindex = filename.indexOf(".png");<br />            if (cutindex != -1)<br />            {<br />                filenameModif = filename.substring(0, cutindex)+"_thumb.png";<br />                f2 = new File(path+"/"+filenameModif);<br />                if (f1.renameTo(f2))<br />                    System.out.println("Renombrado");<br />                else<br />                    System.out.println("Fallo al renombrar");<br />            }<br />        }<br />    }<br />}<br /><br /></code>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-55719877673073933302010-08-03T09:12:00.000-07:002010-08-03T09:20:55.629-07:00Trabajar con XML y C#Hola a todos,<br /><br />Hace tiempo que no escribo en el blog pero es que últimamente ando muy muy liado con los diferentes proyectos que estamos desarrollando en <a href="http://www.syderis.com/">Syderis</a>. Hoy os pongo un pequeño ejemplo de como leer y escribir XML con la API de .NET framework y C#.<br /><br /><span style="font-weight: bold;">Dado el xml:</span><br /><code><br /><?xml version="1.0" encoding="utf-8" ?><br /><sample><br /> <equipments><br />   <equipment type="tipo1"><br />     <point x="1900" y="1000"><br />       <title>Titulo</title><br />       <image >DSCN2903</image><br />       <description>Esta es la descripción del primer punto</description>     <br />     </point><br />     <point x="1000" y="2300"><br />       <title>Titulo</title><br />       <image />DSCN2903<br />       <description>mirador2</description>     <br />     </point><br />   </equipment><br /> </equipments><br /></sample><br /><br /></code><br /><br /><span style="font-weight: bold;">Código del lector:</span><br /><br /><code><br />public void Read(string filename)<br />       {<br />           XmlDocument xml = new XmlDocument();<br />           try<br />           {<br />               xml.Load(filename);<br />               XmlNodeList nodeEquipments = xml.GetElementsByTagName("Equipments");<br /><br />               //Equipamientos<br />               XmlNodeList equipmentsNodes = ((XmlElement)nodeEquipments[0]).GetElementsByTagName("equipment");<br />               string sfilename;<br />               string stitle;<br />               string sdescription;<br />               string sx, sy;<br />               foreach (XmlElement equipment in equipmentsNodes)<br />               {<br />                   string stype = equipment.GetAttribute("type");<br />                   XmlNodeList pointsNodes = equipment.GetElementsByTagName("point");<br />                             <br />                           <br />                   foreach (XmlElement pointNode in pointsNodes)<br />                   {<br />                       sx = pointNode.GetAttribute("x");<br />                       sy = pointNode.GetAttribute("y");<br />                       stitle = pointNode.GetElementsByTagName("title")[0].InnerText;<br />                       sfilename = pointNode.GetElementsByTagName("image")[0].InnerText;<br />                       sdescription = pointNode.GetElementsByTagName("description")[0].InnerText;                      <br />                       Console.Writeline("type:"+stype+" coord:"+sx+sy+" filename"+sfilename+" title:"stitle+" description:"+sdescription);<br /><br />                   }<br />               <br />               }<br />           <br />           }<br />           catch (XmlException e)<br />           {<br />               Console.WriteLine("XML file load Failure." + e.ToString());<br />           }<br />}<br /><br /></code><br /><br /><br /><span style="font-weight: bold;">Código escritor:</span><br /><br /><code><br />public void Write(string filename)<br />       {<br />           XmlTextWriter xmlWriter = new XmlTextWriter(filename, System.Text.Encoding.UTF8);<br />           xmlWriter.Formatting = Formatting.Indented;<br />           xmlWriter.WriteStartDocument();<br /><br />           xmlWriter.WriteStartElement("Sample");<br /><br />           //Equipments<br />           xmlWriter.WriteStartElement("Equipments");<br /><br />           foreach (string etype in equipments.keys)<br />           {<br />               xmlWriter.WriteStartElement("equipment");<br />               xmlWriter.WriteAttributeString("type", etype);<br /><br />               foreach (Equipment e in equipments[etype])<br />               {<br />                   //<point x="100" y="100"><br />                   xmlWriter.WriteStartElement("point");<br />                   xmlWriter.WriteAttributeString("x", e.Point.X.ToString());<br />                   xmlWriter.WriteAttributeString("y", e.Point.Y.ToString());<br /><br />                   xmlWriter.WriteStartElement("title");<br />                   xmlWriter.WriteString(e.Title);<br />                   xmlWriter.WriteEndElement();<br /><br />                   //<image />filename<br />                   xmlWriter.WriteStartElement("image");<br />                   xmlWriter.WriteString(e.Image);<br />                   xmlWriter.WriteEndElement();<br /><br />                   //<description>mirador</description><br />                   xmlWriter.WriteStartElement("description");<br />                   xmlWriter.WriteString(e.Description);<br />                   xmlWriter.WriteEndElement();<br /><br />                   xmlWriter.WriteEndElement(); // End point<br />               }<br />               xmlWriter.WriteEndElement(); //End equipment<br />           }<br /><br />           xmlWriter.WriteEndElement(); //End Equipments<br /><br />           xmlWriter.WriteEndElement(); //End Sample<br /><br />           xmlWriter.Flush();<br />           xmlWriter.Close();<br /><br />       }<br /></code>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com3tag:blogger.com,1999:blog-464513392335827691.post-8847415789706576892010-02-02T09:44:00.000-08:002013-08-31T06:33:47.554-07:00Conectarse con el puerto Serie/paralelo desde JavaBien pues para esta tarea existe una librería llamada Java Comunication API. Pero hay que tener en cuenta que para comunicarte a través de algo físico de la máquina Java nos proporciona una fachada de métodos que por debajo se conectan con diferentes drivers según si estamos corriendo el programa en un windows, macos o linux. Esto quiere decir que además de tener la máquina virtual de java instalada tendrás que instalar un driver en el sistema operativo. Yo empece a trabajar con la implementación de Sun pero me dió muchos problemas y no conseguía que me detectara los drivers instalados, así que finalmente opte por usar una implementación de Java Comunication API open source llamada RXTX. Con ella fue todo rodado por lo que os recomiendo que la useís.<br />
<br />
Su página web es <a href="http://rxtx.qbang.org/">http://rxtx.qbang.org/</a><br />
<br />
La instalación es muy simple os bajais la librería en la que os viene:<br />
<br />
- RXTXcomm.jar (librería que implementa la especificación de Java Comunication API)<br />
- Directorio con drivers para Linux<br />
- Directorio con drivers para Solaris<br />
- Directorio con drivers para Mac_OS_X<br />
- Directorio con drivers para Windows<br />
<br />
<br />
Añadis RXTXcomm.jar como librería a vuestro proyecto java y el driver lo meteis en la instalación del jre\bin<br />
<br />
Ahora la implementación de una conexión al puerto serie.<br />
<br />
<code><br />public class EjemploRXTX {<br /> static CommPortIdentifier portId;<br /> static CommPortIdentifier saveportId;<br /> static Enumeration portList;<br /> static InputStream inputStream;<br /> static OutputStream outputStream;<br /> static BufferedInputStream bufferedInputStream;<br /> static SerialPort serialPort;<br /> <br /> <br /> public static void main(String[] args){<br /> boolean gotPort = false;<br /> String port;<br /> portList = CommPortIdentifier.getPortIdentifiers();<br /> <br /> while (portList.hasMoreElements()) {<br /> portId = (CommPortIdentifier) portList.nextElement(); //get next port to check<br /> if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {<br /> if ( portId.getName().equals("COM4") ) {<br /> port = portId.getName(); <br /> gotPort = true;<br /> }<br /> <br /> if (gotPort == true) {<br /> try {<br /> serialPort = (SerialPort)portId.open("SMSSender", 2000);<br /> } catch (PortInUseException ex) {<br /> ex.printStackTrace();<br /> }<br /> try {<br /> outputStream = serialPort.getOutputStream();<br /> } catch (IOException ex) {<br /> ex.printStackTrace();<br /> }<br /> try {<br /> serialPort.setSerialPortParams(19200,<br /> SerialPort.DATABITS_8,<br /> SerialPort.STOPBITS_2,<br /> SerialPort.PARITY_NONE<br /> );<br /> } catch (UnsupportedCommOperationException ex) {<br /> ex.printStackTrace();<br /> }<br /> <br /> try {<br /> inputStream = serialPort.getInputStream();<br /> bufferedInputStream = new BufferedInputStream(inputStream);<br /> } catch (IOException e) {<br /> e.printStackTrace();<br /> }<br /> <br /> serialPort.notifyOnDataAvailable(true);<br /> }<br /> }<br /> }<br /> <br /> //Escribir en el puerto serie<br /> try {<br /> if (!(outputStream == null))<br /> outputStream.write("array de váis que queremos enviar");<br /> <br /> byte[] readBuffer = new byte[1];<br /> boolean read = false;<br /> while(!read) {<br /> try {<br /> String getInfo = "";<br /> Thread.sleep(100);<br /> while (bufferedInputStream.available() > 0) {<br /> int numBytes = bufferedInputStream.read(readBuffer);<br /> getInfo += new String(readBuffer);<br /> read = true;<br /> }<br /> feedback += getInfo;<br /> int length = getInfo.length();<br /> } catch (Exception e){<br /> e.printStackTrace();<br /> }<br /> }<br /> } catch (IOException e){<br /> e.printStackTrace();<br /> }<br /> }</code><br />
<br />
<br />
En este ejemplo se usa por debajo una plataforma windows como podéis ver en el nombre del puerto "COM4" en un linux por ejemplo este sería algo como "/dev/ttyS0".<br />
<br />
Tambíen para cada caso teneis que tener en cuenta que los datos de conexión con el dispositivo son particulares a cada dispositivo:<br />
<br />
<code><br />serialPort.setSerialPortParams(19200,<br /> SerialPort.DATABITS_8,<br /> SerialPort.STOPBITS_2,<br /> SerialPort.PARITY_NONE<br /> );</code><br />
<br />
El dispositivo de este ejemplo trabajaba a 19200 baudios, tamaño de mensaje 8 bits, sin paridad y con el bit de parada 2.<br />
<br />
Si ajustais estas cosas ya os debe funcionar para cualquier dispositivo.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com37tag:blogger.com,1999:blog-464513392335827691.post-26282474033919219802010-02-02T09:31:00.000-08:002010-02-02T09:37:54.495-08:00Enviar email desde Java 2º partePara usar el SMTP de google y nuestra cuenta de gmail tendremos que modificar el ejemplo anterior ya que google usa encriptación por SSL para la autenticación. A continuación el como se haría:<br /><br /><code><br />public class Main {<br />    <br />    private static final String SMTP_HOST_NAME = "smtp.gmail.com";<br />    private static final String SMTP_PORT = "465";<br />    private static final String emailMsgTxt = "Esto sería el mensaje";<br />    private static final String emailSubjectTxt = "Aqui el asunto";<br />    private static final String emailFromAddress = "micuenta@gmail.com";<br />    private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";<br />    private static final String[] sendTo = { "Email destino"};<br />    <br />    <br />    public static void main(String args[]) throws Exception {<br />        <br />        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());<br />        <br />        new Main().sendSSLMessage(sendTo, emailSubjectTxt,emailMsgTxt, emailFromAddress);<br />        System.out.println("Sucessfully Sent mail to All Users");<br />    }<br />    <br />    public void sendSSLMessage(String recipients[], String subject,String message, String from) throws MessagingException {<br />        boolean debug = true;<br />        <br />        Properties props = new Properties();<br />        props.put("mail.smtp.host", SMTP_HOST_NAME);<br />        props.put("mail.smtp.auth", "true");<br />        props.put("mail.debug", "true");<br />        props.put("mail.smtp.port", SMTP_PORT);<br />        props.put("mail.smtp.socketFactory.port", SMTP_PORT);<br />        props.put("mail.smtp.socketFactory.class", SSL_FACTORY);<br />        props.put("mail.smtp.socketFactory.fallback", "false");<br />        <br />        Session session = Session.getDefaultInstance(props,new javax.mail.Authenticator() {<br />            <br />            protected PasswordAuthentication getPasswordAuthentication() {<br />                return new PasswordAuthentication("micuenta@gmail.com", "micontraseñadegmail");<br />            }<br />        });<br />        <br />        session.setDebug(debug);<br />        <br />        Message msg = new MimeMessage(session);<br />        InternetAddress addressFrom = new InternetAddress(from);<br />        msg.setFrom(addressFrom);<br />        <br />        InternetAddress[] addressTo = new InternetAddress[recipients.length];<br />        for (int i = 0; i < recipients.length; i++) {<br />            addressTo[i] = new InternetAddress(recipients[i]);<br />        }<br />        msg.setRecipients(Message.RecipientType.TO, addressTo);<br />        <br />        <br />        msg.setSubject(subject);<br />        <br />        msg.setText(message);<br />        Transport.send(msg);<br />    }<br />}<br /></code>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com1tag:blogger.com,1999:blog-464513392335827691.post-33176784489432053352010-02-02T08:46:00.000-08:002010-02-02T09:29:13.811-08:00Como enviar emails desde JavaHace ya un tiempo que no escribo en el blog, pero es que últimamente he estado muy muy liado, lo bueno es que he tenido que trabajar en muchos proyectos lo cuál me permitirá escribir unos cuantos tutoriales este més.<div><br /></div><div> Para este primer tutorial he decidido poner un ejemplo de algo que puede parecer una tontería "enviar un correo electrónico desde Java" pero si no encuentra un buen ejemplo en la red puedes pegarte un buen rato. </div><div><br /></div><div>Lo primero debemos bajarnos la librería JavaMail.jar y activation.jar y añadirlos a nuestro proyecto.</div><div><br /></div><div><a href="http://java.sun.com/products/javamail/">http://java.sun.com/products/javamail/</a></div><div><a href="http://72.5.124.55/javase/technologies/desktop/javabeans/jaf/downloads/index.html">JavaBean activation Framework</a></div><div><br /></div>Nota. No preocuparse son dos jar de poco peso.<div><br /></div><div><br /></div><div>En principio creamos una clase para el envío de emails:</div><br /><br /><code><br />public class SendMail {<br />    <br />    private String from;<br />    private String to;<br />    private String subject;<br />    private String text;<br />    private ITracer tracer;<br />    <br />    public SendMail(String from, String to, String subject, String text){<br />        this.from = from;<br />        this.to = to;<br />        this.subject = subject;<br />        this.text = text;<br />        tracer = FileTracer.getInstance();<br />    }<br />    <br />    public void send(){<br />        <br />        Properties props = new Properties();<br />        props.put("mail.smtp.host", "ServidorSMTP");<br />        props.put("mail.smtp.port", "25"); //Suele ser este puerto<br />        props.put("mail.smtp.auth", "true");<br />                <br />        Session mailSession = Session.getDefaultInstance(props,new javax.mail.Authenticator() {<br />            <br />            protected PasswordAuthentication getPasswordAuthentication() {<br />                return new PasswordAuthentication("UsuarioSMTP",<br />                                                  "PasswordSMTP");<br />            }<br />        });<br />        <br />        Message simpleMessage = new MimeMessage(mailSession);<br />        <br />        InternetAddress fromAddress = null;<br />        InternetAddress toAddress = null;<br />        try {<br />            fromAddress = new InternetAddress(from);<br />            toAddress = new InternetAddress(to);<br />        } catch (AddressException e) {<br />            tracer.send(ITracer.INFO,e.getMessage());<br />        }<br />        <br />        try {<br />            simpleMessage.setFrom(fromAddress);<br />            simpleMessage.setRecipient(RecipientType.TO, toAddress);<br />            simpleMessage.setSubject(subject);<br />            simpleMessage.setText(text);<br />            <br />            Transport.send(simpleMessage);<br />            <br />            tracer.send(ITracer.INFO,"Email enviado a "+toAddress);<br />        } catch (MessagingException e) {<br />            tracer.send(ITracer.INFO,e.getMessage());<br />        }<br />    }<br />    <br />    public void setSubject(String subject)<br />    {<br />        this.subject = subject;<br />    }<br />    <br />    public void setText(String message)<br />    {<br />        this.text = message;<br />    }<br />}<br /></code><br /><br /><div><span class="Apple-style-span" style="font-family:monospace, serif;font-size:100%;"><span class="Apple-style-span" style="font-size:13px;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:monospace, serif;font-size:100%;"><span class="Apple-style-span" style="font-size:13px;"><span class="Apple-style-span" style=" ;font-family:Georgia, serif;font-size:16px;">Y por último sólo nos queda usarlo:</span></span></span></div><div><br /></div><div><br /></div><code><br />SendMail mail = new SendMail("FromEmail",<br />                                             "ToEmail",<br />                                             "Asunto",<br />                                             "Mensaje");<br />mail.send();<br /></code><br /><br /><div><span class="Apple-style-span" style="font-family:monospace, serif;font-size:100%;"><span class="Apple-style-span" style="font-size:13px;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:monospace, serif;font-size:100%;"><span class="Apple-style-span" style="font-size:13px;"><span class="Apple-style-span" style=" ;font-family:Georgia, serif;font-size:16px;">Un saludo y espero que os sea útil ;)</span></span></span></div><div><br /></div>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com2tag:blogger.com,1999:blog-464513392335827691.post-78437186443696613352009-09-27T23:42:00.000-07:002009-09-27T23:54:05.806-07:00Obtener objetos de la session en JSFOtro de los descubrimientos que he realizado tras trabajar en el último proyecto sobre JSF ha sido obtener instancias de objetos o variables creadas por el contenedor web. Como sabéis todos las clases java que utilices para trabajar con JSF (Backing bean, JavaBean...) deben estar dadas de alta en el faces-config.xml y deben de tener un constructor público sin argumentos. Esto es debido a que el contenedor de aplicaciones web (Tomcat, Glassfish ...) crearán instancias de estas clases para cada session (según el ambito especificado en faces-config.xml), esto plantea algunos problemas al querer usar singleton o simplemente al querer comunicar estas instancias de objetos ya que no existen referencias entre estas clases.<br /><br />Bien pues desde cualquier clase java puedes escribir las siguientes líneas y obtendrás el valor de los atributos de otra instancia de las clases creadas por el contenedor.<br /><span style="font-weight: bold;"><br />FacesContext fc = FacesContext.getCurrentInstance();<br /><br />String usr_id = (String)fc.getApplication().createValueBidding("#SessionScope.usr_id").getValue(fc);</span><br /><br />Espero que os sea de utilidad a mi me ha venido de perlas.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com1tag:blogger.com,1999:blog-464513392335827691.post-51151980292559987032009-09-25T05:42:00.001-07:002009-11-02T03:52:05.627-08:00Validator en JSFEste último més he estado trabajando en una aplicación sobre JSF, una tecnología que bajo mi punto de vista todavía le queda para ser madura, sobre todo por la cantidad de framework que puedes usar pero que luego mezclarlos es un verdadero dolor de cabezas. Despues de investigar ICEFace el cual me gustó mucho, ahora tenías que usar tiles para poder incluyectarle trozos y poder crear plantilla. Total que al final uso MyFaces (implementación de Apache de JSF) con Tomahawk que parece ser el más tolerante.<br /><br /><br />Entonces he descubierto muchas de las bondades de JSF entre ellas los validadores, unas clases java que te sirven para la validación de campos en los formularios, pudiendote olvidar del javascript. Esto me ha gustado mucho pero me encontre un escoyo que me gustaría ilustrar. Tenía un campo Año que quería validar. Bien pues cree el validador correspondiente:<br /><br /><code><br />public void validate(FacesContext facesContext, UIComponent uiComponent, Object object) throws ValidatorException {<br /><br /> try {<br /><br /> int anio = Integer.parseInt(object.toString());<br /><br /> if (anio < 1960 || anio > 2050) {<br /><br /> <br /><br /> FacesMessage message = new FacesMessage();<br /><br /> message.setSummary("Año "+anio+" no válido");<br /><br /> throw new ValidatorException(message);<br /><br /> }<br /><br /> } catch (Exception e) {<br /><br /><br /> FacesMessage message = new FacesMessage();<br /><br /> message.setSummary("Año "+object.toString()+" no válido");<br /><br /> throw new ValidatorException(message);<br /><br /> }<br /><br /> }<br /></code><br /><br /><br />Y lo dí de alta en el faces-config.xml<br /><code><br /> <validator><br /><br /> <validator-id>ValidaAnio</validator-id><br /><br /> <validator-class>Validadores.ValidaAnio</validator-class><br /><br /> </validator><br /></code><br />Ahora el problema es que al validar un campo:<br /><code><br /><t:outputText id="Anyo" value="Año(aaaa):"/><br /><br /> <t:inputText id="iAnyo" size="4" maxlength="4" value="#{ActasBusquedaBB.anyo}"><br /><br /> <f:validator validatorId="ValidaAnio"/><br /><br /> </t:inputText><br /></code><br /><br /><br />pues el campo dentro del formulario se quedaba relleno con el valor erroreo.<br /><br />Por ejemplo, si en el input anterior introduzco "abcd" como no es un año este falla en el validador<br />al hacer el casting a entero y sale el mensaje de "Año abcd no válido", pero no se borra el campo del formulario.<br /><br />La pregunta es ¿ Como hacer desde la clase java ValidaAnio que el campo del formulario se borre si el valor es incorrecto?<br /><br />Bien pues se hace en 2 lineas pero encontrarlas me costo sangre y por eso la pongo aquí.<br /><code><br />public void validate(FacesContext facesContext, UIComponent uiComponent, Object object) throws ValidatorException {<br /><br /> try {<br /><br /> int anio = Integer.parseInt(object.toString());<br /><br /> if (anio < 1960 || anio > 2050) {<br /><br /> if (uiComponent instanceof UIInput)<br /><br /> ((UIInput)uiComponent).setSubmittedValue("");<br /><br /> FacesMessage message = new FacesMessage();<br /><br /> message.setSummary("Año "+anio+" no válido");<br /><br /> throw new ValidatorException(message);<br /><br /> }<br /><br /> } catch (Exception e) {<br /><br /> if (uiComponent instanceof UIInput)<br /><br /> ((UIInput)uiComponent).setSubmittedValue("");<br /><br /> FacesMessage message = new FacesMessage();<br /><br /> message.setSummary("Año "+object.toString()+" no válido");<br /><br /> throw new ValidatorException(message);<br /><br /> }<br /><br /> }<br /></code><br /><br /><br />Espero que os sirva ;)Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com3tag:blogger.com,1999:blog-464513392335827691.post-41055436994717252882009-03-26T06:40:00.000-07:002009-03-26T06:43:24.909-07:00Trabajar con Certificados SSL en JavaEste artículo es muy útil cuando desde java queremos comunicarnos a través del protocolo seguro SSL. Este articulo lo encontre en el blob Apaga y vámonos y espero que os sea tan útil como me lo fue a mi.<br /><br />Cuando desarrollamos una aplicación en Java que va ha hacer uso de un recurso seguro (mediante un certificado), debemos asegurarnos que este certificado podrá ser validado por una entidad certificadora que reconozca Java. Debido a que Java es un lenguaje multi-plataforma, no utiliza la información de certificados alojada en el Sistema Operativo.<br /><br />Por defecto, la máquina virtual de Java dispone de las Entidades Certificadoras (CA) más comunes, como Verisign o Thawte. Sin embargo, suele darse el caso, sobretodo en entornos de desarrollo, que necesitemos utilizar una Entidad Certificadora "de prueba". En este caso, debemos importar esta CA en el almacén de claves de la máquina virtual que estemos utilizando.<br /><br /><br /><span style="font-weight: bold;">Importar Certificado en el almacén de certificados (keystore)</span><br /><br /><br />La máquina virtual de Java (JVM) cuenta con un almacén de claves (keystore) que incorpora las entidades más habituales y la posibilidad de agregar aquellas que nos sean necesarias. El keystore se encuentra en la ruta: JVM_PATH\lib\security\cacerts. Por ejemplo:<br /><br /><br />C:\Archivos de programa\Java\jre1.6.0_05\lib\security\cacerts<br />C:\Archivos de programa\Java\jdk1.5.0_15\jre\lib\security<br /><br /><br />Para añadir una nueva entidad certificadora a la JVM que estemos utilizando, debemos utilizar el comando keytool (JVM_PATH\bin\keytool):<br /><br /><br /> keytool -import -keystore "C:\Archivos de<br /> programa\Java\jre1.6.0_05\lib\security\cacerts" -file<br /> c:\NuevaEntidadCertificadora.cer -alias CA_SwitchOffAndLetsGo -storepass<br /> changeit<br /><br /><br /><br /><br />Puede observarse que el almacén de certificados contiene la contraseña por defecto 'changeit'. El nombre que especifiquemos en el alias, debe ser único en el keystore y servirá de referencia futura en el almacén. Podemos listar los certificados instalados utilizando la opción list del keytool:<br /><br /><br /> keytool -list -keystore "C:\Archivos de<br /> programa\Java\jre1.6.0_05\lib\security\cacerts" -storepass changeit<br /><br /><br /><br /><br /><span style="font-weight: bold;">Importar Certificado utilizando el panel de control de Java (Windows)</span><br /><br /><br />Atención: con esta opción únicamente se añadirán los certificados a la instalación activa de la JVM en Windows.<br /><br /><br />Para añadir la nueva entidad, se accederá al Panel de Control de Windows, y se seleccionará la opción "Java". Seguidamente se marcará la pestaña "Seguridad" y se pulsará sobre el botón "Certificados".<br /><br /><br />En el apartado de Certificados, se seleccionará la opción "Importar" y se localizará el archivo que contiene el certificado (si el certificado tiene extensión .cer se tendrá que seleccionar la opción 'Todos los archivos'). El nuevo certificado aparecerá en la pestaña "Usuario".Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-71385998354764604152009-02-11T23:43:00.000-08:002009-02-13T00:01:18.377-08:00Como hacer la descarga de ficheros con servletsPues este es un problema muy curioso, como sabéis los servlet devuelven un stream como respuesta. La configuración más usada para los servlet es que la respuesta sea de tipo html, que especificamos:<br /><br /><span style="font-style: italic;">response.setContentType("text/html");</span><br /><br />Pero el contenido de la respuesta lo podemos cambiar a cualquier otro Mime type:<br /><br /><a href="http://www.webmaster-toolkit.com/mime-types.shtml">http://www.webmaster-toolkit.com/mime-types.shtml</a><br /><br />Según esto podemos hacer que nuestro servlet devuelva cualquier tipo de fichero.<br /><br />En el siguiente ejemplo escribo un servlet capaz de enviarnos un fichero zip de cualquier parte del disco del servidor. (Nota. esto hay que manejarlo con cuidado porque puede producir un hueco de seguridad gordisimo)<br /><br />package org.company.servlet.readfile;<br /><br /><br />import javax.servlet.*;<br />import javax.servlet.http.*;<br />import java.io.*;<br />import java.util.*;<br />import java.net.*;<br /><br /><br />/**<br /> * <p> This class handles Streaming Data Content</p><br /> **/<br />public class Readfile extends HttpServlet {<br /><br /> public void init(ServletConfig config) throws ServletException {<br /> super.init(config);<br /> }<br /><br /> /*<br /> * This Method Handles Post<br /> *<br /> * @param HttpServletRequest request<br /> * @param HttpServletResponse response<br /> */<br /> public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<br /> doGet(request, response);<br /> }<br /><br /> /*<br /> * This Method Handles Get<br /> *<br /> * @param HttpServletRequest request<br /> * @param HttpServletResponse response<br /> */<br /> public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<br /> String urlstr = null;<br /> UserBean user = (UserBean) request.getSession().getAttribute("user");<br /> try {<br /> response.reset();<br /> urlstr = request.getParameter("filename");<br /> ServletOutputStream sOutStream = response.getOutputStream();<br /> streamBinaryData(user.getName(), urlstr, sOutStream, response);<br /><br /><br /> } catch (Exception e) {<br /> e.printStackTrace();<br /> }<br /> }<br /><br /><br /> private void streamBinaryData(String username, String file, ServletOutputStream outstr, HttpServletResponse resp) throws IOException{<br /> String ErrorStr = null;<br /><br /> try {<br /> BufferedInputStream bis = null;<br /> BufferedOutputStream bos = null;<br /> String inFile = "/midirectorio/" + file;<br /> int tam = (int) new File(inFile).length();<br /> bis = new BufferedInputStream(new FileInputStream(inFile));<br /><br /> try { <br /> //Asignamos el tipo de fichero zip<br /> resp.setContentType("application/x-zip-compressed"); //Cualquier mime type<br /> //Seteamos el tamaño de la respuesta<br /> resp.setContentLength(tam);<br /> resp.setHeader("Content-Disposition", "attachment;filename=\"" + file + "\"");<br /> <br /> bos = new BufferedOutputStream(outstr);<br /><br /> // Bucle para leer de un fichero y escribir en el otro.<br /> byte[] array = new byte[1000];<br /> int leidos = bis.read(array);<br /> while (leidos > 0) {<br /> bos.write(array, 0, leidos);<br /> leidos = bis.read(array);<br /> }<br /><br /><br /> } catch (Exception e) {<br /> e.printStackTrace();<br /> ErrorStr = "Error Streaming the Data";<br /> outstr.print(ErrorStr);<br /> } finally {<br /> if (bis != null) {<br /> bis.close();<br /> }<br /> if (bos != null) {<br /> bos.close();<br /> }<br /> if (outstr != null) {<br /> outstr.flush();<br /> outstr.close();<br /> }<br /> }<br /><br /> } catch (Exception e) { <br /> System.out.println("Fichero no encontrado");<br /> resp.sendRedirect("fileNotFound.jsp");<br /> <br /> }<br /><br /> }<br /> }<br /><br />Con este servlet conseguimos que en el navegador aparezca la típica ventanita de "descargas" al hacer una llamada al servlet, que recibe como parámetro el nombre del fichero. Este es el motivo por el que debemos tener cuidado al usar esto ya que podríamos crear un hueco de seguridad y que el usuario final se pueda descargar cualquier fichero de nuestro servidor.<br /><br />Lo mejor sería tener un directorio controlado del que sacar los ficheros como en el ejemplo, o que se filtre el filename de entrada para saber que está entre los permitidos, etc ...Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com8tag:blogger.com,1999:blog-464513392335827691.post-65797505825854287662009-02-11T23:20:00.000-08:002009-02-11T23:37:44.438-08:00Configurar la red manualmente en UbuntuHace tiempo que no escribo en el blog, el motivo es que ando muy muy liado con el trabajo y no me ha dejado tiempo para escribir los artículos que tenía en mente. Pero hoy me ha pasado que al instalarme una máquina nueva con la ultima versión de ubuntu. Si uso la configuración gráfica de red, está no se porqué no se guarda, sólo está activa mientras esté el ordenador encendido pero al reiniciarlo toda la configuración se pierde.<br /><br />Bueno pues este artículo intenta explicar como configurarla manualmente para que se quede permanente ya que desde el UI no lo he conseguido.<br /><br />1 Paso:<br /><br /><span style="font-style: italic;">> vi /etc/network/interfaces</span><br /><br />El contenido debemos editarlo para que quede:<br /><br /><span style="font-style: italic;">auto lo</span><br /><span style="font-style: italic;">iface lo inet loopback</span><br /><br /><span style="font-style: italic;">iface eth0 inet static</span><br /><span style="font-style: italic;">address XXX.XXX.X.XXX</span><br /><span style="font-style: italic;">netmask 255.255.255.XXX</span><br /><span style="font-style: italic;">gateway XXX.XXX.X.XXX</span><br /><span style="font-style: italic;">nameserver XXX.XXX.X.XXX</span><br /><span style="font-style: italic;">domain midominio.es</span><br /><br /><span style="font-style: italic;">auto eth0</span><br /><br /><br />Esta configuración especifica:<br />address -> dirección ip<br />netmask -> máscara de red<br />gateway -> la puerta de enlace, suele ser la dirección ip del rooter<br />nameserver -> (opcional) dirección ip del servidor de dns, puedes poner tantos como servidores dns tengas<br />domain -> para configurar el dominio de red en el que te encuentras<br /><br /><br />Nota. Esta configuración está hecha para una conexión cableada por eso usa eth0<br /><br />Paso 2:<br /><br />Aunque en este fichero podemos especificar los dns, lo suyo es especificarlos en el fichero<br /><br /><span style="font-style: italic;">> vi /etc/resolv.conf</span><br /><br />El contenido del fichero debería ser:<br /><br /><span style="font-style: italic;">nameserver XXX.XXX.X.XXX</span><br /><br />Nota. Tantos nameserver como servidores de dns tengamos.<br /><br />Bueno pues con esto, tu configuración quedará permanetemente configurada, de hecho<br />si el NetworkManager (UI para la configuración de red de ubuntu) funcionara bien, en estos ficheros debe poner algo así como<br /><br /><span style="font-style: italic;"># Generated by NetworkManager</span><br /><br />Eso es todo.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-37775849802047017002008-10-20T09:25:00.000-07:002008-10-20T09:54:42.689-07:00JUnit 3.8.1 vs JUnit 4.xJUnit es una herramienta que nos ayuda en uno de los procesos más abandonados en el desarrollo software, que es la fase de pruebas. Esta fase no suele hacerse como se debería, sino que se suelen hacer pruebas a la vez que vamos desarrollando nuestro software. Pues bien, es importante tener una fase de pruebas en todo desarrollo y esta herramienta nos hace esta tarea mucho más sencilla. De hecho existen metodologías basadas en pruebas de modo que diseñas las pruebas antes que el software y vas desarrollando supervisado a través de los test.<br /><br /> Bueno pues JUnit es una herramienta que desde que se publicó su versión 3.8.1 ha quedado un poco congelada. Pero recientemente el proyecto a vuelto a cobrar vida con las nuevas versiones 4.x. Estas nuevas versión introducen algunos conceptos nuevos y hacen uso de las anotaciones introducidas en la versión 1.5 de java.<br /><br />A continuación, se listan las principales novedades de esta versión de JUnit respecto a versiones precedentes:<br /><ul><li>Es necesario utilizar la versión 5 de la JDK para ejecutar los tests.</li><li>Las clases de prueba no necesitan heredar de la clase junit.framework.TestCase.</li><li>Los métodos de inicialización y liberación pueden ser definidos con cualquier nombre siempre y cuando sean etiquetados adecuadamente con @Before y @After. Además puede existir más de un método de inicialización y liberación simultaneamente.</li><li>Los nombre de los métodos de prueba no necesitan contener el prefijo test, sin embargo es necesario que sean definidos con la etiqueta @Test, la cual permite ser utilizada con parámetros que enriquecen las posibilidades de la prueba.</li><li>Existe la posibilidad de declarar métodos de inicialización y liberación globales a la clase de pruebas mediante las etiquetas @BeforeClass y @AfterClass.</li></ul>Y para terminar os pongo un mismo ejemplo desarrollado con las distintas versiones:<br /><br /><span style="font-weight: bold;">JUnit3.8.1</span><br /><br />package pruebasSistemaSoftware.junit381;<br /><br />import junit.framework.*;<br />import servidorEstadoTrafico.Registro;<br />import servidorEstadoTrafico.RegistroMalFormadoException;<br />import servidorEstadoTrafico.Tramo;<br /><br />public class RegistroTest extends TestCase {<br /><br /> private Registro m_registro;<br /><br /> public void setUp(){<br /> String strCarretera ="M-40";<br /> String strHora="12:23:45";<br /> String strFecha="1/3/2007";<br /> String strClima="Nublado";<br /> String strObras = "No";<br /><br /> m_registro = new Registro(strCarretera,strHora,strFecha,strClima,strObras);<br /> Tramo tramo1 = new Tramo("0","10","3","1","Retenciones","Sin accidentes");<br /> Tramo tramo2 = new Tramo("10","12","2","0","Retenciones","Sin accidentes");<br /> Tramo tramo3 = new Tramo("12","15","3","1","Retenciones","Sin accidentes");<br /> <br /> m_registro.anadirTramo(tramo1);<br /> m_registro.anadirTramo(tramo2);<br /> m_registro.anadirTramo(tramo3);<br />}<br /><br /> public void tearDown(){<br /><br /> }<br /><br /> public void testComprobarFormato(){<br /> try{<br /> m_registro.comporbarFormato();<br /> } catch (RegistroMalFormadoException e){<br /> fail("Se ha originado una excepcion inesperada" + e.toString());<br /> }<br /> }<br /><br /> public void testObtenerLongitud(){<br /> assertEquals(m_registro.obtenerLongitud(),10+2+3);<br /> }<br /><br /><br /><br /><span style="font-weight: bold;">JUnit 4.x</span><br /><br />package pruebasSistemaSoftware.junit4.2;<br /><br />import java.lang.*;<br />import java.util.*;<br /><br />import org.junit.Test;<br />import org.junit.After;<br />import org.junit.Before;<br />import static org.junit.Assert.*;<br />import junit.framework.JUnit4TestAdapter;<br /><br />import servidorEstadoTrafico.Registro;<br />import servidorEstadoTrafico.RegistroMalFormadoException;<br />import servidorEstadoTrafico.Tramo;<br /><br />public class RegistroTest{<br /><br /> private Registro m_registro;<br /><br /> @Before public void inicializar(){<br /> String strCarretera = "M-40";<br /> String strHora="12:23:45";<br /> String strFecha="1/3/2007";<br /> String strClima="Nublado";<br /> String strObras = "No";<br /><br /> m_registro = new Registro(strCarretera,strHora,strFecha,strClima,strObras);<br /> Tramo tramo1 = new Tramo("0","10","3","1","Retenciones","Sin accidentes");<br /> Tramo tramo2 = new Tramo("10","12","2","0","Retenciones","Sin accidentes");<br /> Tramo tramo3 = new Tramo("12","15","3","1","Retenciones","Sin accidentes");<br /> <br /> m_registro.anadirTramo(tramo1);<br /> m_registro.anadirTramo(tramo2);<br /> m_registro.anadirTramo(tramo3);<br /><br /> }<br /><br /> @After public void liberar(){<br /> }<br /><br /> @Test(expected=RegistroMalFormadoException.class)<br /> public void comprobarFormato() throws RegistroMalFormadoException {<br /> m_registro.comprobarFormato();<br /> }<br /><br /> @Test(timeout=1000)<br /> public void obtenerLongitud(){<br /> assertEquals(m_registro.obtenerLongitud(),10+2+3);<br /> }<br /><br />}<br /><br />Ejemplos extraidos del libro "Pruebas de Software y JUnit Un análisis en profundidad y ejemplos prácticos"Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com1tag:blogger.com,1999:blog-464513392335827691.post-32096214068969129502008-10-20T08:39:00.000-07:002008-10-20T09:22:51.338-07:00Plantilla AntBueno como todos ya conoceréis Ant es una herramienta desarrollada integramente en java y que sirve para automatizar el proceso de compilación, despliegue, generación de documentación etc ... Estas y otras muchas son tareas que debemos repetir multiples veces. Bueno pues esta herramienta ha sido pensada de forma que tu describes un fichero xml que por norma general suele llamarse build.xml, en el que describes los pasos que quieres realizar una vez hayas modificado el código. De esta forma podremos desplegar nuestra aplicación tantas veces como queramos en el servidor bajando el fuente desde un ftp o un subversion y todo ejecutando en el directorio el comando "ant" (seguido del nombre del fichero de descripción en el caso de que no sea build.xml).<br /><br />Bueno pues voy a poneros una plantilla que va a recoger todas estas tareas:<br /><br />-<span style="font-weight: bold;">Limpiar:</span> Se eliminan los directorios que contienen las clases compiladas y la documentación que pudo ser generrada anteriormente. Se eliminan ambos a la vez para que no exista una versión de documentación que no se corresponda con las clases compiladas y viceversa.<br /><br />-<span style="font-weight: bold;">Compilar</span>: En la primera de sus tareas se crea, si no existe, el directorio donde se guardarán las clases compiladas. El directorio no existirá porque se ha eliminado con el objetivo limpiar, del cual depende compilar. La segunda tarea compila las clases, situadas en ${src} y almacena los resultados de la compilación en el directorio ${build}. Para ello utiliza el classpath definido en la propiedad ${classpath}.<br /><br />-<span style="font-weight: bold;">Jar</span>: Se genera un archivo .jar con todas las clases presentes een el directorio ${destino} (build). Esta tarea depende del objetivo compilar para garantizar que existan clases compiladas en este directorio antes de intentar generar el archivo .jar.<br /><br />-<span style="font-weight: bold;">Documentar</span>: En la primera tarea se crea, si no existe, el directorio donde se guardará la documentación generada. El directorio no existirá porque se ha eliminado con el objetivo limpiar, del cual depende compilar. La segunda tarea genera la documentación del proyecto.<br /><br />-<span style="font-weight: bold;">Main</span>: Se ejecutan todos los objetivos para crear el programa y dejarlo listo para ser ejecutado.<br /><br /><project name="Proyecto_basico" default="main" basedir="."><br /><br /> <!-- propiedades globales del proyecto --><br /> <!-- definición de directorios que se van a usar--><br /> <property name="src" value="."/><br /> <property name="build" value="build"/><br /> <property name="doc" value="doc"/><br /><br /> <!-- caracteristicas del proyecto--><br /> <property name="classpath" value="./lib/misclases.jar"/><br /><br /> <target name="limpiar"><br /> <delete dir="$"/><br /> <delete dir="${doc}"/><br /> </target><br /><br /> <target name="compilar" depends="limpiar"><br /> <mkdir dir="${build}"/><br /> <javac srcdir="${src}" destdir="${build}" classpath="${classpath}"/><br /> </target><br /><br /> <target name="jar" depends="compilar"><br /> <jar jarfile="clases.jar" basedir="${build}" includes="**"/><br /> </target><br /><br /> <target name="documentar" depends="limpiar"><br /> <mkdir dir="${doc}"/><br /> <javadoc packagenames="proyectobasico" sourcepath="${src}" destdir="${doc}"/><br /> </target><br /><br /> <target name="main" depends="compilar,documentar"><br /> </target><br /><br /></project><br /><br />Bueno espero que os sirva para describir vuestros proyectos si en algún momento no recordáis la sintaxis de Ant.Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-32472959909491639442008-10-02T01:28:00.000-07:002008-10-02T01:33:03.991-07:00Comprimir y descomprimir en LinuxComo siempre los artículos que publico son a modo de recordatorio, de forma que este blog es como una guía para el día a día. Hoy he encontrado un artículo en (http://stolz.gsmlandia.com), que creo muy interesante y necesario, descomprimir y comprimir ficheros en linux desde línea de comandos.<br /><br />Comprimir y descomprimir archivos en Linux desde la línea de comandos es algo habitual y siempre viene bien tener a mano un pequeño resumen de cómo hacerlo. <h2><span style="font-size:130%;">Los típicos<br /></span></h2> <h2>Ficheros .tar</h2> <span class="comando">tar</span> empaqueta varios archivos en uno solo, pero no comprime. <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n tar</span></p></li><li><p>Empaquetar: <span class="comando"># tar cf archivo.tar ficheros</span></p></li><li><p>Desempaquetar: <span class="comando"># tar -xvf archivo.tar</span></p></li><li><p>Ver contenido: <span class="comando"># tar -tf archivo.tar</span></p></li></ul> <h2>Ficheros .gz</h2> <span class="comando">gzip</span> sólo comprime fichero a fichero, no empaqueta varios ficheros en uno ni comprime directorios. <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n gzip</span></p></li><li><p>Comprimir: <span class="comando"># gzip fichero</span></p></li><li><p>Descomprimir: <span class="comando"># gzip -d fichero.gz</span></p></li></ul> <h2>Ficheros .bz2</h2> <span class="comando">bzip2</span> sólo comprime fichero a fichero, no empaqueta varios ficheros en uno ni comprime directorios. <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n bzip2</span></p></li><li><p>Comprimir: <span class="comando"># bzip2 fichero</span></p></li><li><p>Descomprimir: <span class="comando"># bzip2 -d fichero.bz2</span></p></li></ul> Para comprimir varios ficheros y archivarlos en uno solo, al estilo de los compresores <span class="comando">zip</span> o <span class="comando">rar</span> hay que combinar <span class="comando">tar</span> con <span class="comando">gzip</span> o con <span class="comando">bzip2</span> como muestro a continuación. <h2>Ficheros .tar.gz</h2> <ul><li><p>Comprimir: <span class="comando"># tar -czf archivo.tar.gz ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># tar -xvzf archivo.tar.gz</span></p></li><li><p>Ver contenido: <span class="comando"># tar -tzf archivo.tar.gz</span></p></li></ul> <h2>Ficheros .tar.bz2</h2> <ul><li><p>Comprimir: <span class="comando"># tar -c ficheros | bzip2 > archivo.tar.bz2</span></p></li><li><p>Descomprimir: <span class="comando"># bzip2 -dc archivo.tar.bz2 | tar -xv</span></p></li><li><p>Ver contenido: <span class="comando"># bzip2 -dc archivo.tar.bz2 | tar -t</span></p></li></ul> <h2>Ficheros .zip</h2> <ul><li><p>Instalar las herramientas: <span class="comando"># emerge -n zip unzip</span></p></li><li><p>Comprimir: <span class="comando"># zip archivo.zip ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># unzip archivo.zip</span></p></li><li><p>Ver contenido: <span class="comando"># unzip -v archivo.zip</span></p></li></ul> <h2>Ficheros .rar</h2> <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n rar</span></p></li><li><p>Comprimir: <span class="comando"># rar a archivo.rar ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># rar x archivo.rar</span></p></li><li><p>Ver contenido: <span class="comando"># rar l archivo.rar</span> o <span class="comando"># rar v archivo.rar</span></p></li></ul> <h2><span style="font-size:130%;">Los no tan típicos</span></h2> <h2>Ficheros .lha</h2> <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n lha</span></p></li><li><p>Comprimir: <span class="comando"># lha a archivo.lha ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># lha x archivo.lha</span></p></li><li><p>Ver contenido: <span class="comando"># lha v archivo.lha</span> o <span class="comando"># lha l archivo.lha</span></p></li></ul> <h2>Ficheros .arj</h2> <ul><li><p>Instalar las herramientas: <span class="comando"># emerge -n arj unarj</span></p></li><li><p>Comprimir: <span class="comando"># arj a archivo.arj ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># unarj archivo.arj</span> o <span class="comando"># arj x archivo.arj</span></p></li><li><p>Ver contenido: <span class="comando"># arj v archivo.arj</span> o <span class="comando"># arj l archivo.arj</span></p></li></ul> <h2>Ficheros .zoo</h2> <ul><li><p>Instalar la herramienta: <span class="comando"># emerge -n zoo</span></p></li><li><p>Comprimir: <span class="comando"># zoo a archivo.zoo ficheros</span></p></li><li><p>Descomprimir: <span class="comando"># zoo x archivo.zoo</span></p></li><li><p>Ver contenido: <span class="comando"># zoo L archivo.zoo</span> o <span class="comando"># zoo v archivo.zoo</span></p></li></ul> Ahora que sabes como compimir directorios, recuerda hacerte una copia de seguridad de tu sistema :)Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0tag:blogger.com,1999:blog-464513392335827691.post-15517400684411545632008-09-29T23:46:00.000-07:002008-09-29T23:48:37.125-07:00Configuración de SSH en debian<div class="entry"> <p>Lo primero que hacemos es instalar el SSH</p> <pre>#apt-get install ssh</pre> <p>Luego de la instalación buscamos el archivo de configuración sshd_config que esta en</p> <pre># vi /etc/ssh/sshd_config<br /><br />(Nota. no confundir con el fichero ssh_config)<br /></pre> <p>Agregamos o modificamos las siguientes entradas en el archivo</p><p><br /><strong>Parámetro Port</strong><br />Si fuera necesario por seguridad se puede cambiar el puerto por el cual escucha el SSH</p> <pre>Port 54321</pre> <p><strong>Parámetro ListenAddress</strong><br />Especificamos bajo que direcciones intefaces responderá las peticiones</p> <pre>ListenAddress 10.0.0.5</pre> <p><strong>Parámetro PermitRootLogin</strong><br />Especificamos si el superusuario podrá conectarse mediante SSH</p> <pre>PermitRootLogin no</pre> <p><strong>Parámetro X11Forwarding</strong><br />Especificamos si es necesario que se ejecuten aplicaciones gráficas mediante SSH</p> <pre>X11Forwarding no</pre> <p><strong>Parámetro AllowUsers</strong><br />Especificamos que usuarios se conectarán mediante SSH</p> <pre>AllowUsers espiridion, cascajo, pully</pre> <p>Tambien se puede restringir el acceso por usuario y host</p> <pre>AllowUsers espiridion@192.168.0.55 cascajo@192.168.5.22</pre> <p>Reiniciamos el SSH</p> <pre>#/etc/init.d/ssh restart</pre> </div>Jorge Cantón Ferrerohttp://www.blogger.com/profile/17843977389818051312noreply@blogger.com0