VGA – Tutorial 2 – Denthor

                       --==[ PARTE 2 ]==--

Autor: DENTHOR do ASPHYXIA
Atualizado por Snowman
Traduzido por Krull >>> tradução revisada e atualizada em 2019

Para obter os programas mencionados neste tutorial, por favor baixe o zip com a versão antiga do tutorial, neste link.

[Nota: essas coisas entre colchetes foram adicionadas pelo Snownan. O texto original ficou quase todo inalterado, exceto pela inclusão do material em C++]

Introdução

Oi, de novo! Aqui é o Grant Smith, AKA Denthor do ASPHYXIA. Esta é a segunda parte do meu Programa de Treinamento para novos programadores. Eu tive apenas uma resposta indiferente sobre a minha primeira parte da série… (by Krull: Ih, eu também! HAHAHAHA) lembre-se, se eu não ouvir de você, vou assumir que vocês estão todos mortos e eu vou parar de escrever as séries.
😉 E mais, se você entrar em contato comigo, eu vou te dar algumas de nossas rotinas rápidas em Assembly que vão acelerar seus demos infinitamente. Então, vamos lá, mande uma carta para GRANT SMITH na seção principal da Mailbox BBS, inicie uma discussão ou faça algumas perguntas nessa Conferência, Connectix, ou escreva para Grant Smith,
P.O.Box 270
Kloof
3640

Veja, há varios modos de entrar em contato comigo! Use um deles!

Nesta parte, vou colocar a Pallette no seu ritmo. Que diabos é a Pallette? Como eu descubro o que é isso? Como acertar isso? Como é que eu paro o “fuzz” que aparece na tela quando eu mudo a pallette? Como eu apago a tela usando a pallette? Como fazer fade-in na tela? Como fazer fade-out? Por que as ligações telefônicas são tão caras?

A maioria dessas perguntas será respondida nesta, a segunda parte da minha Série de Treinamento para Pascal.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

O que é a Pallette? (paleta de cores)

Umas poucas semanas atrás, um amigo meu estava jogando no computador. No jogo, havia uma máquina com listras de azul atravessando-a. Quando a máquina era ativada, enquanto metade das listras azuis continuavam as mesmas, a outra metade começava a mudar de cor e brilho. Ele me perguntou como duas listras da mesma cor de repente ficavam diferente daquele jeito. A resposta é simples: o programa estava mudando a pallette.

Como você sabe da Parte 1, há 256 cores no modo MCGA, numeradas de 0 a 255. O que você não sabe é que cada uma dessas cores é feita de diferentes intensidades de Vermelho, Verde e Azul, as cores primárias (você deve ter aprendido sobre cores primárias na escola). Estas intensidades são números entre 0 e 63. A cor vermelho brilhante poderia ser obtida, por exemplo, setando-se a intensidade do vermelho para 63, intensidade de verde em 0, e intensidade do azul em 0. Isso significa que 2 cores podem parecer exatamente a mesma, por exemplo, se você setar a cor 10 para vermelho brilhante e a cor 78 para vermelho brilhante. Se você desenhar uma figura usando essas duas cores, ninguém será capaz de dizer a diferença entre as duas… Só quando você mudar a pallete de novo, de uma delas, será possível dizer a diferença.

Também, mudando a pallette inteira, você pode obter os efeitos “Fade in” e “fade out” encontrados em muitos jogos e demos. A manipulação de Pallette pode se tornar muito confusa para algumas pessoas, porque cores que parecem a mesma, são, na verdade, totalmente distintas.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como eu leio o valor de pallette de uma cor?

Isso é muito fácil de fazer. Para ler o valor na pallette, você entra o número da cor que você quer na porta $3c7 [0x03C7], então lê os valores de vermelho, verde e azul respectivamente da porta $3c9 [0x03C9]. Simples, né? Aqui está uma rotina que faz isso para você:

[Pascal]
 Procedure GetPal(ColorNo : Byte; Var R,G,B : Byte);
   { Isso lê os valores de Vermelho, Verde e Azul de uma certa cor e retorna para você }
 Begin
    Port[$3c7] := ColorNo;
    R := Port[$3c9];
    G := Port[$3c9];
    B := Port[$3c9];
 End; 
[C++]
 void GetPal(unsigned char ColorNo, unsigned char &R,unsigned char &G,unsigned char &B) 
{ 
 outp (0x03C7,ColorNo); // aqui está a cor da pallette que eu quero ler
   R = inp (0x03C9);
   G = inp (0x03C9);
   B = inp (0x03C9); 
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como eu seto o valor de pallette de uma cor?

Isso também é tão fácil quanto 3.1415926535897932385. O que você vai fazer é entrar o número da cor que você quer mudar na porta $3c8 [0x03C8], então entrar os valores de vermelho, verde e azul respectivamente da porta $3c9 [0x03C9]. Só por que vocês são todos muito preguiçosos, eu escrevi a rotina para vocês 😉

[Pascal]
 Procedure Pal(ColorNo : Byte; R,G,B : Byte);
   { Isso seta os valores de Vermelho, Verde e Azul para uma certa cor }
 Begin
    Port[$3c8] := ColorNo;
    Port[$3c9] := R;
    Port[$3c9] := G;
    Port[$3c9] := B;
 End; 
[C++]
 void Pal(unsigned char ColorNo, unsigned char R, unsigned char G,unsigned char B) 
{ 
 outp (0x03C8,ColorNo); // aqui está a cor da pallette que eu quero setar
   outp (0x03C9,R);
   outp (0x03C9,G);
   outp (0x03C9,B); 
}

A Asphyxia não usa as rotinas de Pallette acima, nós usamos versões em Assembly, que eu vou dar pra QUEM RESPONDER A ESSA SÉRIE DE TREINAMENTOS (HE HE HE)

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como eu paro o efeito “fuzz” que aparece na minha tela quando eu mudo a pallette?

Se você usou a pallette antes, deve ter notado que há um pouco de “fuzz” na tela quando você a muda. O modo que encontramos isso é o seguinte: há um canhão de elétrons no seu monitor que está constantemente atualizando sua tela, de cima pra baixo. Quando ele chega no fim da tela, ele volta pro topo, para começar a atualizar a tela de novo.

O período em que ele se move do fundo da tela para o topo é chamado Retrace Vertical. Durante o retrace vertical você pode mudar a pallette sem afetar o que está na tela.

O que fazemos é que esperamos até um retrace vertical começar para chamar uma certa rotina; isso significa que tudo que fizermos até agora só vai ser mostrado depois de um retrace, então podemos fazer todo tipo de coisa estranha e fora do comum durante esse retrace e os resultados só serão mostrados quando o retrace for terminado. Isso é bem legal, já que significa que quando mudamos a pallette, o fuzz não aparece na tela, só o resultado (a pallette alterada), é visto após o retrace. Elegante, né? 😉 EU pus a rotina WaitRetrace em Assembly puro no código de amostra que segue junto com esse tutorial.
Use-o sabiamente, meu filho.

NOTA : o WaitRetrace pode ser uma grande ajuda na sua codificação… um código que cabe em um retrace significa que o demo rodará à mesma velocidade, não importando a velocidade do seu computador (a menos que você esteja fazendo muita coisa durante o WaitRetrace e o computador é leeeeeeeeeeeeento). Note que no programa de exemplo a seguir, e no nosso SilkyDemo, a coisa vai rodar na mesma velocidade tanto se o turbo estiver ligado como desligado.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como apagar a tela usando a pallette?

Isto é básico: apenas sete os valores de Vermelho, Verde e Azul de todas as cores para intensidade zero, desse modo:

[Pascal]
 Procedure Blackout;
   { Esta rotina escurece a tela setando os valores na pallette de todas as cores para zero } 
 VAR loop1:integer;
 BEGIN
   WaitRetrace;
   For loop1:=0 to 255 do
     Pal (loop1,0,0,0);
 END; 
[C++]
 void Blackout() { 
 WaitRetrace();
   for (int loop1=0;loop1<256;loop1++)
     Pal (loop1,0,0,0); 
  }

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como fazer um fade-in na tela?

OK, isso pode ser MUITO efetivo. O que você deve fazer primeiro é colocar a pallette numa variável, algo desse tipo:

[Pascal]   
VAR Pall := Array [0.255,1..3] of BYTE;

[C++]      
unsigned char Pall[256][3];

De 0 a 255 é para as 256 cores no modo MCGA, de 1 a 3 é para as intensidades de vermelho, verde e azul;

[Pascal]
 Procedure GrabPallette;
 VAR loop1:integer;
 BEGIN
   For loop1:=0 to 255 do
     Getpal (loop1,pall[loop1,1],pall[loop1,2],pall[loop1,3]);
 END; 
[C++]
 void GrabPallette() { 
 for(int loop1=0;loop1<256;loop1++)
     GetPal(loop1,Pall2[loop1][0],Pall2[loop1][1],Pall2[loop1][2]); 
 }

Isso carrega a pallete dentro da variável pall. Então você deve escurecer a tela (veja acima), e desenhar o que você quer na tela, sem a construção ser mostrada. Então, o que você tem que fazer é passear pela pallette. Para cada cor, você vê se as intensidades individuais são o que elas deveriam ser. Se não, você incrementa uma unidade até que estejam no valor certo. Como as intensidades ficam dentro do intervalo de 0 a 63, você só precisa fazer isso no máximo 64 vezes.

[Pascal]
 Procedure Fadeup;
 VAR loop1,loop2:integer;
     Tmp : Array [1..3] of byte;
       { Esse é o armazenamento temporário de valores de uma cor }
 BEGIN
   For loop1:=1 to 64 do BEGIN
       { Um valor de cor para vermelho, verde ou azul só vai de 0 a 63, então esse loop só precisa ser executado 64 vezes }
     WaitRetrace;
     For loop2:=0 to 255 do BEGIN
       Getpal (loop2,Tmp[1],Tmp[2],Tmp[3]);
       If Tmp[1]<Pall[loop2,1] then inc (Tmp[1]);
       If Tmp[2]<Pall[loop2,2] then inc (Tmp[2]);
       If Tmp[3]<Pall[loop2,3] then inc (Tmp[3]);
         { Se os valores de vermelho, verde e azul, da cor loop2 estão menores do que deveriam ser, incremente-os uma unidade }
       Pal (loop2,Tmp[1],Tmp[2],Tmp[3]);
         { seta a nova, e alterada, paleta de cores }
     END;
   END;
 END; 
[C++]
 void Fadeup() { 
 //Esse é o armazenamento temporário de valores de uma cor
 unsigned char Tmp[3]; 
 // Um valor de cor para vermelho, verde ou azul só vai de 0 a 63, 
   //  então esse loop só precisa ser executado 64 vezes
   for(int loop1=0;loop1<64;loop1++) { 
WaitRetrace();
 for(int loop2=0; loop2<256; loop2++) {
   GetPal(loop2,Tmp[0],Tmp[1],Tmp[2]);
 // Se os valores de vermelho, verde e azul, da cor loop2 estão menores
   // do que deveriam ser, incremente-os uma unidade 
   if ((Tmp[0] < Pall2[loop2][0]) && (Tmp[0] < 63)) Tmp[0]++;
   if ((Tmp[1] < Pall2[loop2][1]) && (Tmp[1] < 63)) Tmp[1]++;
   if ((Tmp[2] < Pall2[loop2][2]) && (Tmp[2] < 63)) Tmp[2]++;
 // seta a nova, e alterada, paleta de cores
   Pal (loop2,Tmp[0],Tmp[1],Tmp[2]);
 }
 }
}

E pronto! Acontece um fade up na tela! Você pode colocar um delay antes do waitretrace se você achar que está rápido demais. Maneiro, né?

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como fazer um fade-out na tela?

Isso é como fazer o fade in, só que ao contrário. O que você tem que fazer é checar a intensidade de cada cor. Se não é zero, você decrementa uma unidade até que seja. BÁÁÁÁSICO!

[Pascal]
 Procedure FadeDown;
 VAR loop1,loop2:integer;
     Tmp : Array [1..3] of byte;
       { Esse é o armazenamento temporário de valores de uma cor }
 BEGIN
   For loop1:=1 to 64 do BEGIN
     WaitRetrace;
     For loop2:=0 to 255 do BEGIN
       Getpal (loop2,Tmp[1],Tmp[2],Tmp[3]);
       If Tmp[1]>0 then dec (Tmp[1]);
       If Tmp[2]>0 then dec (Tmp[2]);
       If Tmp[3]>0 then dec (Tmp[3]);
         { Se os valores de vermelho, verde e azul da cor loop2 ainda não são zero, decremente uma unidade }
       Pal (loop2,Tmp[1],Tmp[2],Tmp[3]);
         { seta a nova, e alterada, paleta de cores }
     END;
   END;
 END; 
[C++]
 void FadeDown() { 
 // Esse é o armazenamento temporário de valores de uma cor
 unsigned char Tmp[3]; 
 for(int loop1=0; loop1<64; loop1++) { 
  WaitRetrace();
  for(int loop2=0; loop2<256; loop2++) {
   GetPal(loop2,Tmp[0],Tmp[1],Tmp[2]);
 // Se os valores de vermelho, verde e azul da cor loop2 ainda não
   // são zero, decremente uma unidade 
   if (Tmp[0] > 0) Tmp[0]--;
   if (Tmp[1] > 0) Tmp[1]--;
   if (Tmp[2] > 0) Tmp[2]--;
 // seta a nova, e alterada, paleta de cores
   Pal(loop2,Tmp[0],Tmp[1],Tmp[2]);
 }
 }
}

Novamente, para dimuir a velocidade das coisas, ponha um delay acima do WaitRetrace. Fazer um Fade Ou na tela parece muito mais impressionante do que apenas limpar a tela; pode fazer um mundo de diferença na impressão que o seu demo, etc, vai deixar nas pessoas que estão vendo. Para restaurar a paleta, apenas faça isso:

[Pascal]
 Procedure RestorePallette;
 VAR loop1:integer;
 BEGIN
   WaitRetrace;
   For loop1:=0 to 255 do
     pal (loop1,Pall[loop1,1],Pall[loop1,2],Pall[loop1,3]);
 END; 
[C++]
 void RestorePallette() { 
 WaitRetrace();
   for(int loop1=0; loop1<255; loop1++)
     Pal(loop1,Pall2[loop1][0],Pall2[loop1][1],Pall2[loop1][2]); 
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Fechando

Bem, lá está a maioria daquelas perguntas originais repondida 😉 O programa de amostra que se segue é bem grande, então deve levar um bom tempo pra você se divertir com ele. Persevere e você se supera. A manipulação da Pallette tem sido um espinho para muitos programadores por algum tempo, ainda assim, espero que eu tenha mostrado a vocês todos o qual espantosamente fácil ela é, uma vez que você pega o básico.

Eu preciso de mais feedback! Em que direção você gostaria que eu fosse? Há alguma seção particular que você gostaria de mais informação? Também, mande-me seus demos, por mais triviais que eles possam parecer. Nós realmente queremos entrar em contato com, ou ajudar, os novos e velhos programadores, mas você tem que nos deixar uma mensagem dizendo sobre você mesmo e o que você tem feito ou quer fazer.

TEM ALGUÉM AÍ FORA?

P.S. Nosso novo demo deve sair em breve… está ficando tão BOMMM… fique de olho nele.

      [ E então ela veio até ele, caiu sobre seu teclado mais uma 
    vez. 'São três da manhã' ela sussurrou. 'Vamos te levar
    pra cama'. Ele se mexeu, seu rosto banhado da luz embotada
    de seu monitor. Ele resmunga qualquer coisa. Quando 
    ela se abaixa por cima dele pra desligar a energia, ela
    pergunta: 'Vale a pena?'. A resposta dele a surpreende.
    'Não' ele diz. Em sua neblina de cafeína, ele ri.
    'Mas é com certeza um grande modo de relaxar.'      ]

                                       - Grant Smith
                                      Terça, 13 de Julho de 1993
                                           02:23 da manhã...

Vejo você na próxima semana!

– Denthor
– Krull

A Nova Krull's HomePage