//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "GLWektory.h"
#include "Teksturowanie.h"
#include "GLNapisy.h"

#include "glext.h"

const bool ShowDebug=true;
void __fastcall ShowDebugInfo(AnsiString tekst)
{
    if (ShowDebug) ShowMessage(tekst);
}

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TGLForm(Owner)
{
   ObslugaMyszy=true;
   KolorTla=clBlack;
   debug_mode=false;
   Mgla=false;
   NieruchomyPokoj=false;
   NieruchomyPokoj_Kolor=clBlue;
   NatezenieSwiatlaOtoczenia=0.5f;
   SwobodneObroty=true;
   SwobodneObrotyWygaszanie=0.0;

   Obracaj(10.0f,1.0f,0.0f);

   //teksturowanie
   //glEnable(GL_TEXTURE_2D);
   Teksturowanie=false;

   AnsiString nazwyPlikow[TEKSTURY_ILOSC]={"tekstura1.bmp","tekstura2.bmp","tekstura3.bmp"};

   glGenTextures(TEKSTURY_ILOSC,Tekstury); //tworzenie tablicy tekstur
	for(int i=0;i<TEKSTURY_ILOSC;i++)
	{
   	int TeksturaSzer,TeksturaWys;
	   unsigned long* Tekstura=WczytajTeksture(nazwyPlikow[i],TeksturaSzer,TeksturaWys);
	   if (Tekstura!=NULL)
	   {
         glBindTexture(GL_TEXTURE_2D,Tekstury[i]);
		   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		   gluBuild2DMipmaps(GL_TEXTURE_2D,3,TeksturaSzer,TeksturaWys,GL_RGBA,GL_UNSIGNED_BYTE,Tekstura);
		   delete[] Tekstura; //oryginalne dane sa usuwane
	   }
   }

   //czcionka 3D
   //czcionkaTimes3D=StworzCzcionke3D(Handle,"Times New Roman CE",false,true);

   Caption=(AnsiString)"GLForm, OpenGL "+(char*)glGetString(GL_VERSION)+",   F1 - Pomoc";

   //Rozszerzenia
   ShowDebugInfo("Testowane z wersjami:\n2.0.3 (NVIDIA GeForce 6600, sterownik 9.1.3.1, Windows XP)\n2.1.1 (NVIDIA GeForce 6600 GT, 6.14.11.6218, Windows XP)");
   //Od wersji OpenGL zaleza nazwy funkcji (ew. suffixy ARB) - aliasy z ARB sa czasem zachowywane

   char* rozszerzenia=(char*)malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1);
   strcpy (rozszerzenia,(char *)glGetString(GL_EXTENSIONS));

   //sprawdzenie czy rozszerzenie jest dostepne
   bool isShaderAvailable=strstr(rozszerzenia,"GL_ARB_shader_objects")>0;
   ShowDebugInfo((AnsiString)"Shadery "+(isShaderAvailable?"":"nie ")+"są obsługiwane");

   ShowDebugInfo((AnsiString)"Shading Language: "+(char*)glGetString(GL_SHADING_LANGUAGE_VERSION));

   TStringList* tokeny=new TStringList();
   char* token=strtok(rozszerzenia," ");
   while(token!=NULL)
   {
       tokeny->Add(token);
       token=strtok(NULL," ");
   }
   //Caption=(AnsiString)"Ilość rozszerzeń: "+tokeny->Count;
   ListBox1->Items=tokeny;
   if (!ShowDebug) ListBox1->Visible=false;

   if (isShaderAvailable) Shader();
}

//---------------------------------------------------------------------------

void __fastcall TForm1::RysujScene()
{
   //ponizsze jest w glext.h
   //int GL_LIGHT_MODEL_COLOR_CONTROL=0x81F8,
   //GL_SINGLE_COLOR=0x81F9,GL_COLOR_SIMPLE=0x81F9,
   //GL_SEPARATE_SPECULAR_COLOR=0x81FA;

   const float wsp_odbicia_szklo[4]={1.0,1.0,1.0,1.0};
   const float wsp_odbicia_matowy[4]={0.0,0.0,0.0,1.0};

   glMaterialfv(GL_FRONT,GL_SPECULAR,wsp_odbicia_szklo);
   glMateriali(GL_FRONT,GL_SHININESS,100);
   glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR); //konieczne, aby tekstury nie psuly

   glPushMatrix(); //zapamietaj macierz model-widok (wloz na stos macierzy)
   glColor4ub(255,255,255,255);
   RysujSzescian(1.0f);

   /*
   glPopMatrix(); //zdejmij ze stosu macierzy = odtworz zapamietany stan
   glBindTexture(GL_TEXTURE_2D,NULL);
   glTranslatef(-4.0f,0.0f,0.0f);
   glColor3f(0.0f,1.0f,0.0f);
   Teksturowanie=false;
   Pisz((AnsiString)"GLForm, OpenGL "+(char*)glGetString(GL_VERSION),czcionkaTimes3D);
   Teksturowanie=true;
   */

   glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SINGLE_COLOR);
   glMaterialfv(GL_FRONT,GL_SPECULAR,wsp_odbicia_matowy);
   glMateriali(GL_FRONT,GL_SHININESS,0);
}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
   //obroty
   if (Shift.Empty())
   {
      if (Key>='0' && Key<='7')
      {
         GLenum swiatlo=GL_LIGHT0;
         switch (Key)
         {
            case '1': swiatlo=GL_LIGHT1; break;
            case '2': swiatlo=GL_LIGHT2; break;
            case '3': swiatlo=GL_LIGHT3; break;
            case '4': swiatlo=GL_LIGHT4; break;
            case '5': swiatlo=GL_LIGHT5; break;
            case '6': swiatlo=GL_LIGHT6; break;
            case '7': swiatlo=GL_LIGHT7; break;
            default: swiatlo=GL_LIGHT0;
         }
         if (glIsEnabled(swiatlo)) glDisable(swiatlo);
         else glEnable(swiatlo);
      }

      switch (Key)
      {
         case 'b':
         case 'B':
            if (glIsEnabled(GL_BLEND)) glDisable(GL_BLEND);
            else glEnable(GL_BLEND);
            break;
         case 'a':
         case 'A':
            if (glIsEnabled(GL_POINT_SMOOTH))
            {
               glDisable(GL_POINT_SMOOTH);
               glDisable(GL_LINE_SMOOTH);
               glDisable(GL_POLYGON_SMOOTH);
            }
            else
            {
               glEnable(GL_POINT_SMOOTH);
               glEnable(GL_LINE_SMOOTH);
               glEnable(GL_POLYGON_SMOOTH);
            }
            break;

         case 'm':
         case 'M':
            Mgla=!Mgla;
            break;

         case 'n':
         case 'N':
            NieruchomyPokoj=!NieruchomyPokoj;
            break;

         case 't':
         case 'T':
            Teksturowanie=!Teksturowanie;
            break;


         case VK_OEM_MINUS:
            NatezenieSwiatlaOtoczenia-=0.01;
            break;
         case VK_OEM_PLUS:
         case '=':
            NatezenieSwiatlaOtoczenia+=0.01;
            break;
      }
   }

   GL_RysujScene();
}
//---------------------------------------------------------------------------


void __fastcall TForm1::RysujSzescian(float krawedz) const
{
	const float a=krawedz;

    glColor3ub(255,0,0);
   glBindTexture(GL_TEXTURE_2D,Tekstury[0]);
	glBegin(GL_QUADS);
	//tylnia
   glNormal3f(0,0,-1); //wektory normalne skierowane na zewnatrz
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-a,-a,-a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-a,a,-a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(a,a,-a);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(a,-a,-a);
	//przednia
   glNormal3f(0,0,1);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-a,-a,a);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(a,-a,a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(a,a,a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-a,a,a);
   glEnd();

   glColor3ub(0,255,0);
   glBindTexture(GL_TEXTURE_2D,Tekstury[1]);
	glBegin(GL_QUADS);
	//prawa
   glNormal3f(1,0,0);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(a,-a,a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(a,a,a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(a,a,-a);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(a,-a,-a);
	//lewa
   glNormal3f(-1,0,0);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-a,-a,a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-a,a,a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-a,a,-a);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-a,-a,-a);
   glEnd();

   glColor3ub(0,0,255);
   glBindTexture(GL_TEXTURE_2D,Tekstury[2]);
	glBegin(GL_QUADS);
	//gorna
   glNormal3f(0,1,0);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-a,a,a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(a,a,a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(a,a,-a);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-a,a,-a);
	//dolna
   glNormal3f(0,-1,0);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-a,-a,a);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(a,-a,a);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(a,-a,-a);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-a,-a,-a);
	glEnd();

   glBindTexture(GL_TEXTURE_2D,NULL);
}

void __fastcall TForm1::Oswietlenie()
{
   MlecznaZarowka();
   //ZoltaIZielonaMleczneZarowki();
   Reflektor();
}

void __fastcall TForm1::MlecznaZarowka()
{
   const float kolor1_rozproszone[]={0.5,0.5,0.5,1.0};
   glLightfv(GL_LIGHT1,GL_DIFFUSE,kolor1_rozproszone);
   glEnable(GL_LIGHT1);
}

void __fastcall TForm1::ZoltaIZielonaMleczneZarowki()
{
   //zolta mleczna zarowka
	const float kolor_rozproszone_zolta[4]={1.0f,1.0f,0.0f,1.0f};
	const float pozycja_zolta[4]={-2.0f,0.0f,1.0f,1.0f};

	glLightfv(GL_LIGHT2,GL_POSITION,pozycja_zolta);
	glLightfv(GL_LIGHT2,GL_DIFFUSE,kolor_rozproszone_zolta);
	glEnable(GL_LIGHT2);

   //zielona mleczna zarowka
	const float kolor_rozproszone_zielony[4]={0.0f,1.0f,0.0f,1.0f};
	const float pozycja_zielony[4]={2.0f,0.0f,1.0f,1.0f};

	glLightfv(GL_LIGHT3,GL_POSITION,pozycja_zielony);
	glLightfv(GL_LIGHT3,GL_DIFFUSE,kolor_rozproszone_zielony);
	glEnable(GL_LIGHT3);
}

void __fastcall TForm1::Reflektor()
{
   const float kolor_rozproszone[4]={0.3,0.3,0.3,1.0};
   const float kolor_reflektora[4]={1.0,1.0,1.0,1.0};
   const float pozycja[4]={0.0,-10.0,10.0,1.0};
   //const float pozycja[4]={2.0f,2.0f,-2.0f,1.0f};
   const szerokosc_wiazki=60.0; //w stopniach
   glLightfv(GL_LIGHT4,GL_POSITION,pozycja);
   glLightfv(GL_LIGHT4,GL_DIFFUSE,kolor_rozproszone);
   glLightfv(GL_LIGHT4,GL_SPECULAR,kolor_reflektora);
   glLightf(GL_LIGHT4,GL_SPOT_CUTOFF,szerokosc_wiazki);
   glEnable(GL_LIGHT4);
}

void __fastcall TForm1::Pomoc()
{
	AnsiString s=(AnsiString)"GLForm 1.1\n(c) Jacek Matulewski 2006-2007\n\n"+
			"OpenGL, wersja "+(char*)glGetString(GL_VERSION)+"\n"+
			"GLU, wersja "+(char*)gluGetString(GLU_VERSION);
   s+="\n\n+/- - Jasność światła otoczenia\nstrzałki - ruch obiektu\n\nQ - Animacja\nB - Alpha blending\nA - Antyaliasing\nM - Mgła\nN - Nieruchomy pokój\nT - Teksturowanie";
	MessageBox(Handle,s.c_str(),"TGLForm",MB_OK | MB_ICONINFORMATION);
}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
   glDeleteTextures(TEKSTURY_ILOSC,Tekstury);
   glDeleteLists(czcionkaTimes3D,256);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Shader()
{
    //deklaracja funkcji wyswietlajacej mniej wazne komunikaty
    void __fastcall ShowDebugInfo(AnsiString tekst);

    //Pobieranie adresow funkcji
    //funkcje obiektow shaderow
    PFNGLCREATESHADERPROC glCreateShader=(PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
    PFNGLDELETESHADERPROC glDeleteShader=(PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
    PFNGLSHADERSOURCEPROC glShaderSource=(PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
    PFNGLCOMPILESHADERPROC glCompileShader=(PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
    //obiekty programow
    PFNGLCREATEPROGRAMPROC glCreateProgram=(PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
    PFNGLLINKPROGRAMPROC glLinkProgram=(PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
    PFNGLUSEPROGRAMPROC glUseProgram=(PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
    //funkcje obiektow (niektore ARB)
    PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB=(PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB");
    PFNGLGETINFOLOGARBPROC glGetInfoLogARB=(PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB");
    PFNGLDELETEOBJECTARBPROC glDeleteObjectARB=(PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB");
    PFNGLATTACHOBJECTARBPROC glAttachObjectARB=(PFNGLATTACHOBJECTARBPROC)wglGetProcAddress("glAttachObjectARB");
    //sprawdzanie czy pobrane adresy
    if (glCreateShader==NULL || glDeleteShader==NULL || glShaderSource==NULL || glCompileShader==NULL ||
        glCreateProgram==NULL || glLinkProgram==NULL || glUseProgram==NULL ||
        glGetObjectParameterivARB==NULL || glGetInfoLogARB==NULL || glDeleteObjectARB==NULL || glAttachObjectARB==NULL)
    {
        ShowMessage("Uwaga! Pobranie adresu funkcji shaderów nie powiodło się");
        return;
    }
    else ShowDebugInfo("Pobranie adresu funkcji shaderów powiodło się");

    /* Vertex shader */
    //Tworzenie obiektu shadera
    GLhandleARB vsh=glCreateShader(GL_VERTEX_SHADER);
    if(vsh!=NULL) ShowDebugInfo("Utworzenie obiektu shadera powiodło się");
    else
    {
        ShowMessage("Uwaga! Utworzenie obiektu shadera nie powiodło się");
        return;
    }

    //Czytanie kodu shadera z pliku
    TStringList* kodShadera=new TStringList();
    try
    {
        kodShadera->LoadFromFile("shader.vsh");
    }
    catch(...)
    {
        ShowMessage("Nie można wczytać kodu shadera z pliku shader.vsh");
    }
    /*if (CzytajPlikTxt("VertexShader1.txt",kodShadera)==NULL)
        ShowMessage("Uwaga! Wczytanie kodu shadera z pliku nie powiodło się");*/
    ShowDebugInfo("Kod vertex shadera:\n\n"+kodShadera->Text);

    /*
    char* CzytajPlikTxt(char* nazwaPliku,int rozmiarBufora,char* bufor);
    char kod[1024];
    CzytajPlikTxt("shader.vsh",1024,kod);
    ShowDebugInfo(kod);
    */

    //Dostarczanie zrodla do obiektu shadera
    const GLchar* zrodlo[1];
    zrodlo[0]=kodShadera->GetText();
    //zrodlo[0]=kodShadera->Text.c_str();
    glShaderSource(vsh,1,zrodlo,NULL);

    //Kompilacja kodu shadera
    glCompileShader(vsh);

    //Weryfikacja kompilacji
    GLint powodzenie;
    glGetObjectParameterivARB(vsh,GL_OBJECT_COMPILE_STATUS_ARB,&powodzenie);
    if (powodzenie) ShowDebugInfo("Kompilacja shadera powiodła się");
    else
    {
        const int maxInfoLogSize=2048;
        GLbyte infoLog[maxInfoLogSize];
        glGetInfoLogARB(vsh,maxInfoLogSize,NULL,infoLog);
        ShowMessage((AnsiString)"Uwaga! Kompilacja shadera nie powiodła się:\n"+(char*)infoLog);
    }

    /* Pixel/Fragment Shader */
    GLhandleARB psh=glCreateShader(GL_FRAGMENT_SHADER);
    kodShadera->Clear();
    try
    {
        kodShadera->LoadFromFile("shader.psh");
    }
    catch(...)
    {
        ShowMessage("Nie można wczytać kodu shadera z pliku shader.psh");
    }
    ShowDebugInfo("Kod pixel shadera:\n\n"+kodShadera->Text);
    zrodlo[0]=kodShadera->GetText();
    glShaderSource(psh,1,zrodlo,NULL);
    glCompileShader(psh);
    glGetObjectParameterivARB(psh,GL_OBJECT_COMPILE_STATUS_ARB,&powodzenie);
    if (powodzenie) ShowDebugInfo("Kompilacja pixel shadera powiodła się");
    else ShowMessage("Uwaga! Kompilacja pixel nie shadera powiodła się");

    /* Obiekt programu */
    //tworzenie obiektu programu
    GLhandleARB program=glCreateProgram();
    //przylaczanie shaderow
    glAttachObjectARB(program,vsh); //odlaczanie: glDetachObjectARB
    glAttachObjectARB(program,psh);
    //linkowanie (konsolidacja) programu
    glLinkProgram(program);
    glGetObjectParameterivARB(program,GL_OBJECT_LINK_STATUS_ARB,&powodzenie);
    if (powodzenie) ShowDebugInfo("Linkowanie programu powiodło się");
    else
    {
        const int maxInfoLogSize=2048;
        GLbyte infoLog[maxInfoLogSize];
        glGetInfoLogARB(program,maxInfoLogSize,NULL,infoLog);
        ShowMessage((AnsiString)"Uwaga! Linkowanie programu nie powiodło się:\n"+(char*)infoLog);
    }
    //walidacja programu z uwglednieniem aktualnego stanu aplikacji i biblioteki OpenGL
    glGetObjectParameterivARB(program,GL_OBJECT_VALIDATE_STATUS_ARB,&powodzenie);
    if (powodzenie) ShowDebugInfo("Walidacja programu powiodła się");
    else
    {
        const int maxInfoLogSize=2048;
        GLbyte infoLog[maxInfoLogSize];
        glGetInfoLogARB(program,maxInfoLogSize,NULL,infoLog);
        ShowMessage((AnsiString)"Uwaga! Walidacja programu nie powiodła się:\n"+(char*)infoLog);
    }
    //uzycie programu
    glUseProgram(program);

    //Usuwanie obiektow
    //usuniecie obiektu shadera nie zwolni wiazania, obiekt zostanie usuniety gdy przestanie byc uzywany
    glDeleteShader(vsh);
    glDeleteShader(psh);
    glDeleteObjectARB(program);
}

#include <fstream.h> //ifstream

char* CzytajPlikTxt(char* nazwaPliku,int rozmiarBufora,char* bufor)
{
    strcpy(bufor,"");
    int dlugosc_tekstu=0;
    char linia[1024]="";
    ifstream plik_we(nazwaPliku);
    if (plik_we==NULL) return NULL;
    while (plik_we)
  	{
        plik_we.getline(linia,1024);
        int dlugosc_linii=strlen(linia);
        if (rozmiarBufora>(dlugosc_tekstu+dlugosc_linii))
        {
            strcat(bufor,linia);
            strcat(bufor,"\n");
            dlugosc_tekstu+=dlugosc_linii;
        }
        else break;
    }
    plik_we.close();
    return bufor;
}


