/*

bazenik 3ds viewer

return:
  0 - doslo k chybe
  1 - este nezacal
  2 - prebehol v poriadku
  3 - uz skoncil
*/

#include <iostream>
#include "api3ds.h"
#include "efekt.h"
#include "efekt_14.h"

extern int sync_num;

typedef struct {
   GLfloat x, y, z, w;
   GLfloat u1, v1;
   GLfloat u2, v2;
} bod;

bod body[256];

Texture3DS *obloha;
Texture3DS *dno;

#define spac 40

unsigned char indices[2*16*15];
void init_structures()
{
  bod* b = body;
  for(int i=0; i<16; i++, b+=16) {
    bod* b2 = b;
    for(int j=0; j<16; j++, b2++)
    {
      b2->x = spac*j - (spac*7.5);
      b2->y = (spac*i - (spac*7.5)+ 2.5)*1.005;
      b2->z = 0;
      b2->w = 1.0;
      b2->u1 = b2->v1 = b2->u2 = b2->v2 = 0;
    }
  }
  unsigned char* p = indices;
  for(int i=0; i<15; i++)
    for(int j=0; j<16; j++)
    {
      *p = (unsigned char)((i<<4)+j); p++;
      *p = (unsigned char)(((i+1)<<4)+j); p++;
    }
} 

void vykresli_hladinu()
{
  glDisableClientState(GL_EDGE_FLAG_ARRAY);
  glDisableClientState(GL_INDEX_ARRAY);
  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);

  int stride = sizeof(bod);

  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(4, GL_FLOAT, stride, body);

  glLockArraysEXT(0, 256);

  glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  glEnable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);
  glDisable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_ALWAYS);

  glDisable(GL_BLEND);
  dno->GL();
  glTexCoordPointer(2, GL_FLOAT, stride, ((char*)body)+24);
  glColor3f(1.0,1.0,1.0);
  for(int i=0; i<15; i++)
    glDrawElements(GL_TRIANGLE_STRIP, 32, GL_UNSIGNED_BYTE,
                   (void*)(indices+(i<<5)));

  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE);
  obloha->GL();
  glTexCoordPointer(2, GL_FLOAT, stride, ((char*)body)+16);
  glColor3f(1.0,1.0,1.0);
  for(int i=0; i<15; i++)
    glDrawElements(GL_TRIANGLE_STRIP, 32, GL_UNSIGNED_BYTE,
                   (void*)(indices+(i<<5)));

  glUnlockArraysEXT();

  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);

  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_LIGHTING);
  glEnable(GL_CULL_FACE);
  glDepthFunc(GL_LESS);

}

double mytime = 0.0;

#define fscale 0.35
#define vyska 10.0
#define vyska_hladiny 140.0
#define adjust (vyska*fscale/spac)
#define vysobloha 1500.0
#define oblsp 0.0004
#define vzduch 1.0
#define voda 1.3
#define refr (vzduch/voda)

void updatuj_hladinu(double t, float Ox, float Oy, float Oz)
{
  bod* b = body;
  for(int i=0; i<16; i++, b+=16) {
    bod* b2 = b;
    for(int j=0; j<16; j++, b2++)
    {
      float x=((float)i-7.5)*fscale;
      float y=((float)j-7.5)*fscale;

      float f=200+vyska*(sin(x+7*t)*cos(2*y-9*t) + cos(x+4*t)*cos(y+5*t));
      float u=adjust*(cos(2*y-9*t)*cos(x+7*t) - sin(x+4*t)*cos(y+5*t));
      float v=adjust*(-2*sin(x+7*t)*sin(2*y-9*t) - cos(x+4*t)*sin(y+5*t));

      b2->z = (GLfloat)f-vyska_hladiny;
      float len = 1.0/sqrt(1.0+u*u+v*v);
      float ny = u*len;
      float nz = -len;
      float nx = v*len;

      float lx = b2->x - Ox;
      float ly = b2->y - Oy;
      float lz = b2->z - Oz;
      len = 1.0/sqrt(lx*lx+ly*ly+lz*lz);
      lx*=len; ly*=len; lz*=len;
      float c = -2*(lx*nx+ly*ny+lz*nz);
      float rx = lx + c*nx;
      float ry = ly + c*ny;
      float rz = lz + c*nz;
      float t = (vysobloha-b2->z)/rz;
      u = oblsp*(b2->x + rx*t);
      v = oblsp*(b2->y + ry*t);

      b2->u1 = 0.5*u+0.5;
      b2->v1 = 0.5*v+0.5;

      c = -(lx*nx + ly*ny + lz*nz);
      rx = lx + c*nx;
      ry = ly + c*ny;
      rz = lz + c*nz;
      len = 1.0/sqrt(rx*rx+ry*ry+rz*rz);
      rx*=len; ry*=len; rz*=len;
      float sinbeta = refr*sqrt(1.0-c*c);
      float cosbeta = sqrt(1.0-sinbeta*sinbeta);
      float tx = sinbeta*rx - cosbeta*nx;
      float ty = sinbeta*ry - cosbeta*ny;
      float tz = sinbeta*rz - cosbeta*nz;
      t = (-b2->z)/tz;
      u = 0.0033*(b2->x + tx*t);
      v = 0.0033*(b2->y + ty*t);

      b2->u2 = 0.5*u+0.5;
      b2->v2 = 0.5*v+0.5;
    }
  }
}



int efekt_14::init()
{
  start=TRUE;
  counter=ZACIATOK14*refresh;

  glClearColor (cl_r, cl_g, cl_b, 1.0);
  if (lgt) 
    {
    glEnable(GL_LIGHTING);    
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
    }
   else 
    glDisable(GL_LIGHTING);    

  if (fog) 
    {
    GLfloat fc[4] = {fg_r,fg_g,fg_b, 1.0};
    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE, GL_EXP2);
    glFogfv(GL_FOG_COLOR, fc);
    glFogf(GL_FOG_DENSITY, fgd);
    }
   else glDisable(GL_FOG);    
return 1;
}

int efekt_14::load()
{
   cout << "Loading efekt14 ... ";
   FILE *stream=fopen(CFGNAME14,"r");
   if (stream==NULL) return 0;
   fscanf(stream,"file name            %s\n",scene_name);
   fscanf(stream,"camera name          %s\n",camera_name);
   fscanf(stream,"begin                %f\n",&begin3d);
   fscanf(stream,"end                  %f\n",&end3d);
   fscanf(stream,"background color     %f,%f,%f\n",&cl_r,&cl_g,&cl_b);
   fscanf(stream,"lightning            %i\n",&lgt);
   fscanf(stream,"fog                  %i\n",&fog);
   fscanf(stream,"fog color            %f,%f,%f\n",&fg_r,&fg_g,&fg_b);
   fscanf(stream,"fog density          %f\n",&fgd);
   fscanf(stream,"visibility           %f\n",&vis);
   fscanf(stream,"minvisibility        %f\n",&minvis);
   fscanf(stream,"hfov,vfov (PI/x)     %f,%f\n",&hfov,&vfov);
   fscanf(stream,"render               %i\n",&render);
   fscanf(stream,"rays                 %i\n",&rays);
   fscanf(stream,"outline              %i\n",&obrysy);
   fscanf(stream,"outline color        %f,%f,%f\n",&ob_r,&ob_g,&ob_b);
   fscanf(stream,"outline width        %i\n",&ob_wdt);
   fscanf(stream,"object name          %s\n",object_name);
   fclose(stream);

   Loader3DS loader;
   sce = loader.Load(scene_name,texture_library);
   if (sce==NULL) return 0;

   obloha=texture_library->GetOrCreate("OBLOHA.JPG");
   dno=texture_library->GetOrCreate("DNO.JPG");

   init_structures();

   cout << "ok!"<<endl;
return 1;
}

int efekt_14::free()
{
return 1;
}

int efekt_14::update()
{
mytime+=1.0/600.0;
return 1;
}

int efekt_14::go(double t)
{
if (t<ZACIATOK14) return 1;
if (t<begin3d) return 1;
if (end) return 3;
if (counter>=KONIEC14*refresh) return free();

if (!start) if (!init()) return 0;

int cur_frm=(int)(t*refresh);
if (cur_frm>KONIEC14*refresh) cur_frm=int(KONIEC14*refresh);
if (cur_frm>counter)
  while (counter<cur_frm)
    {
    counter++;
    if (counter<KONIEC14*refresh) update();
    }
if (counter>=KONIEC14*refresh) return free();

//tu sa kresli->

  if (lgt) 
    {
    glEnable(GL_LIGHTING);    
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
    }
   else 
    glDisable(GL_LIGHTING);    

  if (fog) 
    {
    GLfloat fc[4] = {fg_r,fg_g,fg_b, 1.0};
    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE, GL_EXP2);
    glFogfv(GL_FOG_COLOR, fc);
    glFogf(GL_FOG_DENSITY, fgd);
    }
   else glDisable(GL_FOG);    



  float frejm=(t-begin3d)/(end3d-begin3d);
  kam = sce->GetCamera(camera_name);
  kam->FarClipplane(vis);
  kam->NearClipplane(minvis);
  kam->HorizontalFOV(PI/hfov);
  kam->VerticalFOV(PI/vfov);
  kam->GL(frejm);

  Vector3f c=kam->Origin(frejm);
  updatuj_hladinu(4*mytime, c.x, c.y, c.z);
  vykresli_hladinu();

  if (render) {sce->Render(frejm);}

  if (obrysy||rays)
    {
    Object3DS* object = dynamic_cast<Object3DS*>(sce->GetObject(object_name));
    if(object)
      {
      Vector3f S( 0.0, 200.0, 0.0);
      Vector3f C=kam->Origin(frejm);
      if (rays)   object->Rays(C, S, 420, GL_ONE, GL_ONE);
      if (obrysy) object->Outline (C, 8, 1.0,1.0,1.0);
      }
    }
//<-
return 2;
}
