Ask Question

Name:
Title:
Your Question:

Answer Question

Name:
Your Answer:
User Submitted Source Code!


Description:
  Rotacubo
Language: PASCAL
Code:
{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Rotar un cubo en 3D   }
{    ROTACUBO.PAS          }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Turbo Pascal 7.0    }
{--------------------------}

program RotaCubo;
{$asmmode intel}
{$G+}
{
  Basado en un fuente de Dominio Público, por
  Peter M. Gruhn 1993

  El original se puede encontrar en los SWAG

  Modificaciones por Nacho Cabanes, 1996:
  - Modo 320x200x256, sin BGI (usa algoritmo de Bresenham para dibujar
    líneas y sincroniza con el barrido de la VGA).
  - Emplea algo de ensamblador (ver Ampliación 5)
  - El cubo se mueve sólo.


Posibles mejoras (muchas, sólo pongo algunas como ejemplo):
  - Emplear aritmética entera, y tablas de senos y cosenos para
    mayor velocidad, aunque en este caso no es necesario, porque
    se rotan muy pocos puntos.
  - Que las rotaciones no sean aditivas (se vuelva a rotar a partir
    del original, no de la figura ya rotada, para que los errores no
    se vayan sumando).
  - Más flexibilidad: definir las líneas a partir de sus dos vértices
    en vez de crear las figuras "a pelo".
  - Definir caras 3D para dibujar figuras sólidas.
}

uses
  crt;

const
  gradRad = 1 {grados} * 3.1415926535 {radianes} / 180 {por grado};
  { Convierte un grado a radianes (sin y cos usan radianes) }

type
  punto = record   { Punto en 3d }
    x, y, z : real;
  end;

var
  img : array [0..7] of punto;   { Nuestra imagen tendrá 8 puntos }
  tecla: char;
  color: byte;


procedure retrace; assembler;   { Espera el barrido de la pantalla }
asm
  mov dx,3dah
 @vert1:
  in al,dx
  test al,8
  jz @vert1
 @vert2:
  in al,dx
  test al,8
  jnz @vert2
end;


procedure init;     { Inicializa }
begin
  asm
    mov ax, $13     { Modo 320x200x256 }
    int $10
  end;
  { Datos de la imagen }
  img[0].x := -35;  img[0].y := -35;  img[0].z := -35;
  img[1].x := 35;   img[1].y := -35;  img[1].z := -35;
  img[2].x := 35;   img[2].y := 35;   img[2].z := -35;
  img[3].x := -35;  img[3].y := 35;   img[3].z := -35;
  img[4].x := -35;  img[4].y := -35;  img[4].z := 35;
  img[5].x := 35;   img[5].y := -35;  img[5].z := 35;
  img[6].x := 35;   img[6].y := 35;   img[6].z := 35;
  img[7].x := -35;  img[7].y := 35;   img[7].z := 35;
end;


Procedure Ponpixel (X,Y : Integer; Col : Byte); assembler;
  { Dibuja un punto en la pantalla gráfica, en 320x200x256 }
  Asm
    mov     ax,$A000
    mov     es,ax
    mov     bx,[X]
    mov     dx,[Y]
    mov     di,bx
    mov     bx, dx                  { bx = dx }
    shl     dx, 8                   { dx = dx * 256 }
    shl     bx, 6                   { bx = bx * 64 }
    add     dx, bx                  { dx = dx + bx (= y*320) }
    add     di, dx                  { Posición final }
    mov     al, [Col]
    stosb
end;


procedure LineaB(x, y, x2, y2 : word; color: byte);
{ Dibuja una línea, basado en el algoritmo de Bresenham }
{ Original de Sean Palmer; una pequeña corrección por Nacho Cabanes }
var
 d,
 dx, dy,             { Salto total según x e y }
 ai, bi,
 xi, yi              { Incrementos: +1 ó -1, según se recorra }
         : integer;
begin
 if (x=x2) and (y=y2) then  { Corrige un fallo: si es un sólo punto }
   begin                    { el algoritmo (tal y como era) falla }
   PonPixel(x,y,color);
   exit;
   end;
 if (x < x2) then    { Si las componentes X están ordenadas }
 begin
   xi := 1;          { Incremento +1 }
   dx := x2 - x;     { Espacio total en x }
 end
 else                { Si no están ordenadas }
 begin
   xi := - 1;        { Increm. -1 (hacia atrás) }
   dx := x - x2;     { y salto al revés (negativo) }
 end;
 if (y < y2) then    { Análogo para las componentes Y }
 begin
   yi := 1;
   dy := y2 - y;
 end
 else
 begin
   yi := - 1;
   dy := y - y2;
 end;
 PonPixel(x, y,color);   { Dibujamos el primer punto }
 if dx > dy then         { Si hay más salto según x que según y }
 begin                   { (recta más cerca de la horizontal) }
   ai := (dy - dx) * 2;   { Variables auxiliares del algoritmo }
   bi := dy * 2;          { ai y bi no varían; d comprueba cuando }
   d  := bi - dx;         { debe cambiar la coordenada y }
   repeat
     if (d >= 0) then     { Comprueba si hay que avanzar según y }
     begin
       y := y + yi;       { Incrementamos Y (+1 ó -1) }
       d := d + ai;       { y la variable de control }
     end
     else
       d := d + bi;       { Si no varía y, d sí lo hace según bi }
     x := x + xi;         { Incrementamos X como corresponda }
     PonPixel(x, y, color);          { Dibujamos el punto }
   until (x = x2);           { Se repite hasta alcanzar el final }
 end
 else                        { Si hay más salto según y que según x }
 begin                       { (más vertical), todo similar }
   ai := (dx - dy) * 2;
   bi := dx * 2;
   d  := bi - dy;
   repeat
     if (d >= 0) then
     begin
       x := x + xi;
       d := d + ai;
     end
     else
       d := d + bi;
     y := y + yi;
     PonPixel(x, y, color);
   until (y = y2);
 end;
end;



procedure linea(x1, y1, z1, x2, y2, z2 : real);
{ Convierte las coordenadas de real a entero y muestra centrado en
  pantalla.  La coordenada Z se desprecia en este ejemplo, pero se podría
  usar para dar una mayor sensación de perspectiva (cónica en vez de
  cilíndrica. }
begin
  lineaB(round(x1) + 160, round(y1) + 100, round(x2) + 160, round(y2) + 100,
    color);
end;


procedure dibujaImg;
{ Dibuja la imagen (ésta en concreto -> poco versátil ) }
begin
  linea(img[0].x, img[0].y, img[0].z, img[1].x, img[1].y, img[1].z);
  linea(img[1].x, img[1].y, img[1].z, img[2].x, img[2].y, img[2].z);
  linea(img[2].x, img[2].y, img[2].z, img[3].x, img[3].y, img[3].z);
  linea(img[3].x, img[3].y, img[3].z, img[0].x, img[0].y, img[0].z);

  linea(img[4].x, img[4].y, img[4].z, img[5].x, img[5].y, img[5].z);
  linea(img[5].x, img[5].y, img[5].z, img[6].x, img[6].y, img[6].z);
  linea(img[6].x, img[6].y, img[6].z, img[7].x, img[7].y, img[7].z);
  linea(img[7].x, img[7].y, img[7].z, img[4].x, img[4].y, img[4].z);

  linea(img[0].x, img[0].y, img[0].z, img[4].x, img[4].y, img[4].z);
  linea(img[1].x, img[1].y, img[1].z, img[5].x, img[5].y, img[5].z);
  linea(img[2].x, img[2].y, img[2].z, img[6].x, img[6].y, img[6].z);
  linea(img[3].x, img[3].y, img[3].z, img[7].x, img[7].y, img[7].z);

  linea(img[0].x, img[0].y, img[0].z, img[5].x, img[5].y, img[5].z);
  linea(img[1].x, img[1].y, img[1].z, img[4].x, img[4].y, img[4].z);
end;


procedure rotx;
{ Rotación en torno al eje X.  Un poco de álgebra lineal... }
var
  i : integer;
begin
  color := 0;
  dibujaImg;
  for i := 0 to 7 do
  begin
    img[i].x :=  img[i].x;
    img[i].y :=  img[i].y * cos(gradRad) + img[i].z * sin(gradRad);
    img[i].z := -img[i].y * sin(gradRad) + img[i].z * cos(gradRad);
  end;
  color := 15;
  dibujaImg;
end;


procedure roty;
{ Rotación en torno al eje Y }
var
  i : integer;
begin
  color := 0;
  dibujaImg;
  for i := 0 to 7 do
  begin
    img[i].x := img[i].x * cos(gradRad) - img[i].z * sin(gradRad);
    img[i].y := img[i].y;
    img[i].z := img[i].x * sin(gradRad) + img[i].z * cos(gradRad);
  end;
  color := 15;
  dibujaImg;
end;


procedure rotz;
{ Rotación en torno al eje Z }
var
  i : integer;
begin
  color := 0;
  dibujaImg;
  for i := 0 to 7 do
  begin
    img[i].x :=  img[i].x * cos(gradRad) + img[i].y * sin(gradRad);
    img[i].y := -img[i].x * sin(gradRad) + img[i].y * cos(gradRad);
    img[i].z :=  img[i].z;
  end;
  color := 15;
  dibujaImg;
end;


begin
  init;              { Inicializar }
  repeat
    retrace; rotx;   { Rotar y dibujar }
    retrace; roty;
    retrace; rotz;
  until (keypressed) { Hasta pulsar ESC }
    and (readkey = #27);
  asm
    mov ax, 3        { Modo texto }
    int $10
  end;
end.     
          
Comments: