4.1.- Presentación y el rollete de siempre ;-)

¿Hace un buen día, verdad?... Después de 4 números del CPV, nos empiezan a faltar las palabras y los saludos. La verdad es que la frase inicial se suele emplear cuando no se sabe que decir, pero este no es nuestro caso. Todo lo contrario, tenemos tantas cosas que deciros que no sabemos por donde empezar.Bueno lo mejor será comenzar por el principio....

Bueno, pues un día un menda llamado Adan se encontró con una chorba.... ¿qué? ahhhhh, que me ido muy atras ¿verdad?... bueno me saltaré unos cuantos milenios X-)

A ver, durante la presentación del CPV-03 os comentabamos lo contentos que  estabamos por las colaboraciones que habíamos recibido, pues bien, nuestra  alegría se multiplica. Cada vez son más las personas que captan los mensajes y  las indirectas y se han decidido a salir del anonimato para echarnos una mano.En las dos formas de colaboración que propusimos hemos obtenido respuestas:

- Algunos (pocos) se han dado cuenta que desarrollar un curso de estas características supone una serie de gastos y esfuerzos que las pequeñas aportaciones al solicitar las lecciones por correo ayudan a mitigar.

- Otros tantos se han decidido a participar de una manera más activa y se han aventurado a realizar artículos propios, o bien, a desarrollar las tareas que proponemos en las lecciones.

Aunque parezca una paradoja estos últimos han sido los que más y esta circustancia ha provocado (aparte de nuestra alegría) dos situaciones, una  gratificante y otra alarmante:

- La buena es que debido al número de colaboraciones, nos vemos obligados a variar (para mejor) la planificación del CPV y a intentar incluir en las lecciones el máximo de artículos posibles.

Aprovecho para comunicar a todos los que nos han mandado programas y/o artículos y que no vean sus programas en esta lección, que esten tranquilos, los continuos ajustes a los temarios de las lecciones, nos llevan a retrasar/adelantar artículos, pero nunca a olvidarlos. ;-)

Sobre esta cuestión, puntualizaré que por el momento, no tenemos pensado aumentar el número de lecciones. Con las 6 que teníamos previstas parece ser  que vamos a poder dar cabida a todos los temas (iniciales y nuevos), pero en este aspecto vuestra acogida y opinión cuenta mucho. ;-)

La mala es que como nosotros somos gente de palabra, tenemos (y queremos) enviar las lecciones a todos los colaboradores y esto causa que nuestra balanza económica se decante (aun más) hacia el lado de los número rojos, parece mentira pero es así.

Esta situación nos ha obligado a replantearnos la forma de distribución de las lecciones. Ya en el número pasado nos reservamos la entrega de dos programas para así "estimular" la participación de la gente, pues bien, en este número os vamos a dar la estimulación total, uhhhhi que X ma'quedao.

A partir de ahora las lecciones del CPV saldrán a la luz pública peladitas de fuentes. La "única" forma de conseguir las lecciones completas será colaborando, bien con programas o bien solicitando las lecciones por correo.Sobre esta última forma de distribución ya os comentamos como funciona, pero de todos modos, leeros el apartado de distribución y condiciones que no está de más.

Otro asunto, en las encuestas que hemos recibido, algunas personas nos vuelven a recordar la lentitud del VCPV en la lectura de artículos.... MEA CULPA, MEA CULPA. Ya os comentamos el motivo de estos problemas, en un principio pensamos que podríais esperar unos segundos más mientras se cargan los artículos, pero vemos que no, que os gusta tanto el CPV que os mordéis las uñas esperando que desaparezca el cartel de Espera un momento...... Por este motivo anunciamos  que:

El VCPV va a dejar de existir...... IUUUUPIIIIIII. En teoría esta lección tenía que haber salido con un nuevo lector de artículos, pero no ha podido ser. Para el CPV-05 estamos preparando entre un grupo de personas un nuevo lector para el CPV, más rápido, más bonito, más completo.... y será entonces cuando le demos la patada definitiva al lector antiguo. ;-)

Estamos intentado que la compatibilidad sea total, para que podáis utilizar el nuevo lector con las lecciones antiguas, por lo que seguramente os facilitemos un conversor de lecciones.

Siguiente tema: en esta lección hemos renombrado el diccionario Traducci por Tareas, ya que a partir de ahora incluiremos en él todos esos programitas que nos manda la gente como "pequeñas" colaboraciones pero que tienen un gran valor añadido y por ello deseamos que aparezcan. En este número hemos incluido una traducción al C del programa del fuego virtual de la lección 3, realizada por Francisco Priego y un sencillo, pero muy útil conversor de artículos CPV a TXT, realizado por Cristobal Saraiba que servirá por ejemplo, para que todos aquellos que no tengán impresoras compatibles Epson puedan imprimir los artículos, como archivos de texto normales con sus editores
preferidos.

Ultimo asunto, en esta lección también hemos creado un nuevo directorio en el CPV llamado utiles, dónde incluiremos distintos programas (Shareware o Dominio Público) que consideramos os pueden interesar. Habrá dos tipo de programas principalmente, por un lado estarán utilidades relacionadas con el mundillo de la >programación de sistemas (del estilo de la PCGPE) y por otra parte programas de autores españoles que a lo mejor no tienen nada que ver con los videojuegos pero consideramos que son muy interesantes y que merecen ser conocidos por todo el mundo.

En las lecciones de libre distribución (las que van sin fuentes y se distribuyem por Fido e Internet) incluiremos solo pequeños programas por motivos de espacio en el directorio utiles, pero en las lecciones de distribución por correo (las completas) dejaremos el disco a reventar de programas interesantes y como no siempre programas Dominio Público y Share que no tengan explicitamente prohibida este tipo de distribución y (cuando sea posible) con petición previa de conformidad al autor.

Entre los programas incluidos estarán dos utilidades de Alfredo Huerta.

- CHSMANIA un lector de chistes con cantidad de funciones útiles como clasificación de chistes, impresión, formato de texto, volumenes ampliables y mucho más.

-AHEDITOR un visualizador de archivos textos original y muy potente.  Espero que os guste el estilo de este programa porqué el nuevo lector de artículos del CPV tendrá un funcionamiento muy parecido al mismo.

Después de contaros las últimas noticias del CPV, ale, darle duro al escape y al ataque.
 


4.2.- Colaboraciones y Condiciones.

Abónese

Como ya os hemos adelantado, hemos radicalizado la forma de distribución que comenzamos en el número pasado y a partir de ahora las lecciones que se distribuyan de manera pública no incluirán los programas fuentes. Esta decisión nos acerca casi milimétricamente a la definición pura de SHAREWARE.

En esos milímetros de distancia está, yo creo, la gran virtud del CPV, dar duros a peseta:

- Los autores no queremos que alguna persona, por motivos económicos o "de conciencia" no pueda o quiera pagar las 500 pesetas que cuesta conseguir la lección por correo y por este motivo facilitamos también la obtención de las lecciones completas a base de realizar alguna aportación técnica. Con esta doble posibilidad yo creo que garantizamos que todo el mundo interesado pueda conseguir el CPV y con ello dormiremos un poco más tranquilos. ;-)

Bueno en resumen la forma de distribución es esta:
 

1- Muchos de vosotros obtenéis las lecciones bajándolas de alguna BBS, oséa, vía telefónica que como bien sabréis no es ni mucho menos una ganga.

2- Por el módico precio de 100 duros recibiréis cada lección en vuestro buzón, en sobre de seguridad y en un diskette 3 1/2 HD.
 
3- Como en el disco sobrará espacio, incluiremos en él distintos programas y utilidades de dominio público,( por supuesto no haremos pirateo P-( ) que no tengan explícitamente prohibida esta forma de distribución y que puedan resultar interesantes.

4- Todos nuestros 'subscriptores' tendrán prioridad tanto en el envío de las lecciones cómo en la respuesta a sus consultas.

5- En las lecciones solicitadas por correo incluiremos todos, todos los fuentes de las lecciones, así cómo de los programas auxiliares VCPV (por ejemplo) y de las traducciones que estamos haciendo de los programas Pascal a C.

6- Además, a la gente que nos pida todas las lecciones por correo le enviaremos en la última entrega los fuentes del programa Hormigator (+10000 líneas de código ensamblador y Pascal) y (si las terminamos) unas librerías que estamos desarrollando los autores en C, con todas las funciones incluidas en el CPV y muchas más.

7- Aunque parezca mentira de las 500 pelas que cuesta cada lección, quitando gastos de envío, sobre y diskette, a los autores nos queda una módica cantidad que servirá como donativo pa' la causa. Desde luego no nos vamos a hacer ricos, pero por lo menos servirá para no perder dinero.


Podríamos daros más razones pero si con las anteriores no os vale no os lograríamos convencer ni con una bazoka en la sién. ;-)

A los que no ha hecho falta convencer es a todos nuestros colaboradores, los cuales a estas horas ya estarán disfrutando de sus lecciones dedicadas.

Algunos las han recibido totalmente gratis debido a sus artículos o a sus innumerables muestras de apoyo, estos son ....:

Jose Miguel Espadero, Luis Garcia, Antonio Merillas, Alfredo Novoa, Jose Maria Peña, Francisco Priego, Jesús de Santos, Cristobal Saraiba y Juan Antonio Ubeda.

Otros pocos optarón por la opción de solicitar las lecciones por correo y a ellos se debe también que el CPV no se haya ido a pique. Si tu también quieres "achicar" agua, pues nada, a mandar 500 pesetas de alguna de las siguientes maneras, apunta:

- Giro Postal a nombre de cualquiera de los autores, indicando en los datos del remitente la dirección para el envío de la lección. Nuestras direcciones son las siguientes:

        Jesús Angel Sánchez Mena          Benjamín Moreno Palacios

        [Datos adicionales omitidos en la versión HTML, porque no
         tengo información de que este proyecto tenga continuidad
         actualmente, ni soy quien para distribuir datos personales
         -que posiblemente ya no sean correctos- de los autores}
 
- Mandar una carta de petición a cualquiera de los autores, dónde se indique claramente la dirección para el envío y la fecha y código de identificación de la trasferencia bancaria a alguna de las siguientes cuentas:

        [Datos adicionales omitidos en la versión HTML, porque no
         tengo información de que este proyecto tenga continuidad
         actualmente, ni soy quien para distribuir datos personales
         -que posiblemente ya no sean correctos- de los autores}

Una vez validada la información sobre el ingreso se enviará la lección.

- Si os gusta el riesgo y la aventura podéis optar por mandar dinero en metálico en la carta de petición, pero no nos hacemos responsables de las posibles 'negligencias postales'.

Ya sabéis en cualquiera de las modalidades anteriores el importe mínimo válido es de 500 ptas, transporte, I.V.A., T.A.E., impuesto de matriculación, de lujo, de sociedades, Plan Renove I y II e impuestos de los impuestos incluido. ;-)

Para cualquier duda que tengáis, poneros en contacto con nosotros, nuestras direcciones electrónicas las obtendréis con la tecla de función F4.

Bueno, expondremos ahora las condiciones del CPV, sabemos que son un rollete pero los autores debemos tener algo dónde agarrarnos:

** NECESARIO **   *** NECESARIO ***    *** NECESARIO ****   ** NECESARIO **
 

** NECESARIO **   *** NECESARIO ***    *** NECESARIO ****   ** NECESARIO **
 


4.3.- Formato gráfico GIF.

GIF.

Estas letras se corresponden con uno de los mejores y más difundidos formatos gráficos existentes en el mundo PC. El formato GIF o Graphic Interchange Format fue propuesto por  Compuserve para el intercambio y  transmisión de información gráfica y esta corporación es la encargada de revisar las especificaciones, siendo las más extendidas la 87a y la 89a.

Al igual que el formato gráfico PCX ya explicado en la lección 2, GIF también utiliza un esquema de compresión para almacenar las imagenes. Este se conoce como LZW (Limpel-Ziv-Welsh) y es propiedad de Unisys.

Debido a la complejidad de este algoritmo es difícil intentar explicar en profundidad el formato GIF en un único artículo, pero intentaré que se entienda lo más posible a pesar de todo. Para ello me centraré en un modo de vídeo en particular, el modo 13h y omitiré las explicaciones referentes a características poco extendidas del GIF. Estoy seguro de que vosotros partiendo de este artículo seréis capaces de trabajar sobre otros modos de vídeo y estudiar los GIF más en profundidad. :)

Comencemos viendo los distintos bloques que componen un fichero GIF.

                 / Identificación
                |  Descripción de la pantalla
    Cabecera    |  Tabla global de colores
                |  Descripción de la imagen
                |  Tabla local de colores           <<<--- Opcional
                 \ Longitud mínima de código LZW

                 / Bloque de dato
   Información  |  .....
    Imagen      |  .....
                 \ Bloque de dato

   Extensiones   / de Control gráfico              <<<---
     varias     |  de Comentarios                  <<<---  Opcional
                 \ de Aplicación                   <<<---

Omitiremos el estudio de todos los bloques opcionales ya que no se suelen presentar. Lo que sí nos interesa y mucho es toda la información que contiene la cabecera. Para entenderla mejor veamos un esquema de la misma:

  Posición  Longitud       Significado          Valor más común    Bloque

     0         3         Tipo de imagen              GIF       Identificación
     3         3             Versión              87a / 89a

     6         2       Anchura de la pantalla       320
     8         2       Altura de la pantalla        200         Descripción
    10         1        Byte Información  1          97             de
    11         1           Color de fondo             0          Pantalla
    12         1        Relación Ancho/Alto           0

    13       768         Paleta de colores                      Tabla Global

   781         1            Separador                44
   782         2      Línea comienzo de imagen        0
   784         2        Columna de comienzo           0          Descripción
   786         2       Anchura de la imagen         320              de
   788         2       Altura de la imagen          200            Imagen
   790         1        Byte Información  2           7

   791         1      Long. mín. de código LZW        8          Código LZW

El byte de información 1 presenta la siguiente estructura:

              ---------------------------------
              | A | B | B | B | C | D | D | D |
              ---------------------------------
                        A:  0 -> No hay tabla de color global
                            1 -> Existe tabla de color global
                        B:       Nº de bits por color primario
                        C:  1 -> Tabla global de colores ordenada
                        D:       Tamaño de la tabla global 3*2 elevado a D+1

y el byte de información 2:

              ---------------------------------
              | E | F | G | H | H | I | I | I |
              ---------------------------------
                        E:  0 -> Usar tabla de color global
                            1 -> No usar tabla global
                        F:  1 -> Imagen entrelazada
                        G:  1 -> Tabla local de colores ordenada
                        H:       Bits reservados
                        I:       Tamaño de la tabla local 3*2 elevado a I+1

Seguidamente a toda esta información vienen los bloques de datos,  todos ellos tiene la misma estructura. Primero aparece un byte que indica la  longitud del bloque y a continuación vienen los datos en sí.

Todos estos bloques contienen la información necesaria para que  aplicado el esquema de compresión LZW, obtengamos la imagen del fichero  Lo malo es que ahora no nos vale ir leyendo byte a byte y aplicar el método cómo haciamos en los PCX, no, no, no...

El algoritmo LZW utilizado en los GIF presenta una característica que lo hace diferente y mejor al resto de los métodos de compresión, esta es que utiliza códigos de longitud variable. Cómo veremos más adelante la información que se almacena en el fichero serán códigos en un rango de 1..4095. Para  presentar estos códigos utilizaríamos dos bytes pero...

Cómo sabréis 4095 se puede representar perfectamente con 12 bits y para éste (el mejor de los casos), estaríamos desperdiciando 4 bits. Para  otros valores 2000, 1000, 500 necesitariamos 11, 10 y 9 respectivamente y no los 16 de los dos bytes.

Los códigos de longitud variable se apoyan en esta característica y utilizan en cada momento el mínimo número de bits necesario para representar  el mayor código posible. Con este método, el fichero se debe ver como una  larga cadena de bits de donde nosotros cogeremos  9, 10, 11 o 12 dependiendo de la evolución del algoritmo, 9 será la mínima longitud del código LZW en nuestro caso.

Bien ya sabemos que la información que se va a guardar en el fichero van a ser códigos, pero, ¿Que van a representar esos códigos?...

El algoritmo RLE utilizado en los PCX se basaba en codificar las cadenas en las cuales se repetían los mismos caracteres (colores). El algoritmo LZW se apoya en una idea parecida, codificar las series de colores que se repiten a lo largo de toda la imagen. Estas series las llamaremos patrones.

Tanto el proceso de compresión cómo el de descompresión se basan en una tabla de patrones que se va construyendo paralelamente al tratamiento del fichero. Inicialmente la tabla se compone de los 256 colores de la paleta más dos códigos especiales, el código Clear y el End.

El código Clear indica que debe inicializarse la tabla de patrones con los valores iniciales y el código End señala la finalización del proceso.

Comencemos estudiando el proceso de compresión. Los pasos para éste son los siguientes:
 


Veamos el proceso para la siguiente entrada:

              Entrada                               Tabla
   50 50 50 50 35 50 35 25 50 50 50 35             0 - Color 0
                                                   1 - Color 1
                                                   .......
              Salida                              .......
                                                 255 - Color 255
                                                 256 - Clear
                                                 257 - End
                                                 258 -           <- Indice

Después de los pasos 0 y 1 tenemos la cadena 50 50 cómo no esta en la tabla, la insertamos (Paso 2.2), sacamos el valor 50 y volvemos al paso 1.Luego estamos en esta situación.

              Entrada                               Tabla
   50 50 50 50 35 50 35 25 50 50 50 35           ......   ......
   L  L                                          255 - Color 255
              Salida                            256 - Clear
          50                                     257 - End
                                                 258 - 50 50
                                                 259             <- Indice

Leemos el tercer 50 y tenemos, el último elemento leído 50 más este, 50 otra vez. Lo buscamos en la tabla y la cadena aparece con el código 258  luego seguimos leyendo. Tendríamos ahora la cadena 50 50 50, cómo no está en  la tabla la insertamos en la posición 259 (paso 2.2) y la salida será 55 55  que corresponde con el código 258. El estado actual es:

              Entrada                               Tabla
   50 50 50 50 35 50 35 25 50 50 50 35           ......   ......
   L  L  L  L                                    255 - Color 255
              Salida                            256 - Clear
          50 258                                 257 - End
                                                 258 - 50 50
                                                 259 - 50 50 50
                                                 260 -           <- Indice

Leemos el siguiente 35 y tenemos 50 35 cómo no está en la tabla....y tal, y tal, si seguís el proceso finalmente obtendríamos, a la salida la cadena 50 258 50 35 260 25 259 35 que cómo veis es bastante más corta que la original. La tabla de patrones no es necesaria guardarla en el fichero ya que,
cómo vamos a ver ahora, se generará automaticamente durante el proceso de descompresión.

Lo que verdaderamente nos va a importar a nosotros es el proceso de descompresión. Los pasos del algoritmo sería:
 


Partamos de la salida que obtuvimos antes y comprobemos si conseguimos la cadena original (cruzad los dedos):

              Entrada                               Tabla
       50 258 50 35 260 25 259 35                ......   ......
                                                 255 - Color 255
              Salida                            256 - Clear
                                                 257 - End
                                                 258 -            <- Indice

Leemos 50 y 258. En la tabla insertamos por el momento en la posición 258 el 50 y al no existir todavía el patrón del segundo código 258, insertamos el primer elemento del primer código leído (50) que cómo es un valor < 255 o sea un color de la paleta, el primer elemento es él mismo.

A la salida llevamos el primer código y nos tendría que quedar:

              Entrada                               Tabla
       50 258 50 35 260 25 259 35                ......   ......
       L  L                                      255 - Color 255
                                                 256 - Clear
              Salida                            257 - End
             50                                  258 - 50 50
                                                 259 -            <- Indice

Leemos el siguiente elemento y tenemos la cadena 258 50. Insertamos el patrón del primer código (50 50) y buscamos el 50. Como este existe  insertamos en la tabla el patrón correspondiente a 50 (que es 50). A la salida nos llevamos el patrón del primer código y nos queda:

              Entrada                               Tabla
       50 258 50 35 260 25 259 35                ......   ......
       L  L   L                                  255 - Color 255
                                                 256 - Clear
              Salida                            257 - End
           50 50 50                              258 - 50 50
                                                 259 - 50 50 50
                                                 260 -            <- Indice

Venga el último paso. Leemos el 35 y tenemos la cadena 50 35.  Insertamos en la tabla el patrón de 50 (50) y buscamos en la tabla el segundo cómo existe metemos su patrón (él mismo). A la salida nos llevamos el patrón del primer código (50). En este tercer paso tenemos:

              Entrada                               Tabla
       50 258 50 35 260 25 259 35                ......   ......
       L  L   L  L                               255 - Color 255
                                                 256 - Clear
              Salida                            257 - End
           50 50 50 50                           258 - 50 50
                                                 259 - 50 50 50
                                                 260 - 50 35
                                                 261 -            <- Indice

Si seguís el proceso y no os equivocais obtendréis la cadena original que comprimimos, alucinante verdad. Yo os recomiendo que os leais el ejemplo  una y otra vez y probéis a terminar el proceso, además de ser entretenido  ( por lo menos para mi ) os servirá para definitivamente entender el esquema de compresión LZW. ;)

En el siguiente programa demostración, está implementado el algoritmo anterior. El programa es bastante complejo y la única rutina gráfica es la de PonPunto el resto es todo algorítmica, recursividad, lógica binaria.... Si os animais a meteros con él, únicamente recomendaros que os estudies por separado los dos grandes bloques que componen el programa, por un lado los procesos para recuperar los códigos teniendo en cuenta su longitud variable y por otro lado el proceso de descompresión. Bueno aprovechamos esta demostración para presentarnos. Ejem... ejem... CPVrianos os presento a JASM y a BMP, pues nuestra parte un placer ;-)
 

Pulsa F2 para ejecutar el programa Visu_GIF.   - Posibilidad no disponible en la versión HTML-

Leshe, si parece un cartel de Se Busca.. poned vosotros el precio. ;-) Bueno a pesar de la perdida de resolución al pasar de 16 millones de colores a 256 se nos reconoce ¿no? Luego espero que si alguna vez nos véis por ahí, gustéis de invitarnos a unas birras. :-9''''

Respecto a los formatos gráficos este ha sido nuestro último artículo, como tarea podéis tratar vosotros algún formato que conozcais (y que se utilice BMP, TIF, JPG). Ya sabéis que entregando un artículo, con su programa y todo, obtendréis una lección del CPV gratis y creedme es un gran premio. ;)


4.4.- Modo 4x13h.

4x13h 1/3

Muchos de vosotros habréis oído hablar del famoso modo X, en el que la mayoría de los juegos comerciales están desarrollados. Pues bien, en esta serie de capítulos, vamos a daros a conocer el mundo del modo X con todas sus ventajas e inconvenientes. Comenzamos...

Lo primero a explicar es el nombre del artículo. ¿Qué es eso de modo 4x13h?. Le hemos puesto este titulillo al capítulo, ya que la principal característica del modo X es tener más de una página de vídeo disponible. El modo X es uno de tantos modos de vídeo Tweaked (logrados en base a jugetear con los registros de la VGA) y quizás sea el más utilizado para la programación de videojuegos. Todo los que os contemos será extrapolable para los diferentes modos X-Tweaked existentes, con las peculiaridades propias (resolución y número de páginas) de cada uno de ellos.

Nuestros artículos versarán sobre un modo de vídeo que sigue la filosofía del modo X. X tiene como resolución estándar 320x240 (la razón la veremos luego) mientras que el nuestro tendrá como resolución 320x200 (igual que el modo 13h) aunque con 4 páginas disponibles de vídeo.

Seguramente, a estas alturas del CPV, creéis que lo sabéis todo sobre la tarjeta VGA, que sois unos maestros en el arte del brochazo a la hora de dibujar en pantalla. Pues siento mucho desilusionaros, ya que en esta lección os voy a romper todos los esquemas que tengáis sobre la estructura de la VGA. Primer pullazo. Aunque en el modo 13h se accede a la memoria de la VGA de una forma lineal (en la dirección A000:0000 esta el primer punto, en la A000:0001 el segundo...), la memoria de la VGA no esta distribuida de forma lineal. En realidad se divide en 4 partes exactamente iguales denominadas bit-planes.

                          +---------------------+
                          |  Memoria VGA        |
                          +----------+----------+
                                     |
                                     |
                 +-----------+-------+------+-----------+
                 |           |              |           |
              +--+--+     +--+--+        +--+--+     +--+--+
              | B1  |     | B2  |        | B3  |     | B4  |
              +-----+     +-----+        +-----+     +-----+

Por ejemplo, si vosotros tenéis una tarjeta VGA de 1 Mb de memoria, en realidad, internamente la tarjeta lo que tiene son 4 bitplanes de 256 Kb cada uno.

Cuando estamos en el modo 13h, esto nos es indiferente, ya que es la VGA la que se encarga del manejo de todo lo relacionado con los bitplanes. Por si os interesa, este modo en la jerga de las tarjetas gráficas se denomina Chain-4 o modo de memoria de vídeo lineal.

Bueno, pues aquí viene el meollo de la cuestión, esto yo creo que es lo más importante del artículo, y es fundamental que lo entendáis. Cuando estamos en el modo 13h, por configuraciones internas de la tarjeta, solo se pueden utilizar 64kb de cada uno de los bitplanes formando 256 Kb en total.

Seguramente diréis: Pero bueno. Entonces, ¿por qué solo utilizamos 64Kb cuando accedemos a la memoria de vídeo para poner un punto en la pantalla?. La razón de esto hay que buscarla en la forma en que se distribuyen los puntos en los bitplanes en el modo 13h. Los cuatro primeros puntos de la pantalla (desde A000:0000 hasta  A000:0003) se sitúan en la primera posición de cada uno de los bitplanes (el punto A000:0000 en la primera posición del bitplane 1, el segundo en la primera posición del bitplane 2 ...), pero (ojo a lo raro que es esto) el quinto punto se sitúa en la QUINTA posición del bitplane 1, dejando así las posiciones 2ª,3ª y 4ª absolutamente desocupadas. De igual forma, el sexto punto se sitúa en la posición QUINTA del segundo bitplane. Se que es un poco lioso, pero a ver si os queda un poco mas claro con este esquema. Los números representan el numero de punto que se almacena en esa casilla:
 

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 1 |   |   |   | 5 |   |   |   | 9 |   |   |   |   Bitplane 1
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 2 |   |   |   | 6 |   |   |   | 10|   |   |   |   Bitplane 2
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 3 |   |   |   | 7 |   |   |   | 11|   |   |   |   Bitplane 3
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 4 |   |   |   | 8 |   |   |   | 12|   |   |   |   Bitplane 4
        +---+---+---+---+---+---+---+---+---+---+---+---+
 

Como podéis ver, por cada casilla de memoria que utilizamos, se desaprovechan 3, es decir, que solo aprovechamos la cuarta parte de la memoria de los bitplanes, si echáis cuentas 256kb/4=64kb, que es la memoria que utilizamos nosotros en nuestro amigo el 13h.

Bueno, pues esto es algo que tenemos que solucionar. Daros cuenta, que si consiguiéramos reagrupar los datos, podríamos almacenar hasta 3 veces más de lo que almacenamos en este momento. Para ello, hay que conocer el funcionamiento de la VGA. La VGA tiene para esto tres modos de funcionamiento que son los siguientes:
 


El modo actual de funcionamiento de la VGA se encuentra almacenado en un par de registros internos. Al entrar en el modo 13h, la VGA carga esos registros con el modo Double-Word. Si nosotros los modificamos, conseguiremos lo que queremos. Estos registros, son los siguientes:

        CRT Controler -------------------> 0x3D4
        Registro "Underline Location" ---> 0x14
        Registro "Mode Control" ---------> 0x17

                      7  6  5  4  3  2  1  0
                     +--+--+--+--+--+--+--+--+
                     |**|**|DW|**|**|**|**|**|
                     +--+--+--+--+--+--+--+--+
                    Registro Underline Location

                      7  6  5  4  3  2  1  0
                     +--+--+--+--+--+--+--+--+
                     |**|WD|**|**|**|**|**|**|
                     +--+--+--+--+--+--+--+--+
                    Registro Mode control

DW: Si este bit esta a '1', indica que estamos en modo double word.
WD: Si este bit esta a '0', indica que estamos en modo Word.
**: No interesan en este momento

Ojo, si nosotros queremos enviar un dato a un registro de la VGA, deberemos, primero enviar el numero del registro al controlador correspondiente mediante una instrucción "OUT", a continuación INCREMENTAR  EL NUMERO DEL CONTROLADOR, y enviar el dato mediante otra instrucción "OUT". Supongamos que en los registros anteriores quiero enviar un 1 al registro Mode Control. El programilla que lo haría sería:

        mov     dx,3d4h        ; Valor del CRT Controler
        mov     al,17h         ; Numero de registro del "Mode Control"
        out     dx,al          ; Lo enviamos
        inc     dx             ; Ojo, esto es necesario, si no, no funcionara
        mov     al,1           ; Dato que queremos enviar
        out     dx,al          ; Y por fin lo enviamos

Además de hacer esto, tenemos que informar a la VGA de que ya no seguimos queriendo tener direccionamiento lineal, sino que queremos acceder de un nuevo modo. Para ello tenemos que desactivar unos cuantos bits en otros cuantos registros. Estos registros son los siguientes:

        Graphics Controler --------------> 0x3CE
        Registro "Graphics Mode" --------> 0x05
        Registro "Miscelanea" -----------> 0x06

                      7  6  5  4  3  2  1  0
                     +--+--+--+--+--+--+--+--+
                     |**|**|**|OE|**|**|**|**|
                     +--+--+--+--+--+--+--+--+
                      Registro Graphics Mode

                      7  6  5  4  3  2  1  0
                     +--+--+--+--+--+--+--+--+
                     |**|**|**|**|**|**|OE|**|
                     +--+--+--+--+--+--+--+--+
                       Registro Miscelanea

        Sequencer Controler -------------------> 0x3C4
        Registro "Memory Mode" ----------------> 0x04

                      7  6  5  4  3  2  1  0
                     +--+--+--+--+--+--+--+--+
                     |**|**|**|**|**|BL|**|**|
                     +--+--+--+--+--+--+--+--+
                       Registro Memory Mode

OE: Si este bit esta a '1', entraremos en el modo llamado ODD/EVEN, o lo que es lo mismo, "no lineal".
BL: Si este bit esta a '1' los bitplanes se procesaran de forma lineal, con lo cual, habrá que ponerlo a '0'.

El proceso pues para entrar en nuestro particular modo X es el siguiente:
 


Bien, pues con esto ya hemos entrado en el modo 4x13h. En este momento, tenemos a nuestra disposición 4 páginas de vídeo completas, sumando en total 256Kb. Ahora, la distribución de los datos en los 4 bitplanes es la siguiente:
 

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 1 | 5 | 9 |   |   |   |   |   |   |   |   |   |  Bitplane 1
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 2 | 6 | 10|   |   |   |   |   |  |   |   |   |   Bitplane 2
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 3 | 7 | 11|   |   |   |   |   |   |   |   |   |  Bitplane 3
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 4 | 8 | 12|   |   |   |   |   |   |   |   |   |  Bitplane 4
        +---+---+---+---+---+---+---+---+---+---+---+---+
 

Por ahora todo es muy bonito. Lo que no os va a gustar es lo que os voy a decir ahora. Debido a que hemos cambiado la estructura interna de la VGA, el modo de acceso también a cambiado. El modelo de memoria lineal que teníamos se ha perdido, con lo cual !! Ninguna de las rutinas que hemos utilizado hasta ahora nos vale para nada !!. Esta es si cabe la mayor de las desventajas de este modo.

Si, así es. Tenemos que empezar a reprogramar nuestras rutinas de tratamiento de sprites, pantallas, puntos. Todo absolutamente, a cambiado. Pero no os preocupéis, vamos a empezar a partir de aquí, a reconstruir todas las rutinas necesarias para poder crear un juego en este fantástico nuevo modo. En sucesivos artículos iremos entregando rutinas cada vez mas complejas para poner sprites en la pantalla, realizar scrolls... . Para empezar vamos a ver como se accede a un único punto...

Antes, en el modo 13h de toda la vida, cada posición de memoria correspondía a un solo punto dentro de cada uno de los bitplanes. Pero ahora eso a cambiado. Desde el momento que pasamos al modo 'byte' de la VGA, cada dirección de memoria afecta a cuatro puntos a la vez (una posición de bitplane. No os asustéis voy a poneros un ejemplo: Antes la dirección A000:0000 hacía referencia únicamente al primer punto de la pantalla. Ahora, la dirección A000:0000 hace referencia a la primera posición de bitplane. Es decir afecta a los puntos 1,2,3 y 4.

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 1 | 5 | 9 |   |   |   |   |   |   |   |   |   |  Bitplane 1
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 2 | 6 | 10|   |   |   |   |   |  |   |   |   |   Bitplane 2
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 3 | 7 | 11|   |   |   |   |   |   |   |   |   |  Bitplane 3
        +---+---+---+---+---+---+---+---+---+---+---+---+

        +---+---+---+---+---+---+---+---+---+---+---+---+
        | 4 | 8 | 12|   |   |   |   |   |   |   |   |   |  Bitplane 4
        +---+---+---+---+---+---+---+---+---+---+---+---+
        \__/\__/ \__/
        /    |      \
 A000:0000 A000:0001 A000:0002

Seguro que os preguntáis: Si una misma dirección accede a 4 puntos, ¿ Como narices accedemos a uno de esos 4 puntos ?. Pues bien, es aquí donde intervienen otra vez los mágicos registros de la VGA. Uno de ellos se denomina MapMask y se encuentra dentro del graphics controler de la VGA.Su función es determinar, en este tipo de modos, en que bitplane se alojara la informacion que nosotros escribamos en memoria. El formato de este registro es el siguiente:

        Sequencer controler ----> 0x3C4
        MapMask             ----> Registro 0x02

                                  +--+--+--+--+--+--+--+--+
                                  |**|**|**|**|B4|B3|B2|B1|
                                  +--+--+--+--+--+--+--+--+

B1: Si es igual a '1' indica que la salida que hagamos a memoria se actualizara en la memoria de la VGA del bitplane 1.
B2: Si es igual a '1' indica que la salida que hagamos a memoria se actualizara en la memoria de la VGA del bitplane 2.
B3: Si es igual a '1' indica que la salida que hagamos a memoria se actualizara en la memoria de la VGA del bitplane 3.
B4: Si es igual a '1' indica que la salida que hagamos a memoria se actualizara en la memoria de la VGA del bitplane 4.
**: No ocupado.

Como podéis observar, podríamos escribir un mismo dato en los  cuatro bitplanes a la vez. Esto a veces podría ser interesante, pero en  general no lo utilizaremos. Así pues, el proceso para escribir un punto en  la pantalla seria el siguiente:

-> Cambiar al modo 13h.
-> Conmutar al modo 4x13h como ya hemos visto.
-> Calcular la dirección de memoria donde debemos enviar el punto.
-> Programar el registro MapMask con el bitplane al que vamos a enviar el punto.
-> Enviar el punto a la memoria como si se tratara de un acceso normal.

Para calcular la dirección de memoria, hay que tener en cuenta que  ahora, cada dirección de memoria agrupa a cuatro puntos. Por lo que si  queremos acceder al punto 20002, este se encontrara en la posición  20002 div 4=5000, e igualmente para calcular el bitplane, deberemos hacer la operación 20002 mod 4=2, es decir, el punto 20002 estará en la dirección 5000, bitplane 2.

Os presento ahora la demostración del artículo, en ella veréis implementado todos los pasos para abrir el modo-x a nuestro ordenador y empezaremos a trabajar sobre este nuevo modo visualizando una imagen cruda.
 

Pulsa F2 para ejecutar el programa Visu_X.   - Posibilidad no disponible en la versión HTML-

A primera vista el programa no tiene nada novedoso, "simplemente"  muestra un gráfico, pero potencialmente es un programa con grandes posibilidades. Por ejemplo, en vez de leer una imagen, podríamos haber recuperado 4 y guardarlas en páginas de vídeo distintas y con una simple instrucción mostrar una u otra de una manera instantanea.

Bueno, pero por esta lección creo que ya es suficiente. En la siguiente entrega, ya hablaremos de como sacar sprites por la pantalla, y como realizar el cambio de páginas, y será entonces cuando comenzará lo realmente interesante...
  


4.5.- Scrolles en modo 13H.

Scroll

Uno de los efectos mas alucinantes que todos vosotros habréis visto en casi todos los videojuegos son los SCROLLES. Un scroll consiste en un desplazamiento del fondo de la pantalla en alguna dirección. Juegos como el ALADDIN o el HOCUS, añaden a su vez la posibilidad de tener SCROLLES de varias pantallas simultaneamente, cada una con su propia velocidad, con lo cual se consigue un efecto en el cual diferentes zonas de la pantalla visual se desplazan a distinta velocidad, dando una sensación de movimiento, profundidad y realismo muy impactante. En este artículo vamos a intentar abarcar todo este mundo, incluidos estos SCROLLES múltiples tan maravillosos. Pero bueno, no anticipemos acontecimientos. Empecemos ...

¿Por qué el modo 13h ?

Por ahora no conocemos demasiado del modo X (el capitulo incluido en esta lección es el primero de la serie) y quizá no entendáis el porque es mas difícil realizar SCROLLES dobles o triples en este modo. La razón es bien sencilla. Como podréis intuir, un scroll múltiple va a necesitar de constantes actualizaciones de pantallas completas, y para esto el modo 13h llega a ser hasta el doble de rápido. Pensad que en este modo podemos llegar a alcanzar velocidades de hasta 35 pantallas por segundo. Mientras que en modo X tendremos suerte si llegamos a las 15 (estoy hablando, por supuesto, sin tener en cuenta buses locales, PCI, ni ninguna historia de esas). El modo X supera ampliamente al 13h en SCROLLES de una única pantalla, ya que este no necesita actualización de la pantalla completa (como veremos en las siguientes lecciones del CPV). Pero desde luego cuando se trata de meter dos o mas páginas, moviéndose cada una con su velocidad, se queda corto.

Mecánica de los SCROLLES

En primer lugar vamos a recordar un poco como iba lo de la VGA. Recordaréis que en el modo 13h, si queríamos enviar un punto a la pantalla bastaba con escribir un dato en memoria en dirección A000:(y*320)+x y de repente el punto aparecía en pantalla. Igualmente si queríamos saber el color del punto que se encontraba en cualesquiera coordenadas, lo que hacíamos era leer de la memoria en esa posición, y obteníamos el valor del color.

Bien, pues ahora pensad esto. Que ocurriría si empezáramos a leer puntos en la memoria de vídeo, y escribiéramos, cada uno de esos puntos, una posición a la derecha de donde estaban ?. A partir de ahora, todos los ejemplos que vaya haciendo los representare en una pantalla simulada de dimensiones 6x6. Pues en este caso ocurriría lo siguiente:

    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 1  | 2  | 3  | 4  | 5  | 6  |       | XX | 1  | 2  | 3  | 4  | 5  |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 7  | 8  | 9  | 10 | 11 | 12 |  =>   | 6  | 7  | 8  | 9  | 10 | 11 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 13 | 14 | 15 | 16 | 17 | 18 |       | 12 | 13 | 14 | 15 | 16 | 17 |
    +----+----+----+----+----+----+  =>   +----+----+----+----+----+----+
    | 19 | 20 | 21 | 22 | 23 | 24 |       | 18 | 19 | 20 | 21 | 22 | 23 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 25 | 26 | 27 | 28 | 29 | 30 |  =>   | 24 | 25 | 26 | 27 | 28 | 29 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 31 | 32 | 33 | 34 | 35 | 36 |       | 30 | 31 | 32 | 33 | 34 | 35 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
      c1   c2   c3   c4   c5   c6          c6    c1   c2   c3   c4   c5

Como habréis observado, lo que hemos conseguido con esto es hacer un desplazamiento horizontal hacia la derecha de las columnas de nuestra matriz. Con lo cual, hemos obtenido ¡¡ UN SCROLL HORIZONTAL !!. Vaya, vaya que casualidad. Y qué pasaría en la matriz inicial, si desplazáramos los datos, no una posición sino 6 (anchura de la matriz).

    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 f1 | 1  | 2  | 3  | 4  | 5  | 6  |       | XX | XX | XX | XX | XX | XX | XX
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 f2 | 7  | 8  | 9  | 10 | 11 | 12 |  =>   | 1  | 2  | 3  | 4  | 5  | 6  | f1
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 f3 | 13 | 14 | 15 | 16 | 17 | 18 |       | 7  | 8  | 9  | 10 | 11 | 12 | f2
    +----+----+----+----+----+----+  =>   +----+----+----+----+----+----+
 f4 | 19 | 20 | 21 | 22 | 23 | 24 |       | 13 | 14 | 15 | 16 | 17 | 18 | f3
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 f5 | 25 | 26 | 27 | 28 | 29 | 30 |  =>   | 19 | 20 | 21 | 22 | 23 | 24 | f4
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 f6 | 31 | 32 | 33 | 34 | 35 | 36 |       | 25 | 26 | 27 | 28 | 29 | 30 | f5
    +----+----+----+----+----+----+       +----+----+----+----+----+----+

Pues en este caso, lo que hemos hecho es desplazar las filas de la matriz, obteniendo un scroll vertical. Es como si hubiéramos cogido la matriz de la izquierda y la hubiéramos bajado una fila.

Se que esto parece muy teórico. Pero imaginaros ahora una matriz igual que esta pero de dimensiones 320x200. Esta matriz constituiría nuestra pantalla. Y con lo que hemos hecho anteriormente conseguiríamos desplazar toda la pantalla una columna a la derecha en el primer caso, y desplazar toda la pantalla una fila hacia abajo en el segundo. Igualmente se realizarían los SCROLLES hacia la izquierda (desplazar todo una posición a la izquierda) y los SCROLLES hacia arriba (desplazar todo 320 posiciones a la izquierda).

Seguramente este método os habrá parecido una buena idea, pero es extremadamente lento, pero que muy ,muy lento. Imposible de utilizar en un videojuego que queramos que tenga unas condiciones medianamente aceptables. La razón en muy simple, hemos metido la pata hasta el fondo en algunas cosillas, como son las siguientes:
 


Ambos problemas tienen una misma solución. Podríamos pensar en lo siguiente: ¿Qué pasaría si yo tuviera almacenada la información de la pantalla en algún sitio ?. Pues bien fácil, que nos ahorraríamos por lo pronto los 64.000 accesos de lectura a la memoria de vídeo, además, como solo íbamos a llevar al vídeo la información después de haber hecho el scroll, podríamos quitar esa franja tan indeseable que entra por la izquierda o por la derecha. En fin, se trata del conocido principio del la página virtual. Tener una ventana en memoria donde realizar todos los cambios, y así ahorrarnos todos los accesos de lectura a la memoria de vídeo.

El proceso de hacer el scroll se realizaría ahora haciendo los cambios necesarios en la memoria principal, y efectuando un volcado de esa memoria principal a la pantalla una vez realizadas todas las modificaciones.

Bueno, pues esto ya va teniendo mejor pinta. Pero aun se puede optimizar un poquillo mas. Supongamos que estamos programando en C, y que para almacenar nuestra pantalla utilizamos un puntero. Entonces haríamos lo siguiente:

                char * pantalla;
                ...
                pantalla=(char *)malloc(64000)

Es decir que nuestra pantalla virtual es un puntero. Con lo visto hasta ahora, lo que tendríamos que hacer en cada paso del scroll sería desplazar los datos apuntados por 'pantalla' hacia donde conviniera. Pero ¿Qué ocurre si en lugar de mover los datos, movemos el puntero ?, o dicho en C:

pantalla=MK_FP(FP_SEG(pantalla),FP_OFS(pantalla)+1);

Pues voy a mostrároslo con el ejemplo de antes:

 +----------+                               +----------+
 | pantalla |                               | pantalla |
 +----+-----+                               +----+-----+
      |                                          |
      |                                          |
    +-+--+----+----+----+----+----+       +----+-+--+----+----+----+----+
    | 1  | 2  | 3  | 4  | 5  | 6  |       | 1  | 2  | 3  | 4  | 5  | 6  |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 7  | 8  | 9  | 10 | 11 | 12 |  =>   | 7  | 8  | 9  | 10 | 11 | 12 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 13 | 14 | 15 | 16 | 17 | 18 |       | 13 | 14 | 15 | 16 | 17 | 18 |
    +----+----+----+----+----+----+  =>   +----+----+----+----+----+----+
    | 19 | 20 | 21 | 22 | 23 | 24 |       | 19 | 20 | 21 | 22 | 23 | 24 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 25 | 26 | 27 | 28 | 29 | 30 |  =>   | 25 | 26 | 27 | 28 | 29 | 30 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
    | 31 | 32 | 33 | 34 | 35 | 36 |       | 31 | 32 | 33 | 34 | 35 | 36 |
    +----+----+----+----+----+----+       +----+----+----+----+----+----+
 

Nuestro puntero 'pantalla' apunta ahora a la segunda casilla, con lo que hemos conseguido el mismo efecto que si hubiéramos hecho el desplazamiento. Con lo cual, ¡¡ Hemos conseguido hacer el scroll horizontal con un incremento de un puntero !!. Mas optimo que esto no puede haber nada. Solo tiene un pequeño defectillo. Y es que incrementando, incrementando... nos hemos salido de la memoria que teníamos reservada. Pero también para esto existe solución.

Y la solución es la siguiente. Reservar todo un segmento para nuestra pantalla virtual. De esta manera nuestro puntero, al llegar al desplazamiento 0xFFFF que es el máximo, al sumarle 1, obtenemos 0x0000 con lo cual nos quedamos dentro de la memoria que teníamos reservada.

SCROLLES múltiples

Si habéis entendido todo lo que ha habido hasta aquí, no tendréis ningún problema con esta parte. Hasta ahora, teníamos una sola página virtual, con un solo fondo. Si queremos tener varios fondos, no tenemos mas que reservar un segmento completo para cada uno de los fondos con los que queremos realizar el scroll.

Si queremos que uno de los fondos vaya mas rápido que otro, no tenemos mas que incrementar su puntero en un valor mas alto que el del otro. Por ejemplo:

pantalla1=MK_FP(FP_SEG(pantalla1),FP_OFS(pantalla1)+2);
pantalla2=MK_FP(FP_SEG(pantalla2),FP_OFS(pantalla2)+4);

En este ejemplo, el fondo representado por 'pantalla2' ira el doble de rápido que el de 'pantalla1'.

Además, deberemos tener una pantalla virtual extra donde volcar todas nuestras páginas desplazadas, antes de enviarlas a la memoria de vídeo.

Solo hay un pequeñísimo problema y es que ¿Cómo logramos el efecto de trasparencia para que unas pantallas dejen ver a las otras por debajo ?. De esto se debe encargar la rutina que ponga el fondo encima de la página virtual destino. Deberemos tener un color especial (nosotros siempre utilizamos el 0) de manera que cuando la rutina encuentra en la página a poner ese color, pasa directamente al siguiente punto, y no escribe nada. Con lo cual se conserva el color que hubiera en el fondo.

Con todo esto, el método para hacer un buen scroll múltiple, se reduce a lo siguiente:


Aquí tenéis una pequeña demo de lo que se puede llegar a hacer con este método que os hemos enseñado. A ver que os parece, los controles son los cursores para manejar a la hormiga y Esc para salir, aunque esta tecla no desearéis pulsarla nunca: ;-)

Pulse F2 para ejecutar el programa Scroll13.   - Posibilidad no disponible en la versión HTML-

Bueno, creemos que este es el método más estándar ('creemos' por que todo esto ha sido fruto de nuestra elucubración mental), para hacer SCROLLES múltiples. Lo que ocurre es que tiene sus inconvenientes. Que, como no, son la memoria y la velocidad.

Esto es, para cada una de las páginas con las que queramos hacer el scroll, hay que reservar un segmento de memoria. Lo cual son 65536 bytes por página. Si, por ejemplo, queremos hacer un scroll con 3 páginas, tendríamos que reservar 3(páginas)*65536+64000 (para el trasvase) son 260.608 bytes sólo para realizar el scroll. Lo cual es una burrada.

Además, daros cuenta también que en muchas ocasiones puede no interesarnos hacer el scroll de una página completa, si no que solo sean unas nubes moviéndose o algo así, con lo cual estamos perdiendo tiempo moviendo datos que en ocasiones no nos sirven para nada. Podríamos plantearnos entonces la posibilidad de no desplazar pantallas completas, sino desplazar tan solo sprites, con lo cual ahorraríamos en memoria y por supuesto en tiempo. De hecho, es esta posibilidad la que emplearemos en algún juego del mes.

Bueno, pues con esto ya sabéis lo fundamental sobre scrolles en modo 13h. Nos queda la asignatura pendiente del modo X que será desarrollada en la última lección que dediquemos a este modo. Esperamos vuestras noticias.
  


4.6.- Formato de sonido .VOC.

VOC

En el principio de los videojuegos por ordenador, las posibilidades sonoras de los ordenadores antiguos, impedían añadir grandes efectos a los juegos comerciales. Con la introducción de los PC's en el mercado, se empezaron a crear dispositivos electrónicos, que conectados al bus del ordenador, permitieron crear efectos sonoros cada vez mas complicados y perfectos. Estos dispositivos son conocidos como tarjetas de sonido, y una de las mas conocidas es la SoundBlaster.

La tarjeta de sonido SoundBlaster incorporaba una serie de programas capaces de crear y modificar tanto sonidos digitalizados, como música sintetizada (FM) que trataremos en otro capítulo del CPV. El formato estándar para los sonidos digitalizados que idearon los creadores de la SoundBlaster es el que utilizan los conocidos ficheros '.VOC', y que son ahora, el objetivo de este artículo.

En uno de los capítulos de la primera lección del CPV llamado "Naturaleza del sonido", recordaréis que decíamos que un sonido digitalizado no era mas que una serie de muestras recogidas del medio ambiente con una frecuencia determinada. Pues eso es en realidad lo que es el fichero '.VOC' en sí. Una serie de muestras que alguien recogió en su momento del exterior, y las almacenó en el fichero. Además, nuestro '.VOC' debe contener información sobre la frecuencia con la que se recogieron las muestras para que en la reproducción de ellas, nos adaptemos a la misma velocidad. Pero bueno, basta ya de rollo, y voy a explicaros, paso por paso, como esta formado un fichero '.VOC':

Un fichero '.VOC' tiene el formato típico de cualquier formato estándar. Posee una cabecera, y una serie de bloques que a su vez disponen de su propia cabecera, en esta viene información sobre que tipo de bloque es, numero de repeticiones ... Pero en fin, vamos por partes:

Cabecera de en fichero '.VOC'

La cabecera de un fichero '.VOC' se sitúa como su propio nombre indica al comienzo del fichero. Posee los siguientes componentes:

            Bytes               Que son

        ->  0-13h               Descriptor de fichero:
                                Consiste en una cadena ASCII cuyo valor es
                                siempre "Creative Voice File" seguida del
                                carácter 1Ah que sirve a los programas para
                                reconocer este fichero como un '.VOC'.

        -> 14h-15h              Offset de datos:
                                Son dos bytes que nos indican el
                                desplazamiento respecto del inicio del fichero
                                donde se sitúa el primer bloque de datos.
                                En general suele valer 001Ah (ojo, este valor
                                se almacena como "byte-bajo byte-alto", tener
                                cuidado).

        -> 16h-17h              Número de Versión de Formato
                                Este campo apenas tiene importancia, pero
                                si os interesa os diré que indica el numero
                                identificador de la versión del formato '.VOC'
                                (por si hubiera nuevas revisiones del formato)
                                su valor habitual es "0A 01"=1.10

        -> 18h-19h              Identificador de fichero '.VOC'
                                Este campo tampoco se utiliza para nada,
                                es una redundancia del anterior, y en
                                general vale 1129h

Bloques de datos

A continuación de la cabecera se sitúan los bloques de datos en el desplazamiento señalado por el campo "Offset de datos". Estos bloques son los que realmente contienen los datos del sonido. Cada bloque consta, como ahora veremos, de un byte  que nos dice que tipo de bloque es, y a continuación, la información del bloque. En el siguiente esquema, "Bytes" indica el desplazamiento respecto del inicio del bloque, y "Significado" lo que significan esos bytes. Todos los posibles bloque son los siguientes:
 

-> Tipo 0: Bloque terminador

Este tipo de bloque indica que hemos llegado al final de un fichero '.VOC'. Todo fichero que siga este formato debe finalizar con este bloque. El formato de este bloque es:

          BYTES       SIGNIFICADO

           0           "00" Fin de fichero VOC

-> Tipo 1: Bloque de datos

Este tipo de bloque es el mas importante. Incluye dentro de si los valores digitalizados a emitir por la SoundBlaster, así como información sobre la frecuencia de muestreo. Su formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "01" Identificador del Bloque

           1 - 3       Longitud del bloque (ojo, ya que son tres bytes,
                       y además se encuentra almacenado en el formato
                       "byte-bajo byte-medio byte-alto"

           4           Constante de Tiempo. Este campo es el que lleva la
                       información sobre la frecuencia de muestreo, y por
                       tanto, sobre la velocidad de reproducción de las
                       muestras aquí recogidas. Es un solo byte que ha
                       sido calculado a partir de la frecuencia de muestreo
                       de la siguiente forma.

                           Cons de T = (256-1000000/Frecuencia de muestreo)

                       Este valor nos será útil en la próxima lección,
                       cuando programemos la SoundBlaster directamente...

           5           Tipo de Empaquetamiento. Indica el formato de los
                       datos incluidos debajo. Este empaquetamiento puede
                       ser:

                                "00" = 8 bits sin empaquetar
                                "01" = 4 bits empaquetados
                                "02" = 2.6 bits empaquetados
                                "03" = 2 bits empaquetados
                                "04" = Por el canal 1
                                "05" = Por el canal 2
                                "06" = Por el canal 3
                                "07" = Por el canal 4
                                "08" = Por el canal 5
                                "09" = Por el canal 6
                                "10" = Por el canal 7

                       El 99.99 de los ficheros VOC existentes utiliza el
                       formato "00" en el que los datos están tal y como
                       fueron recogidos del exterior en su día. De todas
                       formas, aquí esta toda la información.

           6-final     Datos: Tiene que haber "Longitud de Bloque - 2" datos.
 

-> Tipo 2: Continuación de datos

Viene normalmente después de un bloque del tipo 1 y sirve para decir que los datos que vienen a continuación, tienen las mismas características (Constante de tiempo, compactación...) que el bloque anterior. El formato es el siguiente.

          BYTES       SIGNIFICADO

           0           "02" Identificador del Bloque

           1 - 3       Longitud del bloque (ojo, ya que son tres bytes,
                       y además se encuentra almacenado en el formato
                       "byte-bajo byte-medio byte-alto"

           4-final     Datos. Con el formato de el bloque anterior.

-> Tipo 3: Periodo de silencio

Sirve para introducir, en medio de un sonido digitalizado, un silencio de duración variable. El formato es:

          BYTES       SIGNIFICADO

           0           "03" Identificador del Bloque

           1 - 3       Longitud del bloque (siempre, en este tipo de bloque
                       vale 3)

           4 - 5       Periodo: Duración del silencio

           6           Constante de Tiempo: Calculada con la misma formula
                       que antes, a través de la frecuencia de muestreo
 

-> Tipo 4: Marca

Sirve para introducir, una marca dentro del fichero '.VOC'. El driver CT-VOICE (ver capítulo dedicado a él) actualizara la variable de estado a el valor de la marca. El formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "04" Identificador del Bloque

           1 - 3       Longitud del bloque

           4 - 5       Valor de la marca.

-> Tipo 5: Texto ASCII

Sirve para introducir un texto en medio del fichero VOC. En programas dedicados a la ejecución de '.VOC' sirve para que en medio del sonido digitalizado introducir un mensaje. El formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "05" Identificador del Bloque

           1 - 3       Longitud del bloque

           4 - final   Cadena de datos ASCII. Terminada por un NULL ("00")

-> Tipo 6: Inicio de repetición

Se usa en conjunción con el bloque de tipo 7, y sirve para establecer bucles de repetición de una secuencia de bloques. Se repetirán las veces indicadas, lo contenido entre un bloque de tipo 6, y un bucle de tipo 7. El formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "06" Identificador del Bloque

           1 - 3       Longitud del bloque (siempre, en este tipo de bloques
                       será de 2)

           4 - 5       Numero de repeticiones: Este será el numero de
                       repeticiones que se ejecutara lo que haya entre
                       este bloque, y el próximo bloque de tipo 7
 

-> Tipo 7: Fin de repetición

Se usa en conjunción con el bloque de tipo 6, y sirve para establecer bucles de repetición de una secuencia de bloques. El formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "07" Identificador del Bloque

           1 - 3       Longitud del bloque (siempre, en este tipo de bloques
                       será de 0)

-> Tipo 8: Bloque extendido

Este bloque vino a subsanar el hecho de que en el bloque de tipo 1, como habréis podido observar, no se consideran '.VOC' en estéreo. Con el nacimiento de la SoundBlaster PRO (en estéreo) y sucesoras, no se aprovechaban todas las posibilidades de esta tarjeta, y es por esto que se introdujo este bloque para tocar sonidos digitalizados en estéreo. Este bloque precede siempre a un bloque de tipo 1. Que será quien realmente llevara los datos a ejecutar. Los atributos del siguiente bloque (tales como constante de tiempo, modo de empaquetamiento...) serán ignorados. El formato es el siguiente:

          BYTES       SIGNIFICADO

           0           "08" Identificador del Bloque

           1 - 3       Longitud del bloque (siempre valdrá 4)

           4 - 5       Constante de Tiempo. Depende de si estamos
                       considerando un sonido en mono o en estéreo se calculara
                       así:
                          En mono:
                           Cons de T = (256.000.000/Frecuencia de muestreo)
                          En estéreo:
                           Cons de T = (256.000.000/(2*Frecuencia de muestreo))

           6           Tipo de Empaquetamiento: Exactamente igual que el
                       campo del mismo nombre en el bloque de tipo 1.

           7           Modo de uso   0 = Mono
                                     1 = Estéreo

Consideraciones finales

Hemos decidido explicaros este formato de sonido debido a que es el que vamos a utilizar a partir de ahora en el desarrollo de cualquier demostración o juego. Y hemos decidido escoger este formato ya que es lo suficientemente completo como para ilustrar como se manejan sonidos digitalizados. Además, existe un driver que lo usa, y que es capaz de manejar todos estos tipos de bloques.

Aunque, como habréis observado, existe mucha variedad de posibilidades a la hora de realizar un sonido (podemos incluir bucles, textos, marcas, etc.). El 99.99 de los ficheros '.VOC' tienen la siguiente estructura:

                Cabecera
                Bloque de tipo 1 (datos)
                Bloque de tipo 0 (terminación)

Esto nos viene bien, ya que aunque el driver disponible creado por creative labs (y que explicamos en otro capítulo de esta lección) reconoce todos estos bloques, una posible rutina implementada por nosotros que saque por la SoundBlaster directamente un fichero '.VOC', se complicaría horriblemente si tuviéramos que implementar todos los bloques posibles. Así que lo que haremos en la siguiente entrega, será implementar una rutina que lo haga pero solo para los '.VOC' que tengan este tipo de estructura.

Bueno, pues aquí se termina este artículo. Como única demo de este, solo puedo mostraros un programa que, dado un VOC pasado por argumento, te saca por pantalla toda la información de los bloques que contiene interiormente. Hasta la próxima:

Pulse F2 para ejecutar el programa VOC-INFO.   - Posibilidad no disponible en la versión HTML-
 
 


4.7.- Efectos sonoros por la SB.

SB

Bueno, seguro que muchos de vosotros suspiraréis largamente y diréis "POR FIN". Y es que muchos en las encuestas que hemos recibido, el tema que con mas impaciencia esperabais era el de tratamiento de sonido con la SoundBlaster. Ha sido duro, mucho tiempo de investigación y trabajo nos han permitido recopilar la información necesaria para realizar este artículo y el del formato VOC. Pero creemos que ha merecido la pena.

Pero basta de charla y comencemos a entrar en materia...

¿Qué es el CT-VOICE?

El fichero CT-VOICE, que incluye Creative Labs en todos sus paquetes de software que acompañan a las tarjetas de sonido, es un ejecutor de ficheros '.VOC'. Es un programa capaz de reconocer la estructura de un fichero '.VOC' y programar la SoundBlaster para que sea capaz de reproducir en su salida el contenido de este fichero.

Si recordáis de los comienzos del CPV un artículo que se llamaba "Naturaleza del Sonido", ahí os indicábamos que un sonido digitalizado no es más que una serie de muestras obtenidas a una determinada frecuencia de muestreo. Pues bien, lo que hace el CT-VOICE es programar todo lo necesario, como ahora veremos, para que la SoundBlaster sea capaz de leer de memoria las muestras que en su día fueron capturadas, y sacarlas en secuencia por el altavoz de la SoundBlaster.

Para que entendáis todo lo que a continuación vamos a explicaros, es necesario que sepáis unas cuantas cosas.

¿Qué es eso de la DMA?

En principio, en una computadora sin nada especial, es la CPU del ordenador la  que puede acceder al contenido de la memoria. Esto se traduce en que si un dispositivo de entrada/salida (de los que se conectan en el bus), como podría ser la SoundBlaster, necesita datos para realizar su función (como por ejemplo, sacar un sonido digitalizado) tendría que ser la CPU la que se encargara, mediante instrucciones del tipo de IN y OUT, de suministrarle la información necesaria.

Pero claro, si la CPU esta ocupada en enviarle a los periféricos la información, no puede dedicarse a otro tipo de tareas como puede ser el tratamiento de los gráficos, o el desarrollo del programa. Es por esto que se introdujo en los PC's lo que se llama DMA.

La DMA (Direct Access Memory), permite a un periférico acceder, sin intervención de la CPU, a una parte de la memoria, liberando a la CPU de esta tarea. Daros cuenta que si la SoundBlaster dispusiera de esta posibilidad, nos liberaríamos de todo el trabajo de llevar hasta la tarjeta todos los datos de nuestro sonido digitalizado, con lo cual podríamos ganar una burrada de tiempo.

Pues efectivamente, dispone de esa posibilidad. Seguro que os habrá pasado que muchos juegos antes de empezar a jugar os piden que introduzcáis el canal de DMA para configurar la SB. Este canal es, no muy rigurosamente dicho, el sitio por donde la SB va a pedirle a la memoria que le transmita los datos.

¿Qué es eso de la IRQ?

Seguro que a estas alturas del curso, todos sabéis ya lo que es una interrupción. De todos modos os lo recordaré, se trata de una parada que se induce en el procesador, para que se pueda ejecutar un código especial de tratamiento de esa interrupción. También recordaréis la diferencia entre interrupciones software e interrupciones harwdware. ¿No?

Las interrupciones software son aquellas que se producen desde dentro  de nuestro programa cuando, por ejemplo, efectuamos una llamada a la interrupción 10h para cambiar el modo de vídeo. Las interrupciones hardware son aquellas que son producidas por elementos externos al procesador, como son los periféricos y entre ellos la SoundBlaster. A este ultimo tipo de interrupciones se las llama en la jerga informática "IRQ's".

Cuando realizamos, como hemos visto en el apartado anterior, una trasferencia mediante DMA con la SoundBlaster, el procesador necesita saber cuando ha terminado la trasferencia. Por este motivo, cuando la SoundBlaster termina con el trasvase de información, genera una IRQ. Nuestro programa debe conocer la IRQ, ya que si no, sería imposible que nos enteráramos de cuando acabó la trasferencia de datos. Si no conociéramos este dato, podríamos meter la pata y, por ejemplo, liberar el buffer donde tenemos almacenados los datos del sonido, sin que este haya terminado de ejecutarse. Lo que podría tener consecuencias imprevisibles.

Funcionamiento del CT-VOICE

Bueno, pues para poder programar nuestro CT-VOICE querido, tenemos que conocer, antes que nada, con que canal de DMA, con que IRQ y sobre todo, en que dirección base esta funcionando nuestra SoundBlaster. Ya que en la inicialización de este driver tendremos que comunicárselo para que no haya problemas.

Para que nuestro programa pueda utilizar este driver es necesario  cargarlo en memoria. Esto depende muy mucho del lenguaje que estemos empleando para nuestra programación. En pascal es quizá mas fácil que en C, ya que basta declarase un tipo 'pointer', alojarle memoria suficiente, y leer el fichero CT-VOICE.DRV completo al buffer creado, guardando la variable del tipo 'pointer', ya que a través de esta variable accederemos a el a través de una instrucción de ensamblador CALL.

En C esto es bastante mas complicado. Lo que hay que hacer es declararse una variable del tipo función, alojar memoria suficiente en ella, y cargar (igual que en el caso de pascal) todo el driver en el buffer creado, efectuándose la llamada al driver como si de una función cualquiera se tratará.

Bien, una vez que tenemos cargado el driver en memoria, el paso de parámetros al mismo se realiza mediante los registros del la CPU. Dependiendo del valor que introduzcamos en los mismos el driver realizara una  función u otra. Las posibles funciones que puede realizar el driver vienen especificadas a continuación.

Funciones del Driver

Para que el driver ejecute la función que nosotros deseamos, basta introducirle en el registro BX, la función deseada, y si es necesario, algún parámetro más, se utilizaran el resto de los registros de la CPU para introducirlos. Una vez hecho esto, se efectúa una llamada al lugar donde tengamos cargado el driver. La lista de las funciones que el CT-VOICE puede ejecutar son las siguientes:
 

Función 0: Obtención del número de versión del driver.

Esta función no tiene demasiada importancia, ya que únicamente permite obtener el numero de versión del CT-VOICE.DRV. Pero no afectará para nada a nuestro programa. Los parámetros necesarios son:

        Entrada:      BX = 0
        Salida:       AH,AL número de versión

Función 1: Seleccionar dirección base de la SoundBlaster.

Seria útil que nuestra rutina de inicialización de la SoundBlaster llamara a esta función. Aunque si no lo hace, el CT-VOICE, tomará por defecto 0x220.

        Entrada:      BX = 1
                      AX = Nueva dirección base
        Salida:       Ninguna

Función 2: Seleccionar IRQ de la SoundBlaster.

Es necesario que se invoque a esta función antes de llamar a la función de inicialización del driver que viene después, aunque si no se hace, se tomará por defecto la 7.

        Entrada:      BX = 2
                      AX = Nueva IRQ
        Salida:       Ninguna

Función 3: Inicialización del driver

Esta rutina debe ser llamada siempre antes de intentar sacar cualquier tipo de sonido digitalizado por la SoundBlaster. Efectúa un reconocimiento de los valores introducidos en el driver (la dirección base, la IRQ, y la DMA) y si alguno de ellos no es satisfactorio, devolverá un error.

        Entrada:      BX = 3
        Salida:       AX = 0 --> No hubo error
                      AX = 1 --> Error en la dirección base
                      AX = 2 --> Error en la entrada/salida
                      AX = 3 --> Error en la IRQ o en la DMA

Función 5: Seleccionar palabra de estado

La palabra de estado indica si estamos o no ejecutando un '.VOC' en el momento actual. Pero para que esta palabra sea actualizada por el driver debemos decirle donde se encuentra. Y esto se hace mediante esta función:

        Entrada:      BX = 5
                      ES:DI  Apuntando a la palabra que queremos seleccionar
        Salida:       Ninguna

Función 6: Ejecutar '.VOC'

"POR FIN" diréis. Pues sí, esta es la función que se encarga de sacar por la SoundBlaster un '.VOC'. Solo tenéis que llamar a esta función (después de haber inicializado el driver) pasándole como parámetros la dirección del primer bloque (ver capítulo del formato '.VOC') y la SoundBlaster comenzará mágicamente a sonar.

        Entrada:      BX = 6
                      ES:DI   Puntero apuntando al comienzo del buffer
        Salida:       AX = 0  Salida exitosa
                      AX = 1  Fallo

Función 8: Parar ejecución '.VOC'

Esta función para la ejecución de un voc, si es que hay alguno en curso, se debe utilizar antes de sacar un nuevo voc por si existe alguno que se esté ejecutando.

        Entrada:      BX = 8
        Salida:       Ninguna

Función 9: Resetear Driver

Esta función efectúa un reset de la tarjeta, y debe ser llamada antes de la salida de nuestro programa, ya que sino, la tarjeta puede quedar en un estado inconsistente, y no responder a nuestras nuevas llamadas.

        Entrada:      BX = 9
        Salida:       Ninguna
 

Función 19: Seleccionar la DMA de la SoundBlaster.

Es necesario que se llame a esta función antes de llamar a la función de inicialización del driver (función numero 3), aunque si no se hace, se tomara como defecto el canal 0.

        Entrada:      BX = 19
                      AX = Nuevo canal de DMA
        Salida:       Ninguna


Notas Finales

Hay que tener mucho cuidado con el orden en que ejecutamos las funciones, y la manera en que pasamos los parámetros. Así pues debemos procurar siempre, efectuar una llamada a las funciones que permiten establecer la dirección base, la IRQ y el canal de DMA, antes de efectuar una inicialización de driver. Igualmente, antes de poder ejecutar un '.VOC' tenemos que efectuar dicha inicialización, ya que si no nos podemos encontrar con la desagradable sorpresa de que se nos cuelgue el ordenador...

El CT-VOICE no solo sirve para la salida de datos por la SoundBlaster. También sirve para la 'entrada' de los mismos a través de las líneas de MIC o LINE IN de la tarjeta. Pero como aquí lo que nos interesa es que la SoundBlaster 'toque' lo que nosotros queramos, no las hemos incluido. Si de verdad os interesa, comunicárnoslo, y haremos algo al respecto, si además en el mensaje nos notificáis que sois o que queréis ser colaboradores del CPV, pues entoncés daremos palmas con las orejas y to'. 8-) :-)

El driver CT-VOICE no es perfecto. Mas bien yo diría que es una patata cocida. En principio, cada tipo de tarjeta (véase SoundBlaster 2.0, PRO, 16 bits, 16 bits con ASP ...) tiene su propio CT-VOICE, y este no es utilizable en otro tipo de tarjeta. Es probable que la demo que vendrá después, si vuestra tarjeta no es la misma que la mía (una SoundBlaster PRO) no os funcione. La solución esta en tener el driver de todas las posibles tarjetas, y que nuestro jugador elija una de ellas al comenzar a jugar.

Seguro que a alguno de vosotros se le podría ocurrir, intentar probar una a una todas las posibles configuraciones y para cada una de ellas, intentar inicializar la tarjeta. Si obtenemos error probamos con otra, y si no, es que hemos detectado la configuración. Pues en principio es una buena idea, lo que pasa es que no funciona. Mi vecino tiene una SB 16 bits con ASP, intente hacerlo, y mediante este método me detectó la configuración base = 0x240, IRQ=2, DMA=3 y.... no acertó ni una, es que ni una sola. Así que mejor que antes de empezar a jugar, le pidáis al usuario que introduzca dichos parámetros y os quitáis de problemas.

Incluso con todas las precauciones posibles que podáis tomar. El 50 por cien de las veces que intentéis programar el CT-VOICE no os va a funcionar. Y el otro 50 por cien os funcionará a vosotros, pero no a vuestro vecino. Por cualquier paso mal dado, deja de funcionar. Así que, como en todo lo relacionado con este mundo de los videojuegos, al final lo mejor es programarse una rutina propia a nivel de puertos que realice esta tarea. Esto es lo que haremos en el próximo capítulo. Además esto nos permitirá trabajar directamente con los datos a enviar a la SoundBlaster con lo cual podremos intentar hacer algún efecto curioso (ecos, invertir el sonido, mezclar varios sonidos...). Os recomiendo que para entonces os repaséis el artículo "Naturaleza del sonido" de la primera lección y de paso que solicitéis si no lo habéis hecho ya las lecciones por correo para así aseguraros de que recibiréis la nueva lección con todos los fuentes.

Bueno. No podíamos terminar este artículo sin un programa demostración Esta demo os pedirá que introduzcáis que tarjeta tenéis, que dirección base, que IRQ y que canal de DMA, y a continuación el nombre del fichero '.VOC' que queráis ejecutar. Si tenéis suerte y los hados están de vuestra parte, conseguiréis oír el sonido, sino, mala suerte y revisar que no hayáis introducido ningún dato mal.

Pulsa F2 para ejecutar el programa CT_DEMO.   - Posibilidad no disponible en la versión HTML-

Nosotros sólo hemos incluido en la lección el driver de la SB PRO. Para que funcione el programa con cualquier otro de los modelos de la gama Sound-Blaster deberéis modificar el programa para que cargue el driver correspondiente. Tomaros esto como unos deberes. ;-)
 
 

4.8.- Algorítmica. Estructura cola.

LAS COLAS

En la anterior lección estudiamos una parte de la algorítmica de los videojuegos, esto eran los algorítmos de colisión. Ahora vamos a tratar otra estructura imprescindible para la mayoría de nuestras creaciones. Se trata de las colas.

Todos tenemos un concepto de esta palabra bastante claro, las colas del metro, del cine, del Inem... y un método bastante claro para entrar en ellas, las frases ¿Quién da la vez? o ¿Quién es el último? Je Je ;-) Pero no, no estamos hablando de estas colas, sino de una de las estructuras de datos más utilizadas en la programación.

Existen dos grandes organizaciones de datos a este nivel. Las colas y las pilas. Su utilidad es la misma, almacenar termporalmente una serie de elementos en espera de ser procesados, sin embargo su funcionamiento es totalmente opuesto.

Las pilas siguén un modo de funcionamiento conocido cómo LIFO, del inglés Last Input First Output, usease el último en entrar el primero en salir. Según esto los elementos que se sacan de la pila serán los últimos que introducimos. Un ejemplo general podía ser una pila de libros, dónde lo más normal es coger los libros que están encima, (que serán los últimos que se pusieron) de coger los de otras posiciones, lo más seguro, sería que se os caigan todos los libros encima y el de Física que será el más gordo en toda la uña.... X-O En Informática, estas estructuras se utilizan por ejemplo en la llamadas a subrutinas, para almacenar los valores de retorno y los parámetros de las mismas. Su mal uso o pequeño tamaño suele desencadenar en un conocido error llamado desbordamiento de pila.

Pero lo que a nosotros nos interesa son las colas, y no quiero que nadie piense mal, ¿ta' claro? ;-D Las colas funcionan según un esquema FIFO o First Input First Output, donde el primero que entró será el primero en salir (como debe ser, no vale colarse). Las colas se utilizan en multitud de areas informáticas (dígase colas de impresión, colas de procesos en S.O...) y una de ellas son los videojuegos.

Su razón de ser es bastante clara. El mundo sería muy bonito si hubiese sitio para todos en el metro, entradas para todo en el cine, trabajo para todos pero no es así y es necesario establecer un orden de prioridad. Con la informática en general y los videojuegos en particular nos pasa lo mismo. Sería ideal tener un procesador para cada tarea que queramos realizar y que estas fuesen atendidas según lo solicitan pero no es así.

Os enuncio como ejemplo los procesos normales en un videojuego a los que se debe atender:
 


Ante este cúmulo de procesos que se crean, no nos queda más remedio que establecer una serie de colas para organizar y priorizar el tratamiento de todo ellos.

Bien, ya os he contado el porqué de las colas ahora os voy a contar el cómo poniendos un ejemplo real de una cola en un videojuego. Imaginemos que queremos diseñar un juego dónde manejaremos un personaje el cual puede disparar. Cada vez que dispare debemos introducir una entrada en nuestra cola de disparos. Un ejemplo de esta estructura podía ser la siguiente:

        Const
          MAX_ELEMENTOS = 10;

        Type
          TipoDisparo:record
                       X:integer;
                       Y:integer;
                       Retardo:integer;
                       RetardoIni:integer;
                      end;
          TipoColaDisparos:record
                            NumeroDisparos:integer;
                            Disparos:array[1..MAX_ELEMENTOS] of TipoDisparo;
                           end
        Var
          ColaDisparos:TipoColaDisparos;

Por cada disparo del usuario lo único que debemos hacer nosotros es lo siguiente:

          ColaDisparos.Numero := ColaDisparos.Numero + 1;
          ColaDisparos.Datos[Numero].X := PosX (*Posición inicial de la bala*)
          ColaDisparos.Datos[Numero].Y := PosY
          ColaDisparos.Retardo := RETARDO_INICIAL
          ColaDisparos.RetardoIni:= RETARDO_INICIAL

Y ya está con esto meteríamos todos los disparos que realize la hormiga en una cola, pero.... nos falta algo.... Lo anterior sería comó el ¿Quién da la vez? de las colas que os conté al principio, ahora... necesitamos la taquillera para esta cola.

En informática los procesos que atienden a las colas se suelen llamar manejadores. En nuestros programas tendremos un manejador para cada cola que creemos. Los manejadores debes estar activados continuamente, como esto no es posible en un entorno monotarea, hay que emular esta característica en base a repartir el tiempo de proceso. En nuestros juegos tendremos siempre un bucle principal que ejecutará unas detrás de otras todas las rutinas del programa, entre estas estarán los nombrados manejadores, las rutinas de visualización de gráficos, de emisión de sonidos, de actualización de marcadores...

Pero volvamos a los manejadores, el modo de funcionamiento de estas rutinas es bastante sencilla. En cada paso del bucle comprobaremos todos los elementos de la cola y para cada uno de ellos lo primero que haremos será comprobar el valor del campo retardo. Este campo indica el número de ciclos del bucle principal a esperar para procesar el elemento.

Decrementaremos el retardo en cada ciclo, si no es cero continuamos normalmente y si el valor es 0 actuaremos sobre el elemento de la cola y pondremos el valor retardo con su estado inicial (RetardoIni). Cómo veis con estos valores podemos asignar una prioridad a todos los elementos que introduzcamos en nuestra estructura.

El tratamiento de cada elemento de la cola dependera de lo que sea cada uno, por ejemplo, un disparo, un enemigo, una explosión...

Para finalizar con este artículo os voy a enseñar la demostración de construmbre. En alguna encuestas algunos solicitáis más info de como realizar un videojuego, pues bien, por ello hemos decidido continuar con el juego 'er Komekokos' que os presentamos en la lección anterior y así construir un juego poco a poco. Entonces vimos cómo mostrar gráficos y detectar colisiones en la pantalla, vamos a dar un paso más en nuestro particular juego introduciendo unos pocos de enemigos. Estos, los vamos a organizar en una colas que controlarán su tiempo de vida, su tipo y velocidad. Comprobad el resultado...

Pulsa F2 para ejecutar el programa ALGOCOLA  - Posibilidad no disponible en la versión HTML-

Como véis es bastante simplón el resultado. La técnica que utilizamos para el movimiento de los enemigos es la más sencilla que se pueda imaginar, esto es, mover los enemigos en linea recta hasta que 'choquen' y en ese momento darles una nueva dirección, de entre las dos posibles, (La dirección que llevaba y la opuesta no cuentan). Como os daréis cuenta esta implemen- tación tiene muchos pero que muchos defectos, destacaré algunos:

- Al solo cambiar de dirección en las colisiones, existen distintas partes del mapa del juego, que no van a ser recorridas nunca. Lo ideal sería que las decisiones se tomasen en el momento de encontrarse con una posible nueva dirección, un cruce de caminos vamos....

- Como la decisión de coger unos de los dos caminos posibles la tomamos de forma aleatoria, los enemigos son un poco 'estupidos' y si nos cazasen sería de pura chorra. Otra solución también sencilla y más acertada sería darles 'inteligencia' a los enemigos facilitandoles la posición de nuestro pacman, con la cual ellos podrían calcular la nueva ruta y así tendrían más fácil hacernos la puñeta.

Todo esto requeriría mucha más lógica en el programa y la introducción de nuestro protagonista y .... vamos que tendríamos ya el juego terminado y de nuevo algunos os perderíais entre tanto código. Por el momento conformaros con entender el funcionamiento de las colas en los videojuegos, que de eso trata el artículo, e ir asentado los conocimientos que cogáis para que, si en el próximo artículo continuamos y "terminamos" er Komekokos, hayáis entendido la construcción de un videojuego paso a paso.

Bueno aquí acaba este artículo, esperamos que nos sigáis sugiriendo temas de algorítmica para explicar, os ayudaremos en lo que podamos. :-D
 
 


4.9.- Técnicas gráficas avanzadas.

Ray Tracing.

- Por José María Peña Sánchez -

Antes de nada me gustaría dejar claro que este primer capítulo de introducción al Ray Tracing será bastante teórico, cosa que es totalmente necesaria para comprender los capítulos siguientes, de todas formas intentaré hacer todo este 'ladrillo' lo más ameno posible :) .

El primer concepto que me interesa recalcar es el objetivo primordial del Ray Tracing, que apunta directamente a la visualización de un mundo virtual representado dentro del computador, por ejemplo: nosotros podemos imaginarnos un sistema de coordenadas espaciales x, y ,z y dentro de ellas localizar una serie de objetos : en el punto (2,5,-2) tengo una esfera de radio 4 del color tal y en el punto (3,7,15) un cilindro de tales o cuales características, ... esta representación nos sería enormemente fácil de implementar en un ordenador (mediante unas estructuras con unos campos: posición, tipo de objeto, color) Así pues, nuestro objetivo es poder VER ese mundo en el ordenador y no solo desde una posición fija sino que podamos MOVERNOS por él y ver sus objetos desde donde queramos.

Una vez planteado el objetivo vamos a empezar a buscar una solución para nuestra idea, y para ello nos fijaremos en como el ojo humano ve el mundo exterior:

Figura 1:        /
                 /                Como vemos en la figura 1 la luz
        |\     /                 procedente del mundo exterior entra
        |  \ /    MUNDO          por nuestra Pupila en forma de rayos
        |   0   EXTERIOR         e impacta en nuestra Retina, con la
     *->|  /i\                  cualidad de que, por el tamaño de la
     |  |/  |  \                Pupila, a cada punto de la Retina le
     |   Pupila  \              alcanza un único rayo (en realidad le
   Retina          \             alcanzan varios, pero este número es
                                 proporcionalmente tan reducido en comparación
con los que la alcanzarían si no existiese ese único punto de entrada, que
asumimos que es solo uno). Este funcionamiento es a su vez el principal
fundamento de las máquinas fotográficas.

Y ahora supongo que os preguntaréis para que nos sirve todo este rollo óptico para nuestro problema, pues imaginaros que pudiéramos dividir nuestra retina en una serie de puntos (por ejemplo 320 x 200, oh que casualidad) y que para cada uno de esos punto (mediante el rayo que le llega) pudiéramos determinar un color, entonces podríamos representar ese mundo exterior en una pantalla de ordenador, esto parece mucho más prometedor.

Ahora vamos a cambiar ligeramente nuestro modelo y para evitar que las imágenes entren invertidas en nuestro 'ojo computacional' vamos a poner nuestra Retina (que a partir de este momento llamaremos Plano de Imagen) delante de la Pupila (que desde ahora llamaremos Ojo): Para que lo entendáis imaginaros mirando por una ventana un paisaje, a cada punto del cristal de la ventana le corresponderán unas figuras diferentes.

Y ahora suponed que delante de esa ventana colocamos una rejilla tan fina que a cada orificio le corresponde no una figura, sino solo un color; nosotros podríamos ir copiando punto a punto el color de cada agujero uno en cada pixel y asé formaríamos una imagen que correspondería a lo que nosotros vemos desde esa ventana.

Hasta ahora hemos hablado del color de cada uno de esos puntos del Plano de Imagen, que como podemos suponer nos viene dado por el rayo que pasa por ese punto y llega hasta el Ojo, pero ¿Desde donde ha viajado ese rayo?: Como vosotros sabréis cada color que percibe el ojo humano es fruto de un rayo original (supuestamente blanco) que impacta en algún objeto que tiene una cualidad que llamamos color que es simplemente la capacidad de absorber  o rebotar cada uno de los componentes del rayo de luz blanca, es decir cada uno de los colores que forman la luz blanca.

Esto nos puede dar una primera idea de como representar nuestro mundo virtual en la pantalla del ordenador; si nosotros tenemos una serie de objetos, una fuente de luz y un punto y un plano de proyección, nosotros podemos lanzar desde la fuente de luz multitud de rayos que rebotaran por toda la serie de objetos de nuestro mundo seleccionando el color final del rayo y finalmente impactarán en nuestro punto de proyección, atravesando el plano por una coordenada, eureka, tenemos el color de ese punto. Además, podemos estudiar diversas situaciones como la no existencia de fuentes de luz: que hace que no se genere ningún rayo por lo cual no se puede determinar ningún color (en la oscuridad nuestro ordenador no 've' nada), o como el caso de existir diversas fuentes de luz, caso en el que se lanzan todos los rayos y para cada punto del plano de proyección componemos todos los rayos que lo han alcanzado.

Nuestra forma de representar el mundo parece bastante buena, hasta que intentamos hacer un programa que la simule, momento en el que nos encontramos con los siguientes problemas:

-¿Cuantos rayos hay que lanzar desde cada fuente de luz para poder tener un color en cada punto del Plano de Imagen?

-¿Cuando dejamos de rebotar entre los objetos del mundo virtual?

La segunda de las cuestiones será una de las cuestiones mas problemáticas en todo método de Ray Tracing que use modelos de luz u objetos con cualidades especulares.

Sin embargo el primero de nuestros problemas es mucho más urgente, porqué no sabemos cuantos rayos son necesarios lanzar desde la fuente de luz por cada punto del Plano de Imagen, porqué existe un altísimo porcentaje (más del 99,99) de los rayos que nunca impactarán en el Ojo, simplemente imaginaros mirando por vuestra ventana, si consideramos al sol como única fuente de luz, ¿Cuantos rayos tendremos que lanzar desde él, para que, después de un número moderado de rebotes, pasen por nuestra ventana y entren en nuestro ojo?, el número de rayos perdidos es inimaginable y lo que es peor aun, no tenemos ninguna forma de reducir este número, porque si desperdiciamos rayos que no van en la dirección en la que se encuentra el Ojo, podemos perdernos objetos situados detrás de la fuente de luz. En resumen no existe ningún criterio para eliminar direcciones de rayos desde la fuente de luz, ni tampoco existe ninguna cota superior que nos asegura que la retina ha recibido, como mínimo, un rayo por cada punto de la misma.

PRIMER MODELO DE RAY TRACING

Después de la desilusión sufrida por haber encontrado obstáculos insalvables en nuestro primer modelo, vamos a presentaros el primer modelo REAL de Ray Tracing, se trata de un modelo MUY simple que carece de procedimientos para acelerar el trazado de la imagen, así como de multitud de efectos que añaden realismo, pero creo que con los ligeros guiones remarcados en la introducción podrá ser entendido sin ningún problema.

Ahora vamos a enumerar las características de este modelo así como la nomenclatura que usaremos para definirlo (no se trata de una nomenclatura estandarizada puesto que cada autor usa una propia):

-Objetos del mundo virtual:                            (SPHERE & PLANE WORLD)
         -Esferas: Usaremos esferas lisas de un solo color y sin texturas.
         -Planos infinitos: De un solo color y sin texturas.
-Fuentes de luz:                                       (AMBIENT LIGHT)
         -Luz de ambiente: No existen fuentes de luz de ningún tipo, se asume
          que la escena está iluminada por una luz constante en todos los
          puntos.
-Cualidades de los objetos:                            (NONE)
          No se usaran cualidades reflectivas ni refractivas de los objetos
          así como ningún efecto de niebla o agua, el fondo será un color
          liso uniforme (BACKGROUND COLOR) normalmente negro y no un paisaje.

Antes de empezar con la enumeración de los diferentes procedimientos que debemos implementar vamos a explicar brevemente, antes de especificarlo en detalle en cada función, el objetivo fundamental de este modelo:

Como principal característica del modelo es necesario destacar la ausencia de fuentes de luz concretas que para empezar no sabráamos tratar con nuestro anterior acercamiento al problema, porqué en este ejemplo nosotros no tenemos uno o varios puntos desde donde empezar a lanzar los rayos sino que la escena esta iluminada con una luz en principio procedente de todos los lados, para que os hagáis una idea, es como si todas las paredes de la habitación pudiesen emitir luz, causa que hace que empezar a lanzar rayos de luz hacia adelante (FORWAD RAYTRACING) sea un trabajo totalmente ineficiente, por eso usamos la técnica que se conoce como BACKWARD RAYTRACING.

BACKWARD RAYTRACING: Invertimos el concepto que hasta ahora tenemos de ray tracing, y en vez de buscar un rayo que impacte en la retina, buscamos, de los rayos que impactan en la retina, la fuente originaria, como esto puede ser un poco extraño usaremos un ejemplo de nuestro modelo:

Imaginaros tres planos que se cortan, similar a la esquina formada por dos paredes y el suelo, formado 90 grados y una esfera en un punto próximo a una de las paredes, por ejemplo la izquierda y una segunda esfera incrustada en la intersección entre la pared derecha y el suelo. Ahora imagináos que miramos esta escena mediante una rejilla que forma una matriz de celdillas, si nosotros nos fijamos en cada una de las celdillas desde un punto fijo (nuestro ojo) podemos trazar un rayo que partiendo desde el OJO pase por ese elemento o celdilla del PLANO DE IMAGEN y continúe a lo largo de la imagen, si calculamos el primer elemento que choque con ese rayo, y como asumimos que las escena esta uniformemente iluminada (sin sombras), podemos marcar esa celda con el color del objeto al que hemos impactado pues todos los objetos del escenario están perfectamente iluminados, es decir, supones que desde algún lugar que a nosotros no nos importa le llega a ese punto suficiente luz para que nos rebote hacia nosotros un rayo de su color.

Parece que este método funciona mejor que el anterior porque nos asegura, en principio, que no vamos a realizar trabajo inútil al trazar rayos que nunca pasarán por el plano de imagen; nosotros tenemos una serie de puntos que componen la parte de la imagen que nosotros queremos dibujar en pantalla y mediante la realización de este proceso de lanzamiento de rayos para cada uno de los puntos podemos componerla pixel a pixel.

Una vez explicado, un poco por encima, el funcionamiento del modelo vamos a enumerar e ir explicando cada una de las funciones que sería necesario implementar:

-GENERADOR DE RAYOS: (NOTA:No se trata de ningún artefacto alienígena) Será la función encargada de generar un rayo para cada pixel, considerando como rayo, un punto origen del rayo y una dirección de propagación (es decir un rayo estará compuesto por dos vectores, 6 números).

Este generador de rayos serán un par de bucles anidados que recorran toda la matriz de la ventana de dibujo desde donde nosotros queramos ver el escenario, para ello debe tanto conocer las dimensiones de dicha ventana, como el punto donde esta localizado el ojo y el punto al que este mira. Los rayos generados por este proceso son pasados a la siguiente función:

-CALCULAR RAYO: o más exactamente debería llamarse Calcular Color del Rayo, y que es la encargada de determinar con que objeto impacta este rayo y cual es su color.

En nuestro primer programa el criterio que utilizamos para este proceso es, el de calcular la distancia de intersección con cada uno de los objetos y tomar la menor, teniendo en cuenta que en muchas ocasiones un rayo no tiene porqué impactar con todos los objetos del escenario, en el ejemplo antes citado los rayos que impacten en la esfera incrustada en la pared puede que también lo hiciesen en dicha pared, pero por detrás de la esfera.

En el caso en el que el rayo no colisione con ningún objeto suponemos que ese rayo se ha perdido y se le asigna a su pixel el color perteneciente al fondo de la escena, en caso contrario tomamos como color del pixel el relativo al objeto con el que hemos impactado (en otros modelos posteriores no nos será suficiente con conocer el objeto con el que hemos impactado sino que necesitaremos otros datos si deseamos asignar texturas a los mismos o usar cualidades reflectivas)

En el caso particular de nuestro modelo tenemos dos tipos de objetos(esferas y planos) por lo tanto esta función debe recorrer cada uno de los elementos del vector de objetos (estructura donde hemos almacenado los objetos del escenario) y dependiendo si es plano o esfera calcular su posible intersección con el rayo, para esto debemos tener sendas funciones para calcular dichas intersecciones.

-INTERSECCION CON PLANO: Esta función determina simplemente si un rayo, representado por un vector, intersecciona con un plano.

Llegados a este punto debemos resolver un ligero problema, consistente en el formato que vamos a usar para representar los planos, con el principal objetivo que nos simplifique esta función. Una solución (que no la única) consiste en representar los planos mediante ecuaciones del tipo:

                          Ax + By + Cz - D = 0

Donde nosotros tenemos que almacenar únicamente los valores de las constantes como campos de la estructura de datos objeto (A,B,C,D). Para calcular el punto de intersección entre una recta y un plano debemos repasar nuestros oxidados conocimientos matemáticos al respecto, que nos dicen que cada punto de la recta se puede representar como:

          x = O.x + t*Dir.x            Ecuaciones paramétricas de
          y = O.y + t*Dir.y            una recta.
          z = O.z + t*Dir.z

Ecuaciones que si introducimos en la ecuación del plano dará:

      A (O.x + t*Dir.x) + B (O.y + t*Dir.y) + C (O.z + t*Dir.z) - D = 0

Que una vez simplificada nos da:

      t = -(A * O.x + B * O.y + C * O.z + D) /
            A * Dir.x + B * Dir.y + C * Dir.z

Y como podemos suponer la condición para que exista intersección es que el denominador sea no nulo, pero también si el denominador es mayor que cero, el punto de intersección estará detrás del punto de origen (t<0).

-INTERSECCION CON ESFERA: De forma análoga a lo anterior tomamos una posible epresentación para la ecuación de una esfera:

                     (x - A)^2 + (y - B)^2 + (z - C)^2 = R^2

Y usando las mismas ecuaciones paramétricas de la recta nos resulta:

  (O.x + t*Dir.x - A)^2 + (O.y + t*Dir.y - B)^2 + (O.z + t*Dir.z - C)^2 = R^2

Realizando los cuadrados y reorganizando los términos nos queda el espantoso chorizo de:

  (Dir.x^2 + Dir.y^2 + Dir.z^2) * t^2 +
  2 *((Dir.x * (O.x - A)) + (Dir.y * (O.y - B)) + (Dir.z * (O.z - C))) * t +
  (O.x - A)^2 + (O.y - B)^2 + (O.z - C)^2 - R^2 = 0

De buenas a primeras esta ecuación puede parecer monstruosa, pero si nos paramos a observarla descubrimos que no es más que una ecuación de segundo grado (eso si, bien jugosa) donde cada uno de los términos son:

 a = (Dir.x^2 + Dir.y^2 + Dir.z^2)
 b = 2 *((Dir.x * (O.x - A)) + (Dir.y * (O.y - B)) + (Dir.z * (O.z - C)))
 c = (O.x - A)^2 + (O.y - B)^2 + (O.z - C)^2 - R^2

Y se puede resolver mediante la formula:

 t = (-b +-sqrt(b^2 - 4 * a * c)) / 2 * a

Formula que solo tiene sentido si b*b - 4*a*c es positivo o cero, es decir cuando la recta corta a la esfera en dos puntos o cuando es secante a ella respectivamente, porque en el caso de que dicha expresión sea menor que cero el rayo no impacta en la esfera.

Una vez determinado que el rayo si choca contra la esfera y si la intersección son dos puntos hemos de elegir el menor de ellos.

En resumen para cada rayo hemos de recorrer todo el vector de objetos y para cada uno de los objetos determinar si choca o no, y en caso afirmativo, cual es el valor de 't' y de entre todas las 't' tomadas, seleccionamos la menor.

Aquí tenéis una sencilla demostración del primer modelo de Ray Tracing que os he estado comentado:

Pulsa F2 para ejecutar el programa Raytrac1.  - Posibilidad no disponible en la versión HTML-
 

PROBLEMAS DEL MODELO

Como os daréis cuenta este modelo es MUY MUY MUY LENTO, sobre todo para el grado de realismo que proporciona y teniendo en cuenta que nosotros queremos movernos por un mundo generado de esta forma, si para cada paso tenemos que esperar un minuto para que dibuje las escena puedes morirte. Esta lentitud es debida entre muchas otras cosas a:

 - El uso de coma flotante en todos los datos, en lugar de datos de tipo entero: Una operación con enteros incluso de 32bits, si usamos una compilación para máquinas que permiten dicha aritmética (386+), es MUCHO más rápida que el uso de la coma flotante, sobre todo para máquinas que no disponen de coprocesador matemático. Pero para el uso de valores enteros hemos de tener mucho cuidado con la escala de mundo virtual que estamos usando, porque una mala conversión de un código para coma flotante a entero puede resultar el solapamiento de objetos muy cercanos entre si y de pequeño tamaño o si están demasiado cerca del visualizador, problemas que trataremos en próximas entregas.

 - La elección de objetos del mundo virtual: Como podéis imaginar el tiempo necesario par determinar la intersección entre un plano y un rayo es mucho menor que el requerido para el caso de una esfera. La raíz cuadrada que hay que realizar en el segundo caso retarda fuertemente esta función, e incluso si operamos con valores enteros, casi todas las librerías matemáticas realizan sus operaciones con números en coma flotante que si tenemos que convertir tampoco ganamos mucho tiempo, una solución es aplicar algoritmos para calcular raíces cuadradas enteras mediante métodos de restas sucesivas, fácilmente implementables en ensamblador (ver próximos capítulos)

 - Recorrido del vector de objetos: Tal y como hemos propuesto la función que determina el color del rayo de cada pixel, tenemos que recorrer para cada punto todo el vector, y si queremos por ejemplo añadir una pequeña esfera en una esquinita de la ventana, tendremos que comprobar su intersección con cada uno de los rayos desde el principio de la ventana. Este problema es resuelto mediante una buena organización del vector que haga que para un rayo determinado no de tenga que recorrer más del 10 por cien del mismo, en la peor situación (BOUNDING BOXES y otras ordenaciones jerárquicas que serán expuestas en siguientes capítulos).
 

Pues hasta aquí hemos llegado, espero que os sean útiles estas indicaciones aunque no hemos entrado ni una centésima parte en materia y quedan muchas cosas que aprender, pero creo que para toma de contacto es un buen principio, quizás ligeramente teórico, pero que nos facilitará mucho el camino para ir más deprisa en próximas ediciones, en las que trataremos:

        * Aumento de velocidad:   - Raytracing sobre Enteros
                                  - Bounding Boxes
                                  - etc ...
        * Aumento de realismo:    - Modelos de luz y sombras
                                  - Varias fuentes de luz
                                  - Reflejos
                                  - Penumbras
                                  - Objetos Translúcidos
                                  - "Antialaising"
                                  - Nuevos objetos
                                  - Efectos de niebla
                                  - Representación del agua (olas)
                                  - Representación de nubes
                                  - Objetos de revolución
                                  - Texturas y "Patterns"
                                  - Paisajes como fondos
                                  - Añadiendo animaciones
                                  - Montañas fractales
                                  - Introducción de sprites
                                  - etc ...

Y como principal estrella de los próximos capítulos:

             RAYCASTING: Técnicas de juegos como DOOM o WOLFSTEIN

que si bien difieren del concepto primitivo de raytracing tiene en el su base y comparten la mayoría de las técnicas más espectaculares.

Hasta la próxima entrega del CPV, Saludos.

Si tenéis alguna duda o sugerencia podéis escribirme a:

                preferiblemente ---> jmpenya@clio.ls.fi.upm.es
                                     a910347@zipi.fi.upm.es

AGRAGECIMIENTOS: A Benjamín por su santa paciencia al esperar que terminase de escribir estas líneas que le prometí hace mucho tiempo. Y a mi hermano Luis por dejarme usar el ordenador que siempre ocupa jugando.
  


4.10.- Efectos especiales y demos.

Campo de Estrellas

- Por Jesús de Santos Garcia -

Seguro que casi todos vosotros, y sobre todo aquellos a quienes les guste las demos gráficas, habéis visto algún campo de estrellas. Este es un efecto tan espectacular como sencillo de realizar y por esta razón, es un buen efecto para incluirlo en el CPV.

Vamos a ir poco a poco explicando como hacer un campo de estrellas y luego que cada uno le de su toque personal (hay muchas opciones). Aquellos que tengáis conocimientos de programación gráfica en 3D lo tendréis más fácil, pero poco más. Por tanto, nadie tiene derecho a desanimarse en este tema. ¡Está chupao! ;-D

El motor del programa es la proyección de cada estrella, que vamos a representar por un pixel en pantalla. En un principio las estrellas las manejamos en coordenadas 3D, pero para poder representarlas en pantalla es necesario transformarlas a 2D mediante una fórmula que ahora daré (el que ya la sepa, muy bien, un positivo). Vamos a intentar razonarla nosotros mismos, pero no matemáticamente sino intuitivamente. Partimos de un sistema de coordenadas, donde el punto (0,0,0) está justo en el centro de la pantalla, el Eje XX es el eje horizontal (positivo a la derecha), el Eje YY es el eje vertical (positivo arriba) y el eje ZZ es el eje que se sale de la pantalla (zona negativa) y se mete hacia adentro (zona positiva).

Pues bien, vamos a situar un punto cualquiera (x,y,z) en nuestro espacio. Y pensamos... (venga un esfuerzo ;-DD) ¿qué pasaría si ese punto se fuera alejando cada vez más, es decir, que su coordenada Z aumentara? Pues muy sencillo, que sus coordenadas tenderían cada vez más al centro de la imagen hasta llegar a un punto en que ya no se moviera de su posición (estaría muy, muy lejos). Veámoslo gráficamente, bueno, mejor dicho, Asciimente X-DD

                            La estrella se aleja...

                     Y¦     /Z                       Y¦
                      ¦   /*2                         ¦    *1
                      ¦ /*1                           ¦ *2
                ------+------X                  ------+------X
                    / ¦                               ¦
                  /   ¦                               ¦
                /     ¦                               ¦

                    en 3D                           en 2D

Este es el horizonte hacia el cual tienden las estrellas. La fórmula que me permite hacer está proyección no podía ser otra más que esta (a que ya la sabías):

                    X=X/Z                          Y=Y/Z
 

A esta fórmula le vamos a añadir un nuevo factor, que llamamos D y que representa la distancia del observador a la pantalla. Por tanto el resultado final es este:

                   X=D*X/Z                      Y=D*Y/Z

Casi siempre se suele tomar D como 256, ya que es un número de los mágico. Multiplicar por 256 (o cualquier potencia de 2) es simplemente mover el número bit a bit n posiciones a la izquierda (8 para 256).

La función que hace esto, es la siguiente en C (lo siento por los pascaleros):

void point3d(STAR org,DOSD *proyec) {
       long a,b;
       if(!org.z) return;      /* Evitamos un divide error */
       a=(long)org.x<<8;       /* a= X*256                 */
       a/=(long)org.z;         /* a= a/z  ->  a= 256 * X/Z */
       b=(long)org.y<<8;       /* b= Y*256                 */
       b/=(long)org.z;         /* b= b/z  ->  b= 256 * Y/Z */
       proyec->x=(int)a;       /* Actualizar X             */
       proyec->y=(int)b;       /* Actualizar Y             */
       }

Creo que está claro lo que hace, y si no, la tenéis perfectamente comentada.

Ahora pensemos en cada una de las estrellas, su movimiento es muy sencillo ya que lo único que hacen es acercarse a la pantalla (es decir, restan una cantidad a su coordenada Z). Lo que tendremos que ir haciendo, en consecuencia, es ir restando la Z de la estrella hasta que esta desaparezca por algún borde, o 'se estrelle contra la pantalla'.

Las coordenadas X e Y permanecen constantes. Si al proyectar el punto no entra en la pantalla o simplemente su coordenada Z es 0, entonces ese punto desaparece y se vuelve a crear otro al fondo.

Al pintar la nueva posición de la estrella, es importante que la anterior posición haya sido borrada. Hay dos formas de realizarlo:

-Borrar la posición anterior de cada punto
-Borrar toda la pantalla

El primer método es más eficaz cuando sólo hay una página de vídeo disponible. El segundo, es más apropiado cuando se tienen varias páginas. Aun así, el mejor método es la suma de ambos. Es decir, usando varias páginas y borrar las anteriores posiciones.

Esto hecho para cada uno de los puntos, es lo que genera el campo de estrellas. No me digáis que no es fácil. :-) Pues ahora, a programarlo :-oo

         Estos son los pasos que hay que dar:

     1. Lo primero es iniciar el modo gráfico, el que más o guste, si es un
              modo X con varias páginas mejor.

     2. Preparar la paleta. Esto es muy importante. Ya que el efecto de
              profundidad de las estrellas viene dado únicamente por su
              color (recordar que todas son del mismo tamaño). Algunas
              ideas: de negro a blanco, degradado de azules... es cuestión
              de probar y encontrar alguna combinación explosiva.
              Una vez creada la paleta, cada estrella será pintada con un
              color en función de su Z. Cuando la Z sea mayor más oscuro
              el color, y cuanto más cerca esté el punto más claro el color.
     3. Crear un determinado número de estrellas (500 por ejemplo) e ini-
              ciar sus coordenadas aleatoriamente.
     4. Y creamos un bucle de tantas estrellas como tengamos en el que
              hacemos lo siguiente:
              -Cambiar de la página activa a la visualizada y viceversa.
              -Borrar la pantalla activa. (que es la que no se ve)
              -Proyectar el punto a 2D.
              -Comprobar que entra en la pantalla o que su Z no es 0.
              -Si no entra en pantalla, eliminamos ese punto y generamos
               otro al azar. (Es necesario borrar el punto anterior)
              -Si entra en pantalla, lo pintamos.
              -Restar un valor a la coordenada Z y repetir el bucle hasta
               que se acaben las estrellas.

     5. Y así indefinidamente hasta que queramos o, por ejemplo, hasta que
              el usuario pulse una tecla.

Pulse F2 para ejecutar el programa CAMPO.    - Posibilidad no disponible en la versión HTML-

Bien, ¿qué os parece? He añadido algunas cosillas más para que quede mejor como las rotaciones 3D. Esto no lo he explicado porque no quería liaros más. Lo aprenderéis a fondo en los artículos del CPV sobre 3D.

A continuación os comento las cosas a mejorar:

-El modo gráfico es 320x200 4 páginas. No es el mejor escogido. Podéis probar con 320x400 2 páginas. Cuando hagáis esto, no valdrá con borrar toda la pantalla para eliminar los puntos anteriores. Es necesario que solo borréis los puntos anteriores para ganar velocidad. Los más atrevidos pueden probar con 360x480. ¿Alguien quiere más resolución? Pues ahí tenéis la SVGA, ¿se atreve alguien?

-Se le puede poner movimiento hacia atrás. Esto es fácil ¿eh? :-)

-Falta la música. A ver quien se anima... Existen varios programas para poner música, pero si usáis rutinas vuestras mejor que mejor. CPV va a tocar este tema a fondo (ver temario).

Nada más, espero que os haya gustado este tema. Si hacéis algo, tenéis alguna sugerencia, duda o demás, ya sabéis donde podéis contactar conmigo. Si el tema de las demos te atrae también puedes unirte a mi grupo "INCOGNITA".

       Un saludo de NT.

---------------------------------- Cortar aquí ------------------------------
Nota de JASM:

¿Qué, genial el artículo verdad?... Estos campos de estrellas son de los que hay que verlos con la bolsita para el mareo cerca ;-)

Pues nada, ya os ha comentado ¯NT que forma parte un grupo de demos, en los créditos de la lección podréis encontrar más información sobre ellos. Por nuestra parte esperamos dar todo el apoyo a INCOGNITA y a todos los grupos que están empezando en el mundo de las demos.
---------------------------------- Cortar aquí ------------------------------
 
 

4.11.- El juego de la lección.

Horminoid.

Como es costumbre en todos los artículos del CPV, os presentamos un juego como culminación de esta lección. En esta ocasión se trata de una versión del conocido juego ARKANOID con la particularidad que esta vez será nuestra amiga 'hormi' quien se encargará de ir destruyendo los ladrillos de la guarida del malvado 'scorpio'.

Debido a la epoca en que ha sido desarrollada esta última lección (justo durante los examenes finales de Junio...), dicho juego no esta todo lo acabado que debería. Así pues, la idea original era que el juego llevara la opción de sacar sonidos digitalizados por la SoundBlaster, según se explica en la lección, pero por falta de tiempo, esto no ha sido posible. Algo había que dejar para vosotros verdad ;-)

Otro pegilla que no hemos tenido tiempo de solucionar es sincronizar la máquina con el ordenador sobre el cual se ejecuta. El juego corre bastante rápido en nuestros ordenadores (486 a 50Mhz y 33Mhz), incluso se pasa de la raya. Es seguro que vosotros tendréis que utilizar algún tipo de desacelerador de la CPU que os permita retardar la velocidad del ordenador todo lo que queráis. Utilizadlo y contadnos que tal os va. De nuevo os reiteramos nuestras disculpas por lo apresurado.

El juego en si es muy simple. Mediante el teclado o mediante el joystick nosotros manejaremos a hormi por la parte inferior de la pantalla moviendola de derecha a izquierda, mientras, una pelota rebota por toda la patalla. El objetivo en cada pantalla es conseguir, gracias a los rebotes de la pelota en las paredes y sobre la pala que nuestra amiga hormi tiene sobre su cabeza, romper todos los ladrillos existentes en la pantalla. El juego termina cuando consigamos superar las diez pantallas que hemos diseñado para este juego.

Pulsa F2 para ejecutar el juego del mes HORMINOID.    - Posibilidad no disponible en la versión HTML-

Una de las características que más buscamos en nuestras creaciones es su estructura abierta, es decir, que una vez realizada la base del programa, sea fácil ampliar el juego y dotarle de mayores funciones. Un ejemplo de esta capacidad de ampliación es el fichero mapa.dat de que consta el juego Horminoid, en el se define el mapa de cada una de las pantallas (el tipo y la organización de los ladrillos) modificad este fichero y comprobaréis lo fácil que es diseñar nuevas pantallas a vuestro gusto.

Si ya nos ponemos a toquetear un poco el código la capacidad de ampliar sencillamente el juego se dispara, unos ejemplos de lo que se pueda hacer:

      - Aumentar el número de pantallas (de 10 a las que os deje la memoria.)
      - Con una combinación de teclas poder pasar de pantalla automáticamente
        o dar inmunidad a nuestro protagonista.
      - Dotarle de manejo de ratón.
      - Dotarle de sonidos SB.
      - Definir habilidades extras de la pala y la pelota, como:
            - Que puedan existir dos tipos de pala, corta y larga.
            - Que la pelota pueda aumentar su tamaño, así como disminuir
              aumentar su velocidad.
            - Posibilitar que la pelota se peque a la pala
      - y todo lo que se os ocurra. ;-)

Os habréis fijado que en nuestras creaciones os complicamos mucho vuestra existencia, esto es, que no se los termina ni Dios. O:-) De hecho en muchos de nuestros juegos nos cuesta bastante llegar al final. Por este motivo, que suelen sufrir todos los programadores, en todos los juegos se suele dejar una puerta trasera, cuya existencia sólo conocen los autores (y los crackers). Esta puerta se suele activar mediante una combinación secreta de teclas o algún parámetro en la línea de comandos y su objetivo es facilitar a los programadores avanzar a lo largo del juego con inmunidad total para probar todas la pantallas y todas las funciones del juego. Como nosotros no podíamos ser menos, en TODOS nuestros juegos tenemos habilitada una puerta trasera que os permitirá hacer de tester y llegaros al final de los programas.

Os vamos a desvelar el truco por una sencilla razón, los juegos los estamos desarrollando tan deprisa que no nos da tiempo a poder evaluar su dificultad (que seguramente será elevadísima) y con todo el trabajo que nos lleva hacer todas las pantallas y los finales de juego, sería una pena no daros la oportunidad de conocerlos enteros, por ello os vamos a dar la llave de nuestras puertas traseras, esta es poner como parámetro de nuestros juegos (Hormigator, Aliens Invaders y Horminoid) la palabra JABJA (de Jesús Angel Benjamín Juán Antonio), con ello activaréis una serie de habilidades extras como son:

        - En Hormigator: Elección de la pantalla de inicio, invulnerabilidad
          y flotabilidad al pulsar la tecla 9 del Keypad.
        - En Space Invaders, disparo continuo con el joystick y posibilidad
          de desplazamiento vetical.
        - En Horminoid, invulnerabilidad y variación de la velocidad de la
          pelota con las teclas + y - del keypad.

Estás habilidades no son más que pequeñas variaciones en el código, (por ejemplo, aumentar el valor de una cte, poner una variable booleana como muerte siempre a false o cosas así) que suponen grandes ventajas y cambios en el  resultado final. Estas puertas traseras no son más que una de las ventajas de utilizar estructuras y técnicas abiertas de programación.

Espero que uséis estas habilidades después de intentar superar el juego de una manera honrada y una última cosa, como se supone que esto sólo lo conocen los programadores, puede ser que el programa no funcione correctamente algunas veces (lo que pasa al Pentium vamos ;-) y puede llegar hasta el cuelgue total, avisados estáis, no todo iban a ser ventajas.

Nada más que deciros, espero que disfrutéis de Hormnoid, y lo más importante, que aprendáis de él todo lo que podáis, ya que éste, es el objetivo de los "Juegos de la Lección". Hasta la próxima...
 
 


4.12.- Despedida y direcciones.

Nos Vemos

En el número pasado os incluimos en este mismo lugar una enumeración de las posibles colaboraciones y tareas a realizar para el CPV, como esta lista es muy dinámica y necesita de explicaciones adicionales, no la exponemos aquí. Si estás interesado en realizar tareas o quieres preguntar por algún artículo que quieras realizar, pasate por el area local del CPV en Sakery FOX (tlf. 413 98 55) y/o deja un mensaje a los autores. En esa area encontrarás a bastantes seguidores del CPV y como no a sus autores.

Allí también podrás dejar la siguiente encuesta:

        1. ¿Cómo te enteraste de la existencia del CPV?
             A - A través de un amigo
             B - Vi un mensaje en una BBS
             C - Lo vi anunciado en el teletexto de TVE
             D - Me entere a través de las News de Internet
             E - Un chico alto y moreno me dio un diskette con las lecciones
                 me dijo que lo distribuyera y yo me quede con el diskette.

        2. Si NO eres tu uno de los 50 afortunados de la letra E anterior
           ¿Cómo has conseguido la lecciones del CPV?

        3. Sobre la nueva forma de distribución ...
           ¿Que te parece?
           ¿Cómo la llamarías ;-)?

        4. El precio de 500 pesetas con el que se puede conseguir cada lección
           por correo ¿Lo consideras acertado?

        5. De lo visto hasta el momento en las lecciones...
           ¿Qué es lo que te gusta más?

        6. ¿Qué es lo menos bueno?

        8. Sobre las futuras lecciones del CPV....
           ¿Que tema de los expuestos al comienzo esperas con más impaciencia?

        9. ¿Que temas añadirías o cuales te gustaría ver ampliados?

       10. Observaciones generales sobre el CPV

En las primeras lecciones del CPV nos aventurabamos a adelantaros los temas que ibamos a tratar en la siguiente lección pero con todas las variaciones que están sufriendo la planificación inicial, esto es casi como jugar a la lotería. De todos modos os voy a poner los dientes largos, comentadoos que para el número 5 pretendemos incluir entre otras cosas: Más modo X, más SB, más efectos gráficos, programación TSR y cantidad de trucos de programación, algorítmica y rutinas para la confección de videojuegos. Si queréis que todo esto llegue a realizarse pues nada, leed el mensaje:

Pulsa F2 para ejecutar el programa LOOK-UP.   - Posibilidad no disponible en la versión HTML-

Una vez expuesta la encuesta de rigor os voy a enumerar los distintos sitios donde podéis conseguir las lecciones del CPV de libre distribución:

En Fidonet:

******************************************************************************
                   Listado de BBS dónde el CPV está disponible
******************************************************************************
Nombre         * Teléfono       *  FIDO    * Sysop         * Observaciones
******************************************************************************
Sakery Fox     * (91)-413 98 55 * 2:341/5  * Alberto Ruiz  * Cuartel General
Deckard        * (91)-643 10 67 * 2:241/52 * Pedro de Paz  *
Cesin          * (91)-532 22 05 *          * Francisco Orte*
Charly         *                * 2:344/8  * Carlos Segura *
Psicosic       * (96)-515 53 13 * 2:346/12 *               *
Billie Jean    * (95)-265 53 38 *          *               *
La Escuela     * (91)-501 46 38 *          *               *
******************************************************************************

y en Internet:

Servidores de FTP

        asterix.upm.fi.es    en el directorio pub/facultad/alumnos/juegos
       dulcinea.alcala.es          >>> dirección a confirmar <<<

Para terminar nos gustaría dar las GRACIAS a todas las personas que están colaborando con nosotros en la producción del CPV:

Lo primero de todos quiero dar las GRACIAS y dedicar todo el CPV en general (con la venia de mi compañero) a mi abuelo Antonio Sánchez Fernandez, donde quiera que esté, por enseñarme con su ejemplo que el trabajo no es ningún castigo del hombre sino un medio para la superación y el desarrollo de la persona.

Aparte de esta pequeña dedicatoria queremos también dar las....:

Gracias a Jose Maria Peña Sánchez por su artículo colaboración.

Gracias a Jesús de Santos Garcia y al resto de miembros de INCOGNITA por su artículo colaboración:

               *************************************************
               *               INCOGNITA es                    *
               *************************************************
               * Pedro Eisman                    2:341/33.17   *
               * Sergio Fuertes                  2:341/33 (NP) *
               * Victor Azaustre ( Chewaka )     2:341/5.61    *
               * Sergio Perez    ( Brue )        2:341/72.8    *
               * Jesus de Santos ( ¯nt )         2:341/5.41    *
               *************************************************

Gracias a Juan Antonio Ubeda Torres por permitirnos utilizar sus gráficos para el CPV.

Gracias a Cristóbal Saraiba Bello por sus colaboraciones pequeñas en dificultad pero enormes en valor y significado.

Gracias a Sara Martín Miedes por las correciones a los artículos.

Gracias a la gente de Sakery BBS por los mensajes que me están mandando.

Gracias a todas las BBS que ayudan a distribuir el CPV.

Gracias de antemano a los que ayudéis con vuestra colaboración a que el CPV siga existiendo.
 


[ Anterior | Índice | Siguiente ]

La última versión de este texto se podrá encontrar en Internet, en la dirección:
www.nachocabanes.com