lunes, 15 de agosto de 2011

Geo typical texture

Geo typical texture

Hace un tiempo, en una entrevista me preguntarón por si conocía lo que era el geo type texturing. Basicamente es realizar la textura dependiendo del tipo de objeto que se encuentre en el terreno. Por ejemplo, si queremos realizar la textura de una ciudad, tendremos que saber que tipo de objetos hay en el terreno para aplicar la textura (edificios, parques,..)

Pues bien, al igual que me ocurrio con el simulador de tráfico que en su día me comento mi profesor de programación en cuando hice Equipos Informáticos, y desarrolle uno usando lógica difusa, he creado un pequeño proyecto, citytex, donde el algoritmo realiza lo siguiente:

- Genera un esqueleto de una ciudad en función del número de calles horizontales y verticales.
Aleatoriamente, las calles se inclinan en un sentido o en otro para darle un poco de realismo y no sea una cuadricula perfecta.
Y finalmente se insertan un número de edificios.

- Genera un bitmap con con la información de los objetos que se encuentran. En este paso, solo sabemos las carreteras (en azul) y los edificios(en amarillo).

- Despues, se pinta con el raton las zonas donde queremos que exista una acera, o un parque. Esto se podría sustituir con un algoritmo de rellenado de zonas, pero eso sería otro proyecto.

En el transcurso de su implementación, me he dado cuenta de algunas cosas que ya desarrollare en siguientes post, pero como adelanto, se refiere a la liberación de memoria y al pseudo-código.

La dificultad de este proyecto a radicado a la hora de texturizar las carreteras y generar el grafo interno. Se ha pensado de esta forma, ya que así, representándolo con un grafo, se podría con algoritmos de grafos, determinar que zonas podrían ser rellenadas con unos rascacielos y otras con casas más bajas. Los nodos mejor comunicados estarían rodeados de rascacielos y los de a las afueras, con texturas de viviendas.

Aunque mirando la textura que genera, posiblemente no sea muy realista, pero podría servir de base para futuras versiones. Por ejemplo, que el algoritmo tomase la altura de cada punto y la tomase encuenta para crear la textura.

Uno de los problemas de usar fotos aereas como texturas es su resolución, que debe ser muy alta. Cuando en un simulador de vuelo se está cerca del terreno, la textura, si tiene poca resolución se empieza a pixelar y aparece como no real, quejándose así los pilotos. Una solución para esto, es la creación de texturas proceduralmente, como en este ejemplo, aunque claro, queda mucho, mucho por mejorar, pero ha estado bien e interesante aproximarse un poco a la creación de este tipo de texturas.

Para bajar el programa aquí.




algunos links sobre este tema.

http://www.helistart.com/addons.aspx

http://ict.usc.edu/projects/military_terrain_for_games_pipeline/

PROCEDURAL TEXTURING
Most input datasets generally contain only geometry information, while some may contain rudimentary textures. The MTGP pipeline improves upon the dataset, procedurally applying geo-typical textures (dirt or asphalt, brick or stone sand, grass or concrete) to incoming geometry (roads, buildings and terrain).

jueves, 7 de julio de 2011

El Valor del Software

El Valor del Software

En todos los desarrollos software, hay un tira y afloja con el usuario sobre lo que tiene que hacer el sistema. Una de estas fuentes, de estos tiras y aflojas, son la complejidad de los requisitos software que pide el usuario.

En estas situaciones, en mi opinión, hay que tener cuidado, ya que, si se tira mucho por parte del equipo de desarrollo sobre un requisito, es posible que el usuario, devalue la aplicación tanto, que no quede contento.
Si el usuario está solicitando un requisito que para el es de un gran valor para su trabajo, y finalmente, se modifica tanto ese requisito. Ese requisito no va a portar el valor que esperaba el usuario.
Esto me recuerda a un programilla que desarrolle para una amiga abogada. Este programa contaba a partir de una fecha y un plazo, el día en que vencía ese plazo (dies ad quem). Programar esto, es fácil, pero el valor para el usuario era grande. Pero al poco de ver como la aplicación calculaba el día que vencía el plazo, quiso que el programa se comunicara con su calendario de Google, de forma que le enviase un aviso por correo un día antes de que cumpliera.

Esto se vio pronto que era de un gran valor añadido, ya que ahorraba tiempo, y así, sería más díficil que un plazo se pasase. Pero había una complejidad técnica. Tenía que mirar la API de google. Entonces, tenía dos opciones (al menos en ese momento pensé):

a) Convencer al usuario para que la herramienta  implementara un envio de correo a su cuenta de correo y asi, lo tendría almacenado. La complejidad técnica es menor, ya que en .net esto se hace con unas cuantas lineas.

b) Mirar la API de google y tratar de comunicar el programa, CuentaPlazos, con el calendario de Google.
La opción a) deja al usuario sin centralidad en su calendario toda su actividad. Corriendo el riesgo de bajar el valor de la aplicación, y por consiguiente su uso.

La opción b) , aunque cuesta algo más de tiempo, añade valor a la aplicación, y así lo percibe el usuario.
Con este ejemplo creo que el identificar y mantener los requisitos con más valor para el usuario son fundamentales en el desarrollo software, ya que si no, es posible que no use el futuro software.

En el caso del CuentaPlazos, es posible, que si muestro un calendario con colorines de forma muy vistosa, pero luego, no se comunica con el calendario de google, la aplicación perdería valor para el usuario ya que no se ha implementado un requisito para él fundamental.

Y es que, en definitiva, un software que pide un cliente, debe de satisfacer a dicho cliente, y en el desarrollo habrá que poner los medios para alcanzar dicho objetivo.


sábado, 2 de julio de 2011

Codificando para depurar

Codificando para depurar


En la codificación es muy normal al codificar una función o método de una clase, esperar que funcione como debe de funcionar. Creo que esto es debido a que cuando implementamos un método, sabemos desde donde va a ser llamado y sabemos perfectamente cual será el escenario de esa llamada. Por ejemplo, si estoy implementando.

void CTriangle::create(Point *pPoints);

Desde donde será llamada, siempre el puntero pPoints será distinto de null, y los tres puntos serán distintos. Ya que algoritmo de creación de triangulos, si funciona al 100% correctamente, pasará siempre un array de puntos correctos.


¿Pero que pasa, si ese algoritmo tiene algún bug? crearemos algún triangulo incorrectamente y entonces, en pantalla o no se verán o veremos cosas raras en la pantalla.


Aquí, en este punto, es donde es el motivo de este post. El codificar para depurar. Podemos tener, una función de chequeo dentro del método Create para que inmediatamente que se cree un triangulo, comprobar que es correcto, y si no lo es, hacer saltar un assert. De esta forma, se va, SIN perdida de tiempo, al fallo y no gastar un tiempo precioso en depurar. 
Cierto, esto ralentiza la ejecución, pero después, cuando no salta ningún assert, estás comprobaciones, se pueden comentar en código.


Un ejemplo de ello. Cuando estaba implementando la "Triangulación Restringida de Delaunay", tenía este método que llamaba cada vez que creaba un objeto de la clase CTriangle.



// cheque que el triangulo está construido correctamente
// true si tiene algun error.
bool CTriangle::checkError(void)
{
bool bError = false;


if (isRepeatVertex())
bError = true;
if (isRepeatVertexPoint())
bError= true;
if (checkAdyacents())
{
bError = true;
}


if ( !bError )
{
//TRACE("---------------- ERROR TRIANGULO ------------\n");
//debugView();
}



return bError;
}


checkError comprobaba que el triangulo estaba bien construido, tanto en sus indices al array de puntos, como que los puntos eran distintos entre sí, y en sus triangulo adyacentes. De esta forma, al saltar este chequeo, era un signo de que algo mal estaba en el algoritmo de Triangulación.

Otra forma que ayuda para depurar, es identificar de forma única a cada objeto que se crea. De esta forma, puedes trazar la evolución de un objeto y centrarte en el solamente. Por ejemplo, en CTriangle tenía:

int m_id;

Así, si saltaba el método checkError, con el id del triangulo, podía centrarme en como se creaba este triangulo y su relación con sus adyacentes.


Con todo esto, creo que no debemos de pensar al codificar, que todo saldrá bien, sino debemos pensar, que algo irá mal y tendremos que depurar. Entonces, tendremos que codificar para que dicha depuración sea más fácil, eficaz y productiva.






Aquí, os dejo una demo de un programa que utiliza dicho algoritmo

jueves, 30 de junio de 2011

Rendimiento managed to native code

Rendimiento managed to native code

Leyendo un libro sobre c++/cli, c++/cli in Action, donde el autor hacía referencia de que realizar muchas transiciones entre código manejado y código no manejado (código nativo), haría a la aplicación más lenta.

Para comprobar esto, realize el siguente programilla que tiene dos funciones, callfromUnManaged y callfromManaged. Las dos funciones tienen un bucle que llaman a funciones nativas. CallfromUnManaged no hará ninguna transición, ya que es una función nativa,
pero callfromManaged si tendrá que realizar transiciones.

Visto lo anterior, parece de cajón que callfromManaged será más lenta. Pues sí y no!. Depende de la opciones del compilador y la complejidad de las transición (luego explico esto)
configuración 1:
En relase si tenemos el C++/C ==> Optimization a 'disabled', la función más rápida será callfromManaged.
En relase si tenemos el C++/C ==> Optimization a 'Maximize Speed (/O2)', la función más rápida será callfromUnManaged.
¿Cual sería la explicación? es posible que por defecto, el código IL esté optimizado y el código nativo no,  con lo cual para que optimize el código nativo hay que configurarlo explicitamente.

Lo de la complejidad de la transición de código manejado a nativo, fue por que si solamente se pasan enteros, algunos resultados me daban como que era más rápido el código manejado, pero cuando añadi como parametros el paso de punteros de los dos objetos Student y Teacher para la función:

int calculateSquareRoot(float dataValue,int nTimes,Student *pStudent,Teacher *pTeacher)
Esto hizo que se complicara un poco el código que el compilador tenía que generar para pasar de código manejado a nativo, dando como resultado que ahora, cuando se optimiza el código ('Maximize Speed (/O2)), el método 'callfromUnmanaged' es más rápido.

Unos resultados parecidos se pueden encontrar aquí:

http://www.grimes.demon.co.uk/dotnet/man_unman.htm


Aquí dejo el código fuente: