Universidad Autonoma Metropolitana - 148.206.53.84148.206.53.84/tesiuami/UAM3557.pdf · Rotaciones...

46
. Universidad Autonoma Metropolitana Proyecto en la lngenieria Electrónica I y II Graficación "3D" Asesor: M. en C. Miguel Angel Pizaña López Firma del asesor: INTEGRANTES: Arias Mendoza Carlos Cuevas Bonilla Raúl Vilchis Orozco Roberto NOVIEMBRE DE 1996

Transcript of Universidad Autonoma Metropolitana - 148.206.53.84148.206.53.84/tesiuami/UAM3557.pdf · Rotaciones...

.

Universidad Autonoma Metropolitana

Proyecto en la lngenieria Electrónica I y II

Graficación "3D"

Asesor: M. en C. Miguel Angel Pizaña López

Firma del asesor:

INTEGRANTES:

Arias Mendoza Carlos Cuevas Bonilla Raúl

Vilchis Orozco Roberto

NOVIEMBRE DE 1996

e

Proyecto en la Ingeniería I y II

"Graficación tridimensional"

Contenido

Objetivos y plan de trabajo

Espacios Vectoriales

Representación paramétrica de superficies

Rotaciones

Rotaciones en el espacio

Metodos de ordenación en profundidad

Representación en memoria de objetos tridimensionales

Reconocedor y generador de objetos tridimensionales

Metodo de "2-Buffer"

Administración de la memoria

La programación XMS

Utilización de la rutinas XMM.H

Bibliografía

Proyecto de investigación

Titulo: "Graficación 3D"

Objetivo General:

Crear una biblioteca de funciones de graficación 3D para PC, sujeta al estandard VESA ( SVGA) a 256 colores.

Marco Teórico:

Es bien sabido que la graficación por computadora y en particular la graficación tridimensional requiere de algoritmos complejos y gran cantidad de recursos del sistema (memoria y velocidad de cómputo); Es por ello que convergen en ésta disciplina técnica de programación de sistemas, estructuras de datos, compiladores, optimización, análisis y diseño de algoritmos, algebra lineal y geometría proyectiva.

Uno de los objetivos principales de este proyecto es ( a parte del producto en si ) complementar la formación de los educandos,por medio de la integración de sus conocimientos previamente adquiridos con un propósito común.

Plan de Trabajo

Primer trimestre:

Estudiar diversas estructuras de datos para representar objetos tridimensionales y en función de ello escoger la(s) que más convenga(n) a nuestros propósitos.

Analizar y comprender diversos algoritmos como por ejemplo: "ordenación en profundidad" y "z-buffer". Llevar a cabo su implementación.

La programación se realizará en lenguaje C.

Segundo Trimestre:

Estudiar técnicas de manejo de memoria extendida, para satisfacer las necesidades de memoria de los algoritmos mencionados.

lmplementar una función que genere objetos tridimensionales a partir de ecuaciones de superficies. (Se usarán técnicas de Compiladores)

Espacios Vectoriales

Transformaciones lineales:

Dados U E R3 I V E R3 una transformación lineal cumple las siguientes propiedades:

L ( U + V ) = L ( U ) + L ( V )

L ( c U ) = c L ( U )

por ejemplo L : R 3 "+ R

( a , b , c ) + a ( a , b , c ) + a + b + c ( a, b, c ) "+ 3a - 4b + 2c

L : R3 + R2

( a, b, c ) + 4a, 3b ( a , b , c ) + 5 a , 2 c

Las siguientes transfomaciones R3 -+ R2 reciben un punto en el espacio ( x,y,z ) y lo mandan (transforman) a un punto en el plano X',Y'.

(1 , O, O ) + a l , b l ( O , 1 , 0 ) + a 2 , b2 ( O, O, 1 ) -+ a3 , b3

Y' [a2.b2]

[a3.b3] IR2 IR3

Las transformaciones lineales pueden representarse como matrices, por lo que es posible representar las transformaciones anteriores de la siguiente manera:

L[il;[;:l

. [HI=[ ; : ]

. [ ! ]=[; :I Aplicando las propiedades de las transformaciones lineales se tiene:

L [ ; I = L [i] + L [ & ] + L [HI La matriz de transformación resulta:

1 0 0

base

Representación Paramétrica de Superficies

Coordenadas Esféricas

Una superficie tridimensional puede representarse en coordenadas rectangulares, cilíndricas y esfericas, en particular nos interesan las ecuaciones paramétricas en coordenadas esfericas, la representación espacial de un punto es como sigue:

x = r sen 8 sen # y = r c o w z= r sen 8 cos#

/ z = t c o s # t = r sen 8

Del esquema anterior se observa que un punto con coordenadas (x,y,z) puede describirse en función de dos ángulos ( 8 y 9) los cuales son los parámetos, y la distancia de dicho punto al origen de coordenadas. En el caso de las coordenadas esféricas, los ángulos O y cp toman valores de O - 2n y de O - n radianes respectivamente, con Io cual se cubre todo el espacio tridimensional.

Rotaciones

Una rotación es una transformación lineal, un caso particular es la rotación de un sistema de coordenadas. Considere un punto en el plano X-Y de coordenadas (x,y), expresado en coordenadas polares se tiene:

1 9 = arc tan - X

Y

Si rotamos los ejes coordenados q grados en el sentido contrario a las manesillas del reloj, obtenenmos un nuevo sistema de referencia X-Y'. Las nuevas coordenadas para el punto en cuestion resultan:

YV = arc tan[-) - 6 X

Y

Observando los puntos (Xl,Yl) = (1,O) y (X,,Y,) = ( 0,l) del sistema de coordenadas original, podemos obtener las nuevas coordenadas para este punto, resultando:

( Xl',Yl' ) = ( cos O, sen O )

( X,',Y,' ) = ( -sen e, cos 8 )

Ahora obtenemos que una rotación del plano X-Y 8 grados positivos se puede expresar como el producto de dos matrices:

f cos e- sene )( x ) = f x' ) sen e COSO A y ,) ( y' I

x '=xcos 8 - ysene y '= x sene + y cos e

Es decir para obtener las nuevas coordenadas de un punto que ha sido rotado O grados, se requieren las coordenadas del punto sin rotar y el ángulo de rotación.

Rotaciones en el espacio

Si rotamos los ejes X-Y y dejamos fijo el eje Z, tenemos:

Y

z / 1 X

Las nuevas coordenadas resultan:

(1,0,0) + ( cos 0, sene, O ) (0,1,0) -+ ( -sene, cos 0, O ) (0,0,1) + ( o, o, 1 1

Por lo tanto la matriz de rotación con el eje Z fijo, es la siguiente:

Dejando fijo el eje X :

(1 O o \ ( x Io cos 0 -sen0 II y I = \O sen0 cos 0 I\ z I

Dejando fijo el eje Y :

(cos 0 o -sene x Io 1 O I I Y I = \sen0 O cos 0 A z I

X '

Métodos de detección de superficie visible

Una consideración importante en la generación de despliegues de gráficas realistas es la identificación de aquellas partes de una escena que son visibles desde una posición de vista seleccionada

Los algoritmos de detección de superficie visible se clasifican en forma general dependiendo de si manejan definiciones de objetos de manera directa o con sus imágenes proyectadas. Estos dos planteamientos se denominan métodos de objeto-espacio y métodos de imagen-espacio, de modo respectivo. Un método de objeto-espacio compara objetos y partes de objetos con cada uno de los mismos en la definición de la escena a fin de determinar qué superficies, como un todo, debemos designar como visibles. En un algoritmo de imagen-espacio, la visibilidad se decide punto por punto en cada posición de pixel en el plano de proyección.

Método de clasificación de profundidad

AI utilizar tanto operaciones de imagen-espacio como de objeto espacio, el método de

1 ) Se clasifican las superficies en orden de profundidad decreciente. clasificación de profundidad realiza las funciones básicas siguientes:

2) Se convierten por rastreo las superficies en orden, al iniciar con la superficie de la mayor profundidad.

Las operaciones de clasificación se efectúan tanto en imagen como en objeto-espacio y la conversión de rastreo de las superficies de polígono se lleva a cabo en imagen-espacio.

Este método para solucionar el problema de las superficies ocultas a menudo se denomina algoritmo del pintor. Primero clasificamos las superficies de acuerdo con su distancia desde el plano de visión, de esta manera se capturan en el búfer de enfriamiento los valores para la superficie más alejada. AI manejar cada superficie a la vez ( en orden decreciente de profundidad ), "pintamos" las intensidades de superficie en el búfer de estructura sobre las intensidades de las superficies que se procesaron antes.

La pintura de superficies de polígono en el búfer de estructura de acuerdo con la profundidad se realiza en varios pasos. Si suponemos que vemos a Io largo de la dirección de las z, las superficies se ordenan en la primera trayectoria de acuerdo con el valor más alto de z en cada superficie. La superficie con la profundidad más alta se compara entonces con las otras superficies en la lista para determinar si hay alguna superposición la profundidad. si no se da ninguna superposición en la profundidad, la superficie S se convierte por rastreo. La figura 1 ilustra dos superficies que se superponen en el plano xy pero no tienen ningún traslape de profundidad.

Así se repite este proceso para la siguiente superficie en la lista. En tanto que no haya ninguna superposición, se procesa cada superficie en orden de profundidad hasta que se hayan convertido todas por rastreo. Si se detecta una superposición en la profundidad en cualquier punto de la lista, es necesario realizar algunas comparaciones adicionales para determinar si se debe reorganizar cualquiera de las superficies.

Practicamos las pruebas siguientes para cada superficie que se traslapa con S. si alguna de estas pruebas es verdadera, no es necesario llevar a cabo ninguna reorganización para esa superficie. Las pruebas se listan en orden incremental de dificultad.

1) Los rectángulos limítrofes en el plano de xy para las dos superficies no se sobreponen.

2) La superficie S está por completo detrás de la superficie que se sobrepone con respecto de la posición de vista.

3) La superficie que se sobrepone está por completo enfrente de S con respecto de la posición de vista.

4) Las proyecciones de las dos superficies en el plano de visión no se traslapan.

Realizamos estas pruebas en el orden en que se listan y procedemos a la siguiente superficie que se sobrepone en cuanto encontramos que una de las pruebas es verdadera. Si todas las superficies que se traslapan pasan por lo menos una de estas pruebas, ninguna de éstas se halla detrás de S. Así, no es necesario efectuar ninguna reorganización y se convierte por rastreo S.

La prueba 1 se lleva a cabo en dos partes. Primero verificamos la sobreposición en la dirección de x, luego verificamos la sobreposición en la dirección de y. Si alguna de estas direcciones no presenta sobreposición, los dos planos no pueden oscurecerse entre sí. En la figura 2 se presenta un ejemplo de dos superficies que se sobreponen en la dirección de z pero no en la dirección de x.

figura 2

z! Xmin Xmax X'min X'max

Podemos practicar las pruebas 2 y 3 con una prueba de polígono "interna y externa". Es decir, sustituimos las coordenadas para todos los vértices de S en la ecuación del plano para la superficie que se traslapa y verificamos el signo del resultado. Si se establecen las ecuaciones del plano de manera que el exterior de la superficie se dirija hacia la posición de vista, entonces S está atrás de S'si todos los vértices de S están "adentro" de S ' ( Figura 3).

figura 3

De modo similar S' está por completo enfrente de S si todos los vértices de S están por completo "afuera" de S. La figura 4 ilustra una superficie S' que se traslapa, la cual está por completo enfrente de S, pero la superficie S no está por completo "adentro" de S' (la prueba 2 no es verdadera).

Si las pruebas 1 y 3 no tiene éxito, intentamos con la prueba 4 al verificar las intersecciones entre las aristas limítrofes de las dos superficies utilizando las ecuaciones de la linea en el plano xy. Como se demuestra en la figura 5, dos superficies pueden intersectarse o no incluso si sus extensiones de coordenadas se traslapan en las direcciones de x,y y z.

figura 5

Si las cuatro pruebas fracasan con una superficie de sobreposición particular S', intercambiamos S y S' en la lista calificada. En la figura 6 se presenta un ejemplo de dos superficies que se deben reorganizar con este procedimiento.

figura 6

En este punto aún no sabemos con certeza que hemos encontrado la superficie más alejada del punto de visión. La figura 7 ilustra una situación en que primero intercambiaríamos S y S'.

figura 7

Representación en memoria de objetos tridimensionales

Estructura de datos para un objeto tridimensional:

struct punto { float x,y,z; 1;

struct plano { int numero-de-vertices; float z-min,z-max; float N-X,N-Y,N-Z; struct punto ** ptr-lista-puntos; struct plano *siguiente; 1;

struct objeto { struct punto *ptr-puntos; struct plano *ptr-planos; 1;

El usuario declara dentro de main0 o bien como variable global un puntero a una estructura de tipo objeto o bien una estructura de tipo objeto :

Caso (1) struct objeto *ptr-objeto; Caso (2) struct objeto mi-objeto;

Si el usuario opta por el caso (1) deberá solicitar memoria para la estructura, como se muestra a continuación:

ptr-objeto = farrcalloc(1, sizeof ( struct objeto ) );

o bien:

ptr-objeto = malloc( sizeof ( struct objeto ) );

Calculo para los puntos tridimensionales de un boieto

Dada la ecuación paramétrica de un objeto tridimensional, se plantea al número de puntos a graficar tomando en cuenta el número de intervalos de división para cada uno de los parámetros del objeto, por ejemplo, si U y V son los parámetros de cierto objeto, el usuario determina el número de puntos en los que se divide cada uno, con lo que obtenemos dos variables : parametro-U y parametro-v, la cantidad de puntos a gráficar se obtiene de:

numguntos-a graficar = (( parametro-U +I) * (parametro-V +I));

El siguiente paso es resevar la memoria necesaria para almacenar este número de puntos, el campo puntos de la estructura objeto almacena la dirección de esta lista dinámica de puntos, esto lo hacemos con la siguiente instrucción:

ptr-objeto->puntos = farcalloc(numguntos-a-graficar, sizeof(struct punto) );

En este punto se tiene lo siguiente en memoria:

~~~ ~ ~

memoria

ptr_ 54et5->punt5s + struct punto j I

struct punto 1

De acuerdo a la ecuación paramétrica de cada objeto, se calculan los valor tridimensionales (x,y,z) de cada punto de la superficie del objeto, y se almacenan en la lista de puntos.

Calculo de los planos que forman la superficie del objeto

Con los valores de los puntos (x,y,z) en memoria ahora es posible formar una lista para los planos que formarán el objeto. La estructura de datos declarada como struct plano contiene un campo llamado siguiente con el que se crea la lista de manera dinámica, el otro campo llamado *ptr-/istaJuntos es un puntero que apunta a una lista dinámica de punteros a estructuras de tipo punto, tal y como se muestra en el siguiente esquema:

struct plano m-' struct punto

A

Para crear un plano se solicita memoria dinámica para una estructura de tipo plano, y se comienza a crear la lista de planos, esta lista es apuntada por ptr-objeto->p/anos.

En el momento de crear un primer nodo de la lista, se debe solicitar también memoria para la lista de punteros a estructuras de tipo punto, este nuevo arreglo dinámico de punteros solicitara el número de localidades necesarias según los planos se formen con tres o cuatro puntos, como se observa en la siguiente instrucción:

struct punto *puntos;

puntos = malloc ( (num-de-vertices) * (sizeof (* struct punto) ) );

donde num-de-vertices es uno de los campos de la estructura 'struct plano'. El campo *ptr-lista-puntos apunta a esta lista dinámica de punteros a puntos.

AI momento de formar un plano de 3 o 4 puntos, se calculan los valores de profundidad mínima y profundidad máxima los cuales se guardan en los campos z-min y z-max respectivamente. Recordando que cada vertice del plano tiene componentes x,y,z, es posible comparar cada uno de los valores para z y obtenr el máximo y el mínimo.

z-

Obtencidn de las coordenadas z-min y z-max para un plano partucular.

Tambien es posible calcular en este momento las componentes del vector normal a dicho plano , que ocupan los campos N-X, N-Y y N-Z, estos valores se obtienen de calcular el "produfo cruz" entre dos vectores que pertenecen al plano. A su vez un vector que pertenece al plano se obtienen de restar dos puntos que pasan por el plano, estos puntos ya fueron calculados en la etapa previa a la formación de este plano.

Y I a2

I p4- Pl

En el caso en que un plano este formado por cuatro puntos, se tiene que cada punto tiene corrdenadas x,y,z de tal forma que pi = (x,y,z), obtenemos un vector sobre el plano mediante la resta de dos puntos del plano, por ejemplo:

El producto punto entre dos vectores a y b se obtiene mediante:

a = (al ,a2,a3) y b = (bl,b2,b3)

El producto anterior nos da el vector normal al plano.

"Reconocedor y Generador"

Uno de los objetivos de nuestro proyecto era el de realizar un "Reconocedor y Generador" el cual se encargara de aceptar expresiones de la forma:

"varl[ini,fin,inter],var2[ini,fin,inter],x,y,z"

donde:

varl ,va2:Son los dos parametros de los que dependeran las ecuaciones parametricas. ini,fin :Es donde comienza y donde termina el intervalo de cada parametro. inter :Es el No. de divisiones del intervalo. x,y,z :Son las ecuaciones parametricas del Objeto a graficar.

Una vez reconocida y aceptada la expresión se procede a generar la Lista de Puntos del Objeto.

RECONOCEDOR DE EXPRESIONES

Para reconocer las expresiones se utilizan 3 archivos que el usuario debera utilizar con '%include" y son : gen3d.h lex3d. h parse3d. h

GEN3D.H El archivo "gen3d.h" incluye (por medio de '%include" ) a "parse3d.h".Primeramente la función principal es "genera" y es mandada llamar por el usuario de la siguiente forma:

mi-objeto=genera("expresión");

La variable "mi-objeto" será un apuntador a una estructura tipo objeto, por lo tanto regresara el apuntador a el objeto ya generado. Esta función "genera" realizá propiamente el reconocimiento de toda la expresión y como se observa recibe una cadena que guarda en una variable llamada "exp" la cual a su vez es igualada a la variable "yyin", que es la que utiliza nuestro analizador lexico. A la expresión , para nuestro análisis la dividimos en dos partes, la primera es la que contiene a cada una de los parametros con su respectiva información sobre sus intervalos y la segunda es la que contiene las ecuaciones parametricas. La información de la primera parte es guardada en un arreglo de dos llamado "U" del tipo:

struct GEN-VAR{ float ¡ni; float fin; int n; /*No. de intervalos*/ float val; /*Valor que irá tomado el parametro*/

char nomb[lO]; /*Nombre del parametro*/ 1;

struct GEN-VAR U[2];

Para el análisis de esta primera parte de la expresión se va recorriendo cada uno de los caracteres de la cadena y pasandose cada uno a la vez al analizador lexico. El análisis de la segunda parte se hace dentro de dos ciclos (que van desde "ini" hasta "fin" con un salto de "inter") que van recorriendo los intervalos de los 2 parametros y dandole valor a cada uno.Obteniendo el valor de cada uno de los parametros de esta forma , se manda llamar al analizador semántico para cada una de las ecuaciones parametricas y por supuesto regresara el valor para "x","y" o "z" que son guardados en una estructura de tipo "punto".Así al recorrer los 2 ciclos habremos obtenido la lista de puntos del objeto.

En seguida se mandan a llamar las funciones que ya se han comentado anteriormente:

translada(OBJET0) /*Translada la lista de puntos al nuevo sistema de

Planos(OBJET0) /*Genera los planos del objeto a graficar. Pre-Ordenacion(OBJET0) /*Ordena los planos según la ' Z ' mas negativa.

Coordenadas segun el punto de vision.

Ordenacion-en-Profundidad(OBJET0) /*Ordena ahora los planos segun el Algoritmo de Ordenaciopn por Profundidad.

LEX3D.H Este archivo contiene el Analizador Lexico.Este analizador es llamado tanto por la función "genera" como por el Analizador Semántico, y regresa cada uno de los tokens respectivos:

1) Caractares alfanumericos. Para este caso tenemos cuatro cadenas en particular que regresan tokens especiales: "sin" regresa el token SIN. "cos" regresa el token COS. "tan" regresa el token TAN. "Pi" regresa el token PI. De otro forma solo se regresa el token ID. 2) Digitos (Reconoce de Tipo 'float') y regresa el token NUM. 3) Cualquier otro diferente a los anteriores regresa propiamente el caracter leido.

PARSE3D.H Este archivo tiene un '%include" para "lex3d.h".Fue generado por medio de la herramienta BISON para obtener Analizadores Semánticos :

%{ Mefine alloca malloc #define WSTYPE double

%}

%token WNUM WID SEN COS TAN PI

%left '*' 'P %right umenos %right ' N

yoleft '+' I-'

%%

L3 : L2 ',I E {return($3);}

L2 : L1 ',I E {return($3);}

L1 : E {return($l);}

E : E '+' E {$$=$I +$3;} I E I-' E {$$=$1-$3;} I E '*I E {$$=$1*$3;} I E 'P E {$$=$1/$3;} I '2 E %prec umenos {$$ = 42;) I '(I E I)' {$$=$2;} I E I h l E {$$=pow($l,$3);} I WNUM {$$=yylval;} I WID {$$=yylval;} I SEN '(I E I)' {$$=sin($3);}

I COS'(' E')'{$$=cos($3);} I TAN I(' E I)' {$$=tan($3);}

I error {return(O);} I PI {$$=Pi;}

%% #include "lex3d.h" yyerror(){printf("ERROR!!!!!! ...'I) ;}

GRAFICACION DEL OBJETO

Para la graficación del Objeto se debe incluir al archivo "graf3d.h" , que tiene las siguientes funciones comentadas ya anteriormente:

Inicializa-Graficos().Pide el tipo de modo grafico a inicializar

Libera-Objeto(struct objeto *objeto).Recibe un apuntador a la estructura del objeto.

Dibuja(struct objeto *objeto).También recibe ese apuntador al Objeto y va graficando cada plano ya ordenados propiamente.

INTERFASE CON EL USUARIO

El usuario creará su archivo en C el cual deberá contener primeramente dos '%include" que son "gen3d.h" y "graf3d.h".Despues debera inicializar un apuntador a una estructura tipo objeto.Enseguida debera inicializar el modo grafico mediante la llamada a la función Inicializa-Graficos".El apuntador al objeto declarado previamente será regresado por la llamada a la función "genera",ya teniendo este apuntador se pasa este para ser graficado mediante la funcion "Dibuja".Cuando el usuario decida debera liberar la memoria requerida por toda la estructura del Objeto,mediante la llamada a "Libera-Objeto".

Un ejemplo para graficar un cilindro sería:

#include "gen3d.h" #include "graf3d.h"

struct objeto *mi-objeto;

main(){ clrscr(); Inicializa-Graficos(); mi~objeto=genera~'u[0,2*Pi,20],v[0,2*Pi,20],5O*cos(v),20*u,50*sin(v)"); Dibuja(mi-objeto); getch0; Libera-Objeto(mi-objeto); closegraph();

I

BUFFER Z

Introducción:

Todas los monitores presentan las imágenes mediante la composición de pequeños puntos de color que se encienden y apagan en la pantalla; a estos puntos se les conocen como pixeles y varían en numero dependiendo de la resolución del monitor, a mayor numero de pixeles en la pantalla de un monitor, mayor será la resolución de imágenes de este. Como ejemplo de resolución en monitores se tienen:

320x 200 640x 200 640x 480 800x 600 1024x 764 1024x1024

Estas resoluciones indican el numero de pixeles en línea horizontal contra el numero de pixeles en línea vertical con lo cual se puede definir un sistema de coordenadas {X,Y}. Suponiendo que el espacio tridimensional esta formado por el sistema de coordenadas {X,Y,Z), definiendo la profundidad como Z y haciendo coincidir X & Y del espacio tridimensional con el X & Y de las coordenadas de la pantalla, entonces, la visualización de objetos tridimensionales en una pantalla de video será por la proyección de los mismos en el plano (X ,Y} al cual denominaremos como "Plano de Visión".

El método del Buffer Z es un planteamiento de graficación de imagen en espacio que utiliza un método común para detectar superficies visibles comparando las profundidades de cada superficie en cada una de las posiciones de pixel en la pantalla o plano de visión. La profundidad mínima o mas próxima de todos los planos que se proyectan sobre el la posición de pixel analizado es almacenado en un arreglo rectangular del igual numero de pixeles de la superficie de visión; es este arreglo llamado el buffer de profundidad o buffer z del cual deriva el nombre del método. Es necesario entonces procesar cada una de las superficies que forman los objetos en una escena.

Procedimiento:

Las escenas en la pantalla están formadas por los objetos que se grafican, los objetos están compuestos por superficies planas acotadas por los bordes del objeto. El procedimiento aplicado en la graficación por Buffer Z consiste en realizar un barrido sobre todos los puntos de pixel en la pantalla realizando las operaciones necesarias para determinar si el pixel barrido esta contenido dentro de la proyección ortogonal de la superficie en le plano de visión y en caso de estarlo determinar la profundidad de la superficie en ese punto de pixel y compararla con valores almacenados de antemano en el buffer (es necesario entonces que el buffer sea inicializado a el valor mas profundo de la escena) y reemplazar el valor del buffer con el valor determinado de la profundidad del plano si este equivale a un punto mas próximo al plano de visión. Es requerido para este proceso contar también con un buffer de color de igual tamaño al buffer de profundidad para almacenar el valor del color de cada punto de pixel de la pantalla (este buffer también deberá ser inicializado con el color del fondo de la pantalla). Al finalizar el proceso, el buffer Z contendrá los puntos de profundidad visible para cada punto de pixel en el plano de visión mientras que el buffer de color contendrá el valor del color de cada punto en el plano de visión.

El proceso de implementación de método Buffer Z es el siguiente:

a) Generar los objetos mediante superficies (planos) con vértices coordenados en el espacio tridimensional {X,Y,Z}. Cada objeto deberá contener la información de numero de vértices que componen cada una de sus superficies, las coordenadas de cada vértice, las coordenadas del vector normal de la superficie, color primario del plano y color de la frontera de la superficie.

b) Inicializar el buffer de profundidad al máximo valor de profundidad posible e inicializar el buffer de color al color del fondo.

c) Suponiendo que la línea de rastreo barre los pixeles del plano de visión de izquierda a derecha y de arriba hacia abajo. Se puede ver esta línea de rastreo como una recta horizontal sobre el plano {X,Y>, es necesario para determinar (si existen) los puntos de intersección de la línea de barrido con las líneas de los bordes de las proyecciones de las superficies para comprobar si cierto punto de pixel sobre la línea de rastreo esta contenida en la proyección de la superficie. Una manera sencilla de determinar los puntos de intersección de la línea de rastreo con los bordes de las proyecciones de las superficies es mediante el uso de triángulos equivalentes; Si cada borde de la proyección es una recta que no es horizontal ni vertical y que se intersecta con la recta de la línea de rastreo, es posible formar dos triángulos rectángulos equivalentes tomando como base de un triángulo la recta de la diferencia en X de los extremos del borde y la recta del la línea de rastreo.

punto de j'. .. intersección j '.., . .

a = Ab/B

'"..____ borde de la j . ' -. ilinea de rastreo proyeccion j

< A

d) En caso de que un punto de pixel sobre la línea de rastreo se encuentre dentro de la proyección de una superficie se procede a calcular el valor de la profundidad de la superficie en ese punto utilizando para ello la ecuación del plano:

A x + B y + C z + D = O

z = (-AX - By - D)/C

Supóngase que las coordenadas del vector normal de la superficie tratada son (nx,ny,nz) y que un punto de la superficie en el espació tridimensional es (xl ,yl ,zl) se tiene que:

nx(x - xl) + ny(y - y l ) + nz(z - zl) = O

nx(x) + ny(y) + nz(z) + (-nx(x1)-ny(y1)-nz(z1)) = O

A = n x B = ny C = n Z

D = (-nx(x1)-ny(y1)-nz(z1))

Donde x & y serán las coordenadas del punto de pixel en la proyección de la superficie.

e) Del valor de la profundidad del plano obtenido en el punto de pixel tratado se compara con el valor guardado de antemano en el buffer z. Si el nuevo valor es mas próximo al plano de visión, el valor almacenado en el buffer z será reemplazado por este nuevo valor y en el buffer de color será almacenado el color del punto de pixel que deberá tener para ese punto de profundidad.

f) repetir el procedimiento desde el inciso (c) para cada una de las superficies que componen la escena a graficar. Al finalizar el proceso con todas las superficies se tiene almacenado en el buffer z todos los puntos de visión de una escena y el buffer de color contendrá los colores de cada uno de los puntos de pixel de la escena.

g) Buffer z requiere ser del mismo tamaño rectangular que el numero de puntos de pixel en la pantalla lo que significa tener la capacidad de memoria para las operaciones, cantidad que aumentara al aumentar la resolución de las escenas que se grafiquen, por ejemplo: Para una resolución de 640x200 pixeles y suponiendo que se utilicen valores enteros de profundidad en el buffer z se requieren 64Ox200x2=256 kbytes de memoria para realizar las operaciones mas 128 kbytes para el buffer de color suponiendo que tan solo se manejen 256 colores lo que suma un total de 384 kbytes de memoria necesaria para graficar la escena. Esto conlleva a limitaciones de memoria base disponible por lo que se requiere hacer uso de memoria extendida (si se dispone de ella) o bien dividir la escena en ventanas de menor tamaño de tal manera que se pueda ajustar los buffers y realizar la graficación ventana por ventana.

Librería Gráfica Buffer Z:

Como parte del proyecto, se genero un archivo de librería para graficación por Buffer z denominado "GRAF-3D.H" el cual contiene las funciones para la graficación de objetos tridimensionales generados de antemano por otras librerías.

Funciones para Usuario:

void GraDD (Objeto3D *ob) < graf-3d. h >

Objetivo: Gráfica un objeto 3D con el método de Buffer Z, debido a los requerimientos de memoria del buffer z esta función divide la escena en ventanas de 160x120 puntos de pixel para 256 colores.

Parámetros: (Objeto3D *ob) apuntador a la estructura de objetos que contiene el apuntador a la lista de planos del objeto, el apuntador a la tabla de puntos del objeto y el numero de puntos en la tabla.

retorna: ningún valor.

Funciones Internas de la Librería:

void Algoritmo buffer-z (Plano3D *ob,Punto2D r) void IRango_xy<Punto2D *mn,Punto2D *mx,Punto2D *ax,Punto2D r,Plano3D *pg) void Inicializa buffers (void) void rIntersecc& (int t,Plano3D *pg,Pint **pin,Punto2D r) int PuntosInt (int t,Punto2D pl,Punto2D p2,char *bn) Punto3D -Punto-min (Plano3D *g) Punto3D -Punto-max (Plano3D *g)

Codiqo Principal del archivo GRAF3D.H:

.............................................................. /

I********** UNIVERSIDAD AUTONOMA METROPOLITANA************* 1 /********** Unidad - lztapalapa ************* / .............................................................. 1

/*Proyecto terminal "GRAFICACION 3D" (Trim: 96-1 & 96-P)*/ /*Turbo C*/ /*LIBRERIA GRAF-3D.H*/

#include <math.h> #include <ctype.h> #include <stdlib.h> #define pi 3.141 592 #define K (1 75124.5) ##define XP ((r.x)*160) #define YP ((r.y)*120)

typedef struct { int x,y;

} Punto2D; typedef Punto2D Delta;

/*Estructura de dos dimenciones*l

/*Estructura para puntos de intersection*/ struct intr { int p; char sec; struct intr *sig;

typedef intr Pint; void Graf3D (Objeto3D *ob); void -Algoritmo-buffer-z (Plano3D *ob,Punto2D r);

1;

float L; int BUFFER-Z[160][120]; char BUFFER-COLOR[160][120];

void Graf3D (Objeto3D *ob) {

int t l ,t2,xx,yy; Punto2D tr; xx=getmaxx ()+1 ;yy=getmaxy ()+1;

xx=xx/160;yy=yy/l20; for (t2=0;t2<xx;t2++) {

L=xx/yy;

for ( t l =O;tl <yy;tl++) { tr.x=tl ; tr.y=t2; Algoritmo-buffer-z (ob->Objeto,tr);

1 -

1

/*PROCEDIMIENTO BUFFER Z*/ void -Algoritmo-buffer-z (Plano3D *ob,Punto2D r) {

Plano3D *pg; Punto3D ptl; Punto2D mn,mx,cb; Pint *Inter,*den; Delta ax; float A,B,C,D,sx,sy; int Z,tl ,t2,xx,yy; char bnl ,bn2,cl; xx=getmaxx ();yy=getrnaxy (); setviewport (XP,YP,XP+159,YP+lI9,1); /*venmtana de buffer*/

pg=ob; if (ob==NULL) return; for (;pg!=NULL;) { /*planos del objeto*/

- Inicializa-buffers (); /*inicializa buffers*/

- Rango-xy (&mn,&mx,&ax,r,pg); /*determination del rango*/ if ((mn.x<=mx.x)&&(mn.y<=mx.y)) { /*comprueba plano en ventana*/

bnl =O; for (t2=(rnn.y);t2<=(mx.y);t2++) { /*scanline vertical*/

Inter=NULL; - lnterseccion (t2,pg,&lnter,r); /*intersecciones plano-scanline*/ for ( t l =(mn.x);tl <=(mx.x);tl++) { /*scanline horizontal*/ den=lnter; bn2=0;

for (;den!=NULL;) { /*scanline dentro del plano*/ cl=pg->cp;

if (t l >den->p) bn2=den->sec; if ( t l ==den->p) { bn2= 1 ;

1 cl=pg->cb;

if ((t2==ax.x)ll(t2==ax.y)) cl=pg->cb; den=den->sig;

1 if (bn2!=0) { if (pg->Norrnal.z!=O) { S>i=(tl-320+XP)/(K*L); sy=240-t2-YP; if (bnl==O) { /*determina ecuacion del plano*/

A=pg->Normal.x; B=pg->Normal.y; C=pg->Norrnal.z; D=(-A*(pg->pt[O]->~))+(-B*(pg->pt[O]->y))

+(-C*(pg->pt[O]->z)); bn l= l ;

1 Z=(int)((-(A*sx)-(B*sy)-D)/C); /*determina punto en el plano*/ /*else Z=Z-(A/C);*/ if (BUFFER_Z[tl][t2]<=Z) { /*compara valores en buffer z*/

BUFFER-Z [tl][Q]=Z; BUFFER-COLOR [tl][t2]=cl;

1 1

1 1

den=lnter; for (;den!=NULL;) { /*libera puntos de intersection*/

Inter=den-xig; free(den); den=lnter;

1

1 pg=pg->liga; /*siguiente plano*/

1

1 for (t2=0;t2<120;t2++) { /*dibuja el buffer en pantalla*/ for (tl=0;t1~160;tl++) {

putpixel (tl ,t2,BUFFER_COLOR[tl][t2]); 1

1 setviewport (O,O,xx,yy,l);

1

/*PROCEDIMIENTO DETERMINAZION DEL RANGO DE LA PROYECCION*/ void -Rango-xy (Punto2D *mn,Punto2D *mx,Punto2D *ax,Punto2D r,Plano3D *pg) {

Punto3D rmax,rmin; rmin=-Punto-min (pg); rmax=-Punto-max (pg); mn->x=(int)((K*L*rmin.x+318)-XP); mx->x=(int)((K*L*rmax.x+322)-XP); mn->y=(int)((240-rmax.y)-YP); mx->y=(int)((240-rmin.y)-YP); ax->x=mn->y;ax->y=mx->y; if ((mn->x)<O) mn->x=O; if ((mx->x)>l59) mx->x=l59; if ((mn->y)<O) mn->y=O; if (( mx->y)> 1 1 9) mx->y= 1 1 9;

1

/*PROCEDIMIENTO PARA INICIALIZAR LOS BUFFERS*/ void -Inicializa-buffers (void) {

int i,j,k; for (i=O;i<l2O;i++) { for (j=O;j<l6O;j++) {

BUFFER-Z [j][i]=-32768; BUFFER-COLOR [j][i]=getpixel U,¡);

1 1

1 /*PROCEDIMIENTO PARA DETERMINAR COORDENADAS MINIMAS DE UN PLANO*/ Punto3D -Punto-min (Plano3D *g)

Punto3D m; if (g==NULL) return (m); m.x=g->pt[O]->x; m.y=g->pt[O]->y; m.z=g->pt[O]->z; for (i=l;i<(g->Num);i++) {

if (m.x>(g->pt[i]->x)) m.x=g->pt[i]->x; if (m.y>(g-zpt[i]->y)) m.y=g->pt[i]->y; if (m.z>(g-zpt[i]->z)) m.z=g->pt[i]->z;

1 return (m);

1 /*PROCEDIMIENTO PARA DETERMINAR COORDENADAS MAXIMAS DE UN PLANO*/ Punto3D -Punto-max (Plano3D *g) { int i; Punto3D m; if (g==NULL) return (m); m.x=g->pt[O]->x; m.y=g->pt[O]->y; m.z=g->pt[O]->z; for (i=l;i<(g->Num);i++) {

if (m.x<(g->pt[i]->x)) m.x=g->pt[i]->x; if (m.y<(g->pt[i]->y)) m.y=g->pt[i]->y; if (m.z<(g->pt[i]-zz)) m.z=g->pt[i]->z;

I return (m);

I /*PROCEDIMIENTO PARA DEREMINAR PUNTOS DE INTERSECCION PLAN-SCANLINE*/ void -1nterseccion (int t,Plano3D *pg,Pint **pin,Punto2D r) {

Pint *ipl ,*ip2; Punto2D pt l ,pt2; char bn; int i,j,lnter; for (i=O;i<pg->Num;i++) {

pt l .x=(int)((K*L*(pg->pt[i]->x)+320)-XP); pt l .y=(int)((240-(pg->pt[i]->y))-YP); pt2,x=(int)((K*L*(pg-~pt[(i+l)%(pg-~Num)]-~x)+320)-XP); pt2.y=(int)((240-(pg->pt[(i+l)%(pg->Num)]->y))-YP); Inter=-Puntoslnt (t,ptl ,pt2,&bn); if (bn==O) { ip l =(Pint*)malloc(sizeof(Pint)); if (ipl ==NULL) exit (O); ipl->p=lnter; ip l ->sig=*pin; *pin=ipl ;

1 I ip l =*pin; i= l ; for (;ipl !=NULL;) {

ip2=ipl; for (;ip2!=NULL;) {

Inter=ipl ->p;

ip2->p=lnter;

if ((ipl ->p)>(ip2->p)) {

ip l ->p=ip2->p;

1 ip2=ip2->sig;

1

if (ipl ==*pin) j=ipl ->p; if (j==ipl->p) ¡=I; ipl ->sec=i; i=(++i)%2; if (ipl->sig==NULL) ipl->sec=O; ipl =ipl ->sig;

/*PROCEDIMIENTO PARA CALCULO DE INTERSECCION DE RECTAS*/ int -Puntoslnt (int t,Punto2D p l ,Punto2D p2,char *bn) { float Deter,A,B,a,b; int In; if (((t<=pl .y)&&(t>=p2.y))ll((t<=p2.y)&&(t>=pl .y))) { A=(float)((p2.x)-(pl .x)); B=(float)((pl .Y)-(P~.Y)); b=(float)(t-(p2.y)); if (B!=O) { a=(b*AIB); In=(int)((p2.x)-a); *bn=O;

1 else { *bn=l ; In=O;

1 1 else { *bn=l ; In=O;

1 return (In);

1

Administración de la memoria

En la actualidad una computadora PC moderna esta equipada con al menos 4 MBytes de memoria principal, habitualmente con un tiempo de acceso de 70 ns o mejor. Bajo DOS sólo se pueden emplear 640 KBytes de esta memoria en forma directa. Estos 640 KB pueden no ser suficientes para algún programa en particular, por lo que resulta de especial interés el poder emplear el resto de la memoria.

Memoria Extendida ( XMS )

El estándar XMS fue desarrollado por las compañías Microsoft, Lotus e Intel. En su creación también participo la empresa AST Research. Las funciones del XMS se ponen a disposición por un controlador XMS, que se coloca en el vector de interrupción 2Fh.

Las funciones del controlador no se direccionan mediante una interrupción, sino como Far Call. Para obtener la dirección del controlador ha de comprobar primero si hay instalado un controlador XMS. Esto lo puede hacer llamando a la interrupción 2Fh con el valor 4300h en el registro AX. Si se devuelve el valor 80h en el registro AL, el controlador está instalado. En este caso la interrupción se ha de llamar de nuevo, con el valor 4310h en el registro AX. Se obtiene la dirección del segmento del controlador en el registro ES y la dirección del offset del XMM ( extended Memory Manager ) en el registro BX.

Cuando haya obtenido la dirección del XMM, puede llamar a sus funciones mediante un Far Call. Si la función se pudo procesar con éxito, habitualmente devuelve el código de estado O001 h en A X . En caso contrario el registro BL contendrá un código de error. Los códigos de error tiene el siguiente significado:

80 81 82 8E 8F 90 91 92 93 94 A0 A l A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD BO 61 62

Función Desconocida VDISK-Ramdisk encontrado Error en la linea de direcciones A20 Error general de controlador Error irrecuperable HMA no presente HMA ya asignado Tamaño especificado de DX demasiado pequeño HMA no alojada Linea de direcciones A20 aún activa No queda XMS disponible Todos los handles XMS ocupados Handle no válido Handle fuente no válido Offset fuente no válido Handle destino no válido Offset destino no válido Longitud incorrecta para función MOVE Solapado incorrecto en función MOVE Parity-Error UMB no bloqueado UMB aún bloqueado Rebase del controlador de bloqueo UMB UMB no se puede bloquear UMB disponible es más pequeño No hay UMB disponible Dirección del segmento UMB incorrecta

La programación XMS

El número de función de la funciones del XMM se pasa siempre en el registro AH. A continuación se listan algunas de las funciones básicas que ofrece el XMM.

FunciónOOh Obtener número de versión

Entrada AH = OOh Salida AX = número de versión XMS

BX = número de revisión interno DX = Estado de la HMA

Con esta función puede averiguar el número de versión del controlador. si en el estado HMA se encuentra el valor 1, la HMA está disponible, de lo contrario no se permite el acceso a la misma.

Función03h Activar A20 globalmente

Entrada AH = 03h Salida BL = código de error

Si quiere emplear la HMA en el modo real de forma fiable, ha de llamar esta función antes o después de la petición de la HMA. Recuerde cerrar la linea de direcciones antes de finalizar su programa, para evitar rebases de segmento en los programas subsiguientes.

Función04h Cerrar A20 globalmente

Entrada AH = 04h Salida BL = código de error

Función05h Activar A20 localmente

Entrada AH = 05h Salida BL = código de error

Mediante esta función de activa la línea de direcciones A20. Es decir que sólo se activa si la función no había sido llamada con anterioridad. Esta comprobación se realiza mediante un contador de llamadas, que es incrementado por la función 05h y decrementado por la función 06h.

FunciÓnOGh Desactivar A20 localmente

Entrada AH = 06h Salida BL = cbdigo de error

Función07h Obtener estado de A20

Entrada AH = 07h Salida AX = estado de la linea de direcciones

BL = código de error

Si A20 está libre, obtiene el valor O001 h en A X , de 10 contrario será 0000h.

Función08h Obtener XMS libre Entrada AH = 08h Salida AX = longitud del mayor bloque libre

DX = tamaño total del XMS en KBytes BL = código de error

Con esta función puede averiguar la memoria XMS libre. Pero atención, los valores devueltos siempre miden 64 KBytes de más, ya que también se cuenta la HMA, que también mide 64 KBytes.

FunciÓnOSh Alojar bloque en la memoria extendida

Entrada AH = 09h DX = Tamaño del bloque en KBytes

Salida DX = Handle para el acceso al bloque BL = código de error

Esta función reserva un bloque de memoria extendida ( EMB ). El alojamiento sólo funciona si aún queda libre un bloque del tamaño suficiente en la XMS. El bloque ocupado se puede direccionar a través del Handle devuelto.

FunciónOAh Liberar bloque de memoria extendida

Entrada AH = OAh

Salida BL = código de error DX = Handle para el acceso al bloque

Mediante esta función vuelve a liberar un EMB alojado. Después de la llamada el Handle queda como no válido, y los datos están permanentemente borrados.

FunciónOBh Copiar memoria

Entrada AH = OBh DS = Segmento de la estructura de copia SI = Offset de la estructura de copia

Salida BL = código de error

Con esta función puede copiar memoria a/de la XMS. Ya que los registros no son suficientes para guardar todos los datos, la función emplea una estructura de copia. La estructura tiene la siguiente forma:

Offset Función Tipo de datos

OOh Longitud del bloque a copiar 1 Dword 04 h Handle del bloque fuente 1 Word 06 h Offset en el bloque fuente 1 Dword OAh Handle del bloque destino 1 Word OCh Offset en el bloque destino 1 Dword

Cuando se presenta el solapado de las zonas, la zona fuente ha de estar antes de la zona destino, sino no se garantiza un funcionamiento perfecto. Para permitir una copia rápida, debería comenzar sus zonas de memoria en una dirección divisible entre cuatro.

FunciónOCh Bloquear EMB contra desplazamiento

Entrada AH = OCh

Salida DX:BX = Dirección lineal de 32 bits DX = Handle

BL = código de error

Para que no se creen agujeros en la XMS, el XMM, si es necesario, desplaza algunos bloques de memoria. Para asegurar un bloque contra el desplazamiento, Io que puede ser interesante si se quiere acceder directamente a la memoria, se emplea esta función. Como resultado se obtiene en DX:BX la dirección lineal bajo la cual puede direccionar la memoria.

FunciónODh Desbloquear EMB asegurado

Entrada AH = ODh

Salida BL = código de error DX = Handle

FunciónOEh Obtener información sobre el EMB

Entrada AH = OEh

Salida DX = Longitud del bloque DX = Handle

Mediante estas funciones se pueden obtener tanto informaciones sobre el bloque de memoria, como determinar el número de handles XMS libres. Si la función ha trabajado sin errores, en AX se puede encontrar el valor 0001h. En este caso se encuentra en BH el contador de bloqueo del EMB. El contador se incrementa con cada llamada a la función OCh y se decrementa con cada llamada a la función ODh. Si el valor de BH es mayor de O, ha de llamar a la función ODh BH veces, para liberar el bloque. En BL se encuentra la cantidad de handles XMS libres. Tenga en cuenta que es mejor ocupar bloques de memoria lo más grande posibles, ya que el número de handles es limitado.

FunciónOFh Modificar tamaño del EMB

Entrada AH = OFh BX = Nuevo tamaño del EMB DX = Handle

Salida BL = código de error

Utilización de las rutinas XMM.H

El archivo XMM.H contiene la declaración de una clase llamada vfp (very far pointer) por medio del cual se tendrá acceso a la memoria extendida, esta clase representa un tipo de dato puntero a memoria extendida. Declarando objetos de esta clase el usuario puede almacenar sus datos en la XMS. Las funciones miembro de esta clase son algunas de las mencionadas anteriormente, como por ejemplo, verificar la existencia de un controlador XMM, reservar memoria extendida, liberar memoria extendida, copiar de memoria convencional a extendida, de XMS a memoria convencional, etc.

Estas rutinas se podrán compilar y generar código ejecutable utilizando un compilador C++ que contenga el uso de 'templates', por ejemplo "Borland C++ 3.0" o superior. A continuación se muestra la manera de declarar un objeto vfp, asignar valores, leer valores, etc.

Declaración de objetos vfe

Declarar un objeto de la clase vfp (very far pointer) significa declara un tipo de dato el cual se almacenará en memoria extendida y sera apuntado por cierta variable. Es indispensable que la siguiente linea aparezca en su archivo principal:

#include "XMM.H"

Declaración de un solo elemento

Declarar un solo elemento vfp se logra con la siguiente sintaxis:

vfp<Tipo-de-dato> nombre-del-puntero;

donde Tipo-de-dato se refiere a un tipo de dato predefinido o bien a un tipo de dato definido por el usuario, y nombre-del-puntero es la variable con la cual se puede hacer referencia al dato que se guarda en memoria extendida.

Por ejemplo el segmento de programa siguiente declara un entero apuntado por puntero-a-entero, y una estructura de tipo punto apuntada por puntero-a-mi-estructura, ambos datos el entero y la estructura se guardaran en memoria extendida:

#include "XMM.H" typedef struct {

int x; int y; int z; } punto;

void main()

vfp<int> puntero-a-entero; vfp<punto> puntero-a-mi-estructura;

I

{

...

La manera de referirnos a este elemento es por medio del nombre del puntero vfp, por ejemplo:

puntero-a-entero = 1 O;

Asigna el número 10 al elemento de tipo entero apuntado por puntero-a-entero. Otra manera de guardar este dato es con la siguiente instrucción:

puntero-a-entero[O] = 1 O;

Dado que puntero-a-entero apunta a un solo elemento de tipo int, es como si se tratara de un arreglo lineal de una casilla, por lo cual sus indices permitidos van de [O - (numero-de-casillas - l)], en este caso el índice [O] es válido ya que es el índice inicial y también final del arreglo de 1 casilla. Si se hiciera referencia al índice [I], esto provocaría un error.

Declaración de arreglo lineales

Para indicar al compilador que deseamos un arreglo lineal de elemento de tipo Tipo-de-dato, utilizamos paréntesis seguido del nombre-del-puntero con la siguiente sintaxis:

vfp<Tipo-de-dato> nombre-del-puntero(cuantos)

En este caso cuantos indica al compilador la cantidad de elementos de tipo Tipo-de-dato que se guardarán en XMS y serán referenciados por nombre-de-puntero. Por ejemplo

#include "XMM.H" typedef struct {

char nombre[30]; char dirección[50]; } registro;

void main()

vfp<float> arreglo( 1000); // Arreglo de 1 O00 float's vfp<registro> datos(300); // Arreglo de 300 registros

{

1 ...

Hacemos una referencia a un elemento en particular del arreglo por medio de los indices encerrados entre corchetes [I, por ejemplo:

arreglo[ 1001 = 12345.6789;

Guarda en la localidad 100 el número 12345.6789. Recordemos que en este caso los indices válidos van de [O-9991, por lo que la siguiente instrucción causaría un error:

arreglo[1000] = 12345.6789; // Error no hay casilla 1 O00

Declaración de arreglos bidimensionales ( matrices 1 Otra colección de datos interesante es el arreglo bidimensional, al igual que el arreglo lineal se declara

con paréntesis, como sigue:

vfp<Tipo-de-dato> nombre-de_puntero(num-de_columnas,num_de-renglones);

donde num-de-columnas es en numero de columnas de la matriz, y num-de-renglones es el numero de renglones.

Por ejemplo:

#include "XMM.H"

void main() { vfp<int> VIDEO_VGA(640,480); // Matriz de enteros de 640 x 480 vfp<float> b( 1000,2000); // Matriz de float's de 1 O00 x 2000

1 ...

Para accesar alguna de las casillas de la matriz debemos especificar la columna y el renglón entre paréntesis, esto seguido del nombre-del-puntero, por ejemplo:

a(10,15) = 33;

guarda el número 33 en la casilla ubicada en la columna 10 y en el renglón 15. Para el caso de la matriz 'VIDEO-VGA los indices permitidos son [O-6391 y de [O-4791 para las columnas y los renglones respectivamente.

Operaciones permitidas sobre punteros vfp

Dadas las siguientes declaraciones de punteros vfp:

(a) vfp<char> mi-char; (b) vfpcchar> mi-arreglo-de-chars(4); (c) vfp<char> mi-matriz-de-chars(3,3)

En el caso de un solo elemento no tiene sentido incrementar o decrementar el puntero, pero en los otros dos casos es posible no solo incrementar o decrementar al puntero vfp sino que también es posible sumar y restar cierto desplazamiento al puntero a la colección de datos. Las siguientes instrucciones son válidas:

mi-arreglo-de-chars++; ++mi-arreglo-de-chars; mi-arreglo-de-chars + 1 O; mi-matriz-de-chars--; --mi-matriz-de-chars; mi-matriz-de-chars - 2;

Recordemos que los indices para un arreglo lineal de 10 elemento por ejemplo, van de O - 9, por lo que una referencia al 10 elemento causará un error que detendrá el programa y enviara un mensaje indicando el tipo de error.

La función miembro actual() de la clase vfp

En la declaración de la clase vfp se ha incluido una función miembro llamada actual(), esta función se emplea especialmente en la lectura de elementos apuntados por objetos vfp. Esta función es útil cuando desconocemos el índice del arreglo en memoria o de la matriz utilizada, esto pudiera ocurrir después de emplear las operadores ++ o -- por lo que el programador pudiera perder el índice 'actual'. A continuación se muestra el uso de esta función:

#include "XMM.H"

typedef struct { int Re; int Im; } complejo;

void main()

int real, imaginaria; vfpccomplejo> a(lO0); // Arreglo de 100 números complejos a.malloc-vfp(); // Reservamos memoria a++; a + 33; ++a; a - 12; real = a.actual().Re; // Parte real del incide actual imaginaria = a.actual().lm; // Parte imaginaria del índice actual a.free-vfp(); // Liberamos memoria

{

... 1 Observe como al punteo 'a' ha sido afectado por operaciones de incremento y adición, por lo que resulta

útil tomar el contenido del índice actual sin importar cual sea éste.

A continuación se presenta el listado completo del archivo XMM.H:

I**mm**************H************H**H******M***********~*************

* Proyecto en la ingenieria Electronica I I

Asesor: M. en C. Miguel Angel Pizaña Lopez

Alumno: Carlos Arias Mendoza

* *****M Librerias para el uso de memoria extendida *******

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I *

#include <conio.h> #include <stdlib.h> #include <stdio.h> #include <alloc.h> #include <dos.h> #define buffer-tam 1024

#define error-70h V\n Error(70) Se ha excedido el limite superior del arreglo ..." #define error-71h "\r\n Error(7l) Se ha excedido el limite inferior del arreglo ..." #define error-72h Yr\n Error(72) Se ha excedido el número de renglones del arreglo ..." #define error-73h 'V\n Error(73) Se ha excedido el número de columnas del arreglo ..." #define error-80h 'V\n Error(8O) Función Desconocida.. ." #define error-81 h "\r\n Error(81) VDISK-Ramdisk encontrado ..." #define error-82h %\n Error(82) Error en la linea de direcciones A20..." #define error-8Eh %\n Error(8E) Error general de controlador ..." #define error-8Fh "\r\n Error(8F) Error irrecuperable ..." #define error-90h "\r\n Error(9O) HMA no presente ..." #define error-91 h "\r\n Error(91) HMA ya asignado ..." #define error-92h "\r\n Error(92) Tamaño especificado de DX demasiado pequeño ..." #define error-93h "Wn Error(93) HMA no alojada ..." #define error-94h Yr\n Error(94) Linea de direcciones A20 aún activa ..." #define error-A0h "\r\n Error(A0) No queda XMS disponible ...'I

#define error-Alh 'k\n Error(A1) Todos los handles XMS ocupados ..." #define error-A2h "\r\n Error(A2) Handle no válido ..." #define error-A3h "\r\n Error(A3) Handle fuente no válido ..." #define error-A4h %\n Error(A4) Offset fuente no válido ..." #define error-A5h %\n Error(A5) Handle destino no válido ..." #define error-A6h 'V\n Error(A6) Offset destino no válido ..." #define error-A7h V\n Error(A7) Longitud incorrecta para función MOVE ..." #define error-A8h Yr\n Error(A8) Solapado incorrecto en función MOVE ..." #define error-A9h 'V\n Error(A9) Parity-Error ..." #define error-AAh "\r\n Error(AA) UMB no bloqueado ..." #define error-ABh Yr\n Error(AB) UMB aún bloqueado ..." #define error-ACh 'V\n Error(AC) Rebase del controlador de bloqueo UMB ..." #define error-ADh Yr\n Error(AD) UMB no se puede bloquear ..." #define error-B0h '9r\n Error(B0) UMB disponible es más pequeño ..." #define error-Blh "\r\n Error(B1) No hay UMB disponible ..." #define error-B2h "\r\n Error(B2) Dirección del segmento UMB incorrecta ..."

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I static struct copia {

unsigned long size; unsigned int Handle-Fuente; unsigned long Offset-Fuente; unsigned int Handle-Destino; unsigned long Offset-Destino; I x ; ............................................................................. I

............................................................................. I template <class T> class vfp

public:

vfp(unsigned int valor); vfp(unsigned int renglon,unsigned int columna); vfp<T> operator=(T info); T& operator=(vfp<T>); vfp<T> operator++(int); vfp<T> operator++(); vfp<T> operator--(int); vfp<T> operator--(); vfp<T> operator+(unsigned int incremento); vfp<T> operator-(unsigned int decremento); T& operator[](unsigned int finger); T& operator()(unsigned int renglon,unsigned int columna); T& actual(); unsigned int XMS-Libre(); unsigned int malloc-vfp(); void free-vfp(); void Error(unsigned char code); private: unsigned int Check-for-XMS(); void (far *X")(); T *buffer; unsigned int Presente-XMM; unsigned int XMS-handle; unsigned int indice; unsigned int buffer-size; unsigned int size-of-type; unsigned int indice-izq; unsigned int indice-der; unsigned int num-renglones; unsigned int num-columnas; unsigned int extend-izq; unsigned int extend-der; unsigned long limite; unsigned long size-of-block;

{

VfPO;

1; ............................................................................. I

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> vfp<T>::vfp()

if ( Check-for-XMS() ) Presente-XMM = 1;

size-of-type = sizeof(T); size-of-block = 1 ; buffer-size = buffer-tamlsize-of-type; indice = O; indice-izq = O; indice-der = buffer-size - 1; extend-izq = 1; extend-der = buffer-tam; limite = O; buffer = (T *)calloc(buffer-size,size-of-type);

1

{

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I

............................................................................. I template <class T> vfp<T>::vfp(unsigned int valor)

if ( Check-for-XMS() ) Presente-XMM = 1;

size-of-type = sizeof(T); size-of-block = valor; buffer-size = buffer-tamlsize-of-type; indice = O; indice-izq = O; indice-der = buffer-size - 1 ; extend-izq = 1; extend-der = buffer-tam; limite = valor - 1 ; buffer = (T *)calloc(buffer-size,size-of-type);

1

{

............................................................................. I

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> vfp<T>::vfp(unsigned int renglon,unsigned int columna)

if ( Check-for-XMS() ) Presente-XMM = 1;

size-of-type = sizeof(T); size-of-block = renglon*columna; buffer-size = buffer-tamlsize-of-type; indice = O; indice-izq = O; indice-der = buffer-size - 1 ; num-renglones = renglon; num-columnas = columna; extend-izq = 1; extend-der = buffer-tam; limite = (renglon * columna) - 1; buffer = (T *)calloc(buffer-size,size-of-type); 1 ............................................................................. I

{

............................................................................. I template <class T> vfp<T> vfp<T>::operator=(T info) { buffer[indice] = info; return(*this);

} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> T& vfp<T>::actual()

return(buffer[indice]); {

1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> vfp<T> vfp<T>::operator++(int)

int new-index; new-index = indice; new-index++; if ( new-index <= limite ) indice++;

else Error(Ox70);

{

return(*this); 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> vfp<T> vfp<T>::operator++() { int new-index; new-index = indice; new-index++; if ( new-index <= limite ) indice++;

else Error(Ox70);

returnrthis); 1 ............................................................................. I ............................................................................. I template <class T> vfp<T> vfp<T>::operator--(int) { int new-index; new-index = indice; new-index--; if ( new-index >= O ) indice-;

else Error(Ox71);

return(*this);

............................................................................. I

............................................................................. I template <class T> vfp<T> vfp<T>::operator--() { int new-index; new-index = indice; new-index--; if ( new-index >= O ) indice--;

else Error(Ox71);

return(*this); 1 ............................................................................. I ............................................................................. I template <class T> vfp<T> vfp<T>::operator+(unsigned int incremento) { int new-index; new-index = indice + incremento; if ( new-index <= limite ) indice = new-index; else Error(Ox70);

return(*this); 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I ............................................................................. I template <class T> vfp<T> vfp<T>::operator-(unsigned int decremento) { int new-index; new-index = indice - decremento; if ( new-index >= O ) indice = new-index; else Error(Ox71);

returnythis); 1 ............................................................................. I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> T& vfp<T>::operator[](unsigned int finger) { unsigned int indice-temporal; indice-temporal = finger; indice-temporal %= buffer-size ; if ( finger > limite ) Error(Ox71);

if ( finger >= indice-izq && finger <= indice-der ) return(buffer[indice-temporal]); else { copy-RAM_to-XMS(XMM,1024,buffer,XMS-handle,extend-izq); indice-temporal = (finger I buffer-size); extend-izq = indice-temporal * buffer-tam; extend-der = extend-izq + buffer-tam;

indice-temporal *= buffer-size; indice-izq = indice-temporal; indice-der = indice-izq + buffer-size - 1 ; copy-XMS-to-RAM(XMM,1024,buffer,XMS-handle,extend-izq); 1 return(buffer[indice-temporal]);

1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I ............................................................................. I template <class T> T& vfp<T>::operator()(unsigned int renglon,unsigned int columna) { unsigned int indice-tempora1,finger; if ( renglon >= num-renglones ) Error(Ox72);

if ( columna >= num-columnas ) Error(Ox73);

finger = indice-temporal = renglon*num-columnas + columna; if ( finger > limite ) Error(Ox71);

indice-temporal %= buffer-size ; if ( finger >= indice-izq && finger <= indice-der ) return(buffer[indice-temporal]);

else { copy-RAM-to_XMS(XMM,1024,buffer,XMS-handle,extend-izq); indice-temporal = (finger I buffer-size); extend-izq = indice-temporal * buffer-tam; extend-der = extend-izq + buffer-tam; indice-temporal *= buffer-size; indice-izq = indice-temporal; indice-der = indice-izq + buffer-size - 1 ; copy-XMS-to-RAM(XMM,1024,buffer,XMS-handle,extend-izq); 1 return(buffer[indice-temporal]); 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> unsigned int vfp<T>::Check-for-XMS()

unsigned char code; unsigned long mascara; unsigned int registro-ax; unsigned int XMS-Version,XMS-Revision; void (far *manejador-XMS)();

{

union REGS regs; struct REGPACK reg;

reg.r-ax = 0x4300; I* Comprobar si el controlador está instalado *I intr(Ox2F, &reg); regs.x.ax = reg.r-ax;

if ( regs.h.al == 0x80 ) { reg.r-ax = 0x4310; I* Obtener la dirección del XMM *I intr(Ox2F, &reg);

mascara = reg.r-es; mascara = mascara << 16; (unsigned long )manejador-XMS = mascara; (unsigned int)manejador-XMS = reg.r-bx;

(*manejador-XMS)(); XMS-Version = - A X ; regs.x. bx = -EX; XMS-Revision = XMS-Version; XMS-Version = XMS-Version >> 8; XMS-Revision = (unsigned char)XMS-Revision; code = regs.h.bl;

- AX = o;

if ( XMS-Version <= 2 ) { Error(code); return(0);

1

{ else

XMM = manejador-XMS; Presente-XMM = 1 ; 1 1

return( 1 ); 1 ............................................................................. I ............................................................................. I template <class T> unsigned int vfp<T>::XMS-Libre() { unsigned char code; unsigned int longitud,size; union REGS regs; struct REGPACK reg;

- AX = 0x0800; (*XMM)O; longitud = - A X ; return(1ongitud);

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> unsigned int vfp<T>::malloc-vfp()

unsigned char code; unsigned int handle; unsigned long bytes; unsigned int code-error; unsigned int result; unsigned int k-bytes; unsigned int tipo; unsigned long bloque;

{

if ( Presente-XMM ) { tipo = size-of-type; bloque = size-of-block;

bytes = tipo * bloque; if ( bytes c= buffer-tam ) bytes = ( bytes/buffer-tam );

else bytes = ( byteslbuffer-tam ) + 1 ;

k-bytes = bytes; - AX = 0x0900; - DX = k-bytes; (*X")(); result = - A X ; code-error = -BX; handle = -DX; if ( result == 1 ) { XMS-handle = handle; return(hand1e);

1

{ code = (unsigned char)code-error; Error(code);

1

else

1 return(0);

ternplatecclass T> void vfpcT>::free-vfp() { unsigned char code; unsigned int code-error; unsigned int result;

- AX = OxOA00; - DX = XMS-handle; (*X")(); result = - A X ; code-error = -BX; if ( result == 1 ) return;

else { code = (unsigned char)code-error; Error(code);

}

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I template <class T> void vfp<T>::Error(unsigned char code) { switch(code) { case 0x70 : printf(error-70h);break; case 0x71 : printf(error-71 h);break; case 0x72 : printf(error-72h);break; case 0x73 : printf(error-73h);break; case 0x80 : printf(error-80h);break; case 0x81 : printf(error-81 h);break; case 0x82 : printf(error-82h);break; case Ox8E : printf(error-8Eh);break; case Ox8F : printf(error-8Fh);break; case Ox90 : printf(error-90h);break; case Ox91 : printf(error-91 h);break; case 0x92 : printf(error-92h);break; case 0x93 : printf(error-93h);break; case 0x94 : printf(error-94h);break; case O x A O : printf(error-A0h);break; case O x A l : printf(error-Al h);break; case OxA2 : printf(error-AZh);break; case OxA3 : ~rintf(error~A3h);break; case OxA4 : printf(error-A4h); break; case OxA5 : printf(error-A5h);break; case OxA6 : printf(error-A6h);break; case OxA7 : printf(error-A7h);break; case OxA8 : printf(error-A8h);break; case OxA9 : printf(error-A9h);break; case O x A A : printf(error-Mh);break; case OxAB : printf(error-ABh);break; case OxAC : printf(error-ACh);break; case OxAD : printf(error-ADh);break; case OxBO : printf(error-B0h);break; case OxBl : printf(error-B1 h);break; case OxB2 : printf(error-B2h);break;

I exit(0);

I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I unsigned int copy-FWM-to-XMS(void (far *ptr)(),unsigned long bytes,void far *fuente-offsetunsigned int handle,unsigned long desplazamiento)

unsigned char code; unsigned int code-error; unsigned int result; void (far *manejador-X")(); manejador-XMM = ptr;

{

X.size = bytes; X.Handle-Fuente = O; I* O = RAM *I X.Offset-Fuente = (unsigned 1ong)fuente-offset; X.Handle-Destino = handle; X.Offset-Destino = desplazamiento;

- SI = FP-OFF(&X); - DS = FP-SEG(&X); - AX = OxOB00; (*manejador-X")(); result = - A X ; code-error = -BX; if ( result == 1 ) return(1);

else { code = (unsigned char)code-error; cprintf('lr\n\a Error! (%X) No se pudo realizar la copia de RAM a XMS ...", code); 1 return(0);

1 ............................................................................. I ............................................................................. I unsigned int copy-XMS-to-RAM(void (far *ptr)(),unsigned long bytes,void far *fuente-offset,unsigned int handle,unsigned long desplazamiento)

unsigned char code; unsigned int code-error; unsigned int result; void (far *manejador-X")(); manejador-XMM = ptr;

{

X.size = bytes; X.Handle-Fuente = handle; X.Offset-Fuente = desplazamiento; X.Handle-Destino = O; /* O =.RAM *I X.Offset-Destino = (unsigned 1ong)fuente-offset;

- SI = FP-OFF(&X); - DS = FP-SEG(&X); - AX = OxOB00; (*manejador-X")(); result = - A X ; code-error = -BX; if ( result == 1 ) {

1

{

return(1);

else

code = (unsigned char)code-error; cprintf('lr\n\a Error! (%X) No se pudo realizar la copia de XMS a FWM...",code);

1 return(0);

1

Bibliografía

Borland C++ Handbook Chris H. Pappas &William H. Murray Ill Editorial McGraw Hill

Graficas por Computadora Segunda Edición Donald Hearn & M. Pauline Baker Editorial Prentice Hall

Gráficos Animados por Computadora David Fox & Mitchell Waite Editorial McGraw Hill