void polygon3d(BITMAP *bmp, int type, BITMAP *texture, int vc, V3D *vtx[]);
void polygon3d_f(BITMAP *bmp, int type, BITMAP *texture, int vc, V3D_f *vtx[]);
Dibuja polígonos 3d en el bitmap especificado, usando el modo de render
especificado. A diferencia de la función polygon(), estas rutinas no
soportan figuras cóncavas o con intersecciones, y no pueden dibujar sobre
bitmaps de pantalla en modo-X ( si quiere escribir código en modo-X,
dibuja en un bitmap de memoria y cópialo a la pantalla). El ancho y alto
del bitmap de la textura debe ser un múltiplo de dos, pero puede ser
diferente, ejemplo: una textura 64x16 está bien, pero una de 17x3 no. El
parámetro que cuenta los vértices (vc) debe ser seguido por un array que
contiene el número apropiado de punteros a estructuras vertex:
polygon3d() usa la estructura de punto fijo V3D, mientras que
polygon3d_f() usa la estructura coma flotante V3D_f. Estas son definidas
así:
El cómo se almacenan los datos de los vértices depende del modo de render:typedef struct V3D { fixed x, y, z; - posición fixed u, v; - coordenadas de la textura int c; - color } V3D; typedef struct V3D_f { float x, y, z; - posición float u, v; - coordenadas de la textura int c; - color } V3D_f;
Los valores x e y especifican la posición del vértice en coordenadas de pantalla 2d.
El valor z sólo es necesario cuando use corrección de perspectiva en las texturas, y especifica la profundidad del punto usando coordenadas del mundo 3d.
Las coordenadas u y v sólo son necesarias cuando use texturas, y especifican la posición del punto en el bitmap de la textura que se corresponde con el vértice indicado. El plano de la textura es un plano infinito con el bitmap repetido sobre toda la superficie, y la imagen del polígono resultante en este plano rellenará el polígono cuando se dibuje en pantalla.
Nos referimos a pixels en el plano de la textura como texels. Cada texel es un bloque, nó sólo un punto, y los números enteros de u y v se refieren a la esquina superior izquierda del texel. Esto tiene varias implicaciones. Si quiere dibujar un polígono rectangular y aplicar una textura de 32x32 sobre él, debe usar las coordenadas de textura (0,0), (0,32), (32,32) y (32,0), asumiendo que los vértices son especificados en órden antihorario. La textura será aplicada perfectamente sobre el polígono. No obstante, note que si ajustamos u=32, la última columna de texels que se verán en la pantalla serán los que están en u=31, y lo mismo ocurre para v. Esto es porque las coordenadas se refieren a la esquina superior izquierda de los texels. En efecto, las coordenadas de textura por la derecha y por abajo son exclusivas.
Aquí hay otro punto interesante. Si tiene dos polígonos juntos que comparten dos vértices (como las dos partes de una pieza de cartón doblada), y quiere aplicar sobre ellos una textura contínua, los valores u y v de los vértices que están en la junta serán iguales para ambos polígonos. Por ejemplo, si ambos son rectangulares, un polígono usará (0,0), (0,32), (32,32) y (32,0), y el otro usará (32,0), (32,32), (64,32) y (64,0). Esto aplicará la textura perfectamente.
Por supuesto puede usar números con decimales para u y v indicando puntos que están parcialmente en un texel. Además, dado que el plano de la textura es infinito, puede especificar valores mayores que el tamaño de la textura. Esto puede ser usado para repetir la textura varias veces sobre el polígono.
El valor c especifica el color del vértice, y es interpretado de forma diferente por los modos de render.
El parámetro type especifica el modo de render, y puede ser cualquiera de los siguientes:
POLYTYPE_FLAT:
Un simple polígono con sombreado plano, que toma el color del valor c
del primer vértice. Este tipo de polígono es afectado por la función
drawing_mode(), por lo que puede ser usado para renderizar polígonos
transparentes o XOR.
POLYTYPE_GCOL:
Un polígono con un color de sombreado goraud. Los colores de cada
vértice son tomados del valor c, e interpolados a través del polígono.
Esto es muy rápido, pero sólo funcionará en modos de 256 colores si su
paleta tiene un suave gradiente de colores. En modos truecolor
interpreta el color como valor empaquetado en formato directo de
hardware producido por la función makecol().
POLYTYPE_GRGB:
Un polígono con sombreado goraud que interpola tripletes RGB en vez de
un solo color. En modos de 256 colores usa la tabla global rgb_map
para convertir el resultado a color de 8 bits, por lo que sólo puede
ser usado después de que haya creado una tabla de mapa de colores. Los
colores para cada vértice son tomados del valor c, que es interpretado
como un triplete RGB de 24 bits (0xFF0000 es rojo, 0x00FF00 es verde y
0x0000FF es azul).
POLYTYPE_ATEX:
Un polígono con textura afín. Esto dibuja la textura a través del
polígono con una simple interpolación 2d lineal, que es rápida pero
matemáticamente incorrecta. Puede estar bien si el polígono es pequeño
o plano hacia la cámara, pero como no cuenta con la acortación de
perspectiva, puede producir extraños artefactos movidos en la textura.
Para ver lo que quiero decir, ejecuta test.exe y mire lo que pasa con
el test polygon3d() cuando hace un zoom muy cerca del cubo.
POLYTYPE_PTEX:
Un polígono texturizado con corrección de perspectiva. Esto usa el
valor z de la estructura del vértice así como las coordenadas u/v, por
lo que las texturas se ven correctamente independientemente del ángulo
de visualización. Ya que esto envuelve cálculos de división en al
bucle interior de la texturización, este modo es mucho más lento que
POLYTYPE_ATEX, y usa coma flotante, por lo que será muy lento en
cualquier cosa peor que un Pentium (incluso con una FPU, un 486 no es
capaz de mezclar división de coma flotante con otras operaciones de
enteros tal y como puede hacer un Pentium).
POLYTYPE_ATEX_MASK:
POLYTYPE_PTEX_MASK:
Como POLYTYPE_ATEX and POLYTYPE_PTEX, pero los pixels a cero de la
textura son ignorados, permitiendo que la textura sea transparente.
POLYTYPE_ATEX_LIT:
POLYTYPE_PTEX_LIT:
Como POLYTYPE_ATEX y POLYTYPE_PTEX, pero la tabla global color_map
(para modos de 256 colores) o la función de fundido (para modos
truecolor no-MMX) es usada para fundir la textura con el nivel de luz
tomado del valor c en la estructura del vértice. ¡Esto sólo puede ser
usado después de que haya creado una tabla de mapa de color o
funciones de fundido!
POLYTYPE_ATEX_MASK_LIT:
POLYTYPE_PTEX_MASK_LIT:
Como POLYTYPE_ATEX_LIT y POLYTYPE_PTEX_LIT, pero los pixels a cero de
la textura son ignorados, permitiendo que la textura sea transparente.
POLYTYPE_ATEX_TRANS:
POLYTYPE_PTEX_TRANS:
Renderiza texturas translúcidas. Son aplicables todas las reglas
generales de dibujado translúcido. No obstante, estos modos tienen una
limitación: sólo funcionan con bitmaps en memoria o con memoria de
vídeo lineal (no con video por bancos). Ni si quiera lo intente en
estos casos, ya que las funciones no realizan chequeos y su programa
morirá horriblemente (o como mínimo dibujará mal las cosas).
POLYTYPE_ATEX_MASK_TRANS:
POLYTYPE_PTEX_MASK_TRANS:
Como POLYTYPE_ATEX_TRANS y POLYTYPE_PTEX_TRANS, pero los pixels a
cero de la textura son ignorados.
Si el bit CPU_MMX de la variable global cpu_capabilities está activado, las rutinas GRGB y *LIT truecolor serán optimizadas usando instrucciones MMX. Si el bit CPU_3DNOW está activado, las rutinas truecolor PTEX*LIT tomarán ventaja de la extensión de CPU 3DNow!.
Usar rutinas MMX para *LIT tiene un efecto secundario: normalmente (sin MMX), estas rutinas usan las funciones de fundido y otras funciones de luz, creadas con set_trans_blender() o set_blender_mode(). Las versiones MMX sólo usan el valor RGB que se pasa a set_trans_blender() y hacen la interpolación lineal internamente. Por esta razón, un nuevo conjundo de funciones de fundido que se pasa a set_blender_mode() es ignorado.
Relacionado con: triangle3d, quad3d, polygon, clip3d, cpu_capabilities.void triangle3d(BITMAP *bmp, int type, BITMAP *tex, V3D *v1, *v2, *v3);
Relacionado con: polygon3d, quad3d, triangle.void quad3d(BITMAP *bmp, int type, BITMAP *tex, V3D *v1, *v2, *v3, *v4);
Relacionado con: polygon3d, triangle3d.int clip3d_f(int type, float min_z, float max_z, int vc, V3D_f *vtx[], V3D_f *vout[], V3D_f *vtmp[], int out[]);
Relacionado con: polygon3d, clip3d.int clip3d(int type, fixed min_z, fixed max_z, int vc, V3D *vtx[], V3D *vout[], V3D *vtmp[], int out[]);
Relacionado con: polygon3d, clip3d_f.
Un Z-buffer almacena la profundidad de cada pixel dibujado en una pantalla. Cuando un objeto 3d es renderizado, la profundidad de cada pixel es comparada con el valor ya almacenado en el Z-buffer: si el pixel es más cercano se dibuja, en caso contrario se ignora.
No hace falta ordenar los polígonos. No obstante, sigue siendo útil ignorar los polígonos que no están de cara a la cámara, ya que así se previene la comparación de muchos polígonos ocultos contra el Z-buffer. El render mediante Z-buffer es el único algoritmo soportado por Allegro que resuelve directamente la intersección entre figuras (mire por ejemplo exzbuf.c). El precio que hay que pagar son unas rutinas más complejas (y más lentas).
Los polígonos con Z-buffer son por diseño una extensión de los estilos de render normales POLYTYPE_*. Sólo hay que hacer una OR entre POLYTYPE y el valor POLYTYPE_ZBUF, y las rutinas normales como polygon3d(), polygon3d_f(), quad3d(), etc, renderizarán polígonos con Z-buffer.
Ejemplo:
polygon3d(bmp, POLYTYPE_ATEX | POLYTYPE_ZBUF, tex, vc, vtx);
Por supuesto, las coordenadas z deben ser válidas sin importar el estilo de render.
El procedimiento de render con Z-buffer parece un render con doble buffer. Debería seguir los siguientes pasos: crear el Z-buffer al comienzo de su programa y hacer que la librería lo use mediante set_zbuffer(). Entonces, por cada frame, borre el Z-buffer y dibuje polígonos con POLYTYPE_* | POLYTYPE_ZBUF para finalmente destruir el Z-buffer al finalizar su programa.
Notas sobre los renders con Z-buffer:
Relacionado con: create_sub_zbuffer, set_zbuffer, clear_zbuffer, destroy_zbuffer.ZBUFFER *create_sub_zbuffer(ZBUFFER *parent, int x, int y, int width, int height);
Cuando dibuje con z-buffer en un bitmap, la esquina superior izquierda del bitmap siempre está alineada con la esquina superior izquierda del z-buffer actual. Por lo que esta función es útil principalmente si quiere dibujar en un sub-bitmap y usar el área correspondiente del z-buffer. En otros casos, ej, si quiere dibujar en un sub-bitmap de la pantalla (y no en otras partes de la pantalla), normalmente querrá crear un z-buffer normal (no un sub-z-buffer) del tamaño de la pantalla. No necesita crear un z-buffer del tamaño de la pantalla virtual y entonces un sub-z-buffer de éste.
Relacionado con: create_zbuffer, create_sub_bitmap, destroy_zbuffer.void set_zbuffer(ZBUFFER *zbuf);
Relacionado con: create_zbuffer, clear_zbuffer, destroy_zbuffer.void clear_zbuffer(ZBUFFER *zbuf, float z);
Relacionado con: create_zbuffer, set_zbuffer, destroy_zbuffer.void destroy_zbuffer(ZBUFFER *zbuf);
Relacionado con: create_zbuffer, set_zbuffer, clear_zbuffer.
Allegro provee dos métodos simples para quitar caras ocultas:
El render de escenas sigue los siguientes pasos aproximádamente:
Por cada linea horizontal del área de visualización se usa una lista ordenada de bordes para saber qué polígonos "están dentro" y cuáles están cerca. Se usa coherencia vertical - la lista de bordes es ordenada a partir de la anterior - no cambiará mucho. Las rutinas de render de escenas usan las mismas rutinas de bajo nivel que polygon3d().
Notas del render de escena:
El render con Z-buffer también funciona con el render de escenas. Puede ser útil si tiene algunos polígonos que se interseccionan, pero la mayoría de los polígonos pueden ser renderizados sin problemas usando el algoritmo normal de ordenado de scanlines. Igual que antes: simplemente haga una OR del POLYTIPE con POLYTYPE_ZBUF. Además, tiene que limpiar el z-buffer al comienzo del frame. Ejemplo:
clear_scene(buffer); if (some_polys_are_zbuf) clear_zbuffer(0.); while (polygons) { ... if (this_poly_is_zbuf) type |= POLYTYPE_ZBUF; scene_polygon3d(type, tex, vc, vtx); } render_scene();
int create_scene(int nedge, int npoly);
Reserva memoria para una escena, nedge y npoly son sus estimaciones de
cuántas aristas y polígonos renderizará (no puede salirse del límite que
especifica aquí). Si usa los mismos valores en llamadas sucesivas, el
espacio será reusado (no nuevos malloc()).
La memoria reservada es algo menor que 150 * (nedge + npoly) bytes. Devuelve cero con éxito, o negativo si no se pudo reservar la memoria.
Relacionado con: create_zbuffer, scene_polygon3d, render_scene, clear_scene, destroy_scene, scene_gap.void clear_scene(BITMAP *bmp);
Relacionado con: create_scene, scene_polygon3d, render_scene, destroy_scene, scene_gap.void destroy_scene();
Relacionado con: create_scene, scene_polygon3d, clear_scene, render_scene, scene_gap.int scene_polygon3d(int type, BITMAP *texture, int vc, V3D *vtx[]);
Los argumentos son iguales que para polygon3d(), excepto por el parámetro de bitmap que falta. Se usará el que indicó mediante clear_scene().
A diferencia de polygon3d(), los polígonos pueden ser cóncavos o estar interseccionados. Las figuras que penetran en otras pueden salir bien, pero no son manejadas realmente por este código.
Note que sólo se almacena un puntero a la textura, por lo que debería mantenerla en memoria hasta render_scene(), donde será usada.
Ya que el estilo FLAT es implementado con la función de bajo nivel hline(), el estilo FLAT está sujeto a DRAW_MODE. Todos los modos son válidos. Junto con el polígono, el modo será almacenado para el momento del render, y también otras variables relacionadas (puntero al mapa de color, puntero al patron, ancla, valores de blending).
El valor de los bits CPU_MMX y CPU_3DNOW de la variable global cpu_capabilities afectará la elección de la rutina de bajo nivel en ensamblador que será usada por render_scene() con este polígono.
Devuelve cero con éxito o negativo si no será renderizado debido a que falta la rutina de render apropiada.
Relacionado con: cpu_capabilities, create_scene, clear_scene, render_scene, destroy_scene, polygon3d.void render_scene();
Note que entre clear_scene() y render_scene() no debería modificar el rectángulo de recorte del bitmap destino. Por razones de velocidad, debería ajustar el rectángulo de recorte al mínimo.
Tenga en cuenta también que las texturas pasadas a scene_polygon3d() son almacenadas como punteros y serán usadas en render_scene().
Relacionado con: create_scene, clear_scene, destroy_scene, scene_gap, scene_polygon3d.extern float scene_gap;
El valor por defecto significa que si los valores 1/z (en espacio proyectado) difieren sólo en 1/100 (uno por ciento), serán considerados iguales y el eje x de los planos será usado para saber qué plano está acercándose mientras nos movemos hacia la derecha.
Valores mayores significan márgenes menores, e incrementan la posibilidad de confundir planos/bordes realmente adyacentes. Valores menores significan márgenes mayores, e incrementan la posibilidad de confundir un polígono cercano con uno adyacejte. El valor de 100 está cercano a lo más óptimo. No obstante, el valor optimo oscila con diferentes resoluciones, y puede ser dependiente de la aplicación. Está aquí para que lo pueda ajustar al máximo.
Relacionado con: create_scene, clear_scene, destroy_scene, render_scene, scene_polygon3d.