#include        "3du.h"
#pragma         hdrstop
//              ==========================================================
//              ВНИМАНИЕ !!! Никаких проверок не выполняется !!!
//              ==========================================================

//              Создание новой экструзионной сетки
DECLSPEC        surfNew(struct surface_t *surf,int k,int l)
{
  int cksize;
  surf->k=k;
  surf->l=l;
  surf->nvertices=k*l;
  // Память распределяем один раз под все массивы - так быстрее
  cksize=2*surf->nvertices*sizeof(vector_t)+   // вершины и нормали
    (k-1)*(l-1)*sizeof(vector_t)+              // нормали к граням
    surf->nvertices*sizeof(rgb_t)+            // цвета
    surf->nvertices*sizeof(texcoord_t);         // координаты текстуры
  surf->vertex=(vector_t*)malloc(cksize);
  memZero(surf->vertex,cksize);
  surf->normal=&surf->vertex[surf->nvertices];
  surf->facenormal=&surf->normal[surf->nvertices];
  surf->color=(rgb_t*)&surf->facenormal[(k-1)*(l-1)];
  surf->texcoord=(texcoord_t*)&surf->color[surf->nvertices];
  // Не добавлено не одной вершины
  surf->__nextvtx=0;
  surf->__validfields=0;
  surf->__drawfields=0;
}

//              Уничтожение сетки и освобождение памяти
DECLSPEC        surfDelete(struct surface_t *surf)
{
  free(surf->vertex);
}

//              Добавление одной вершины
DECLSPEC        surfAddVtx(struct surface_t *surf,vector_t *vtx,vector_t *nrm,
  rgb_t *clr,texcoord_t *txc)
{
  // Если исчерпана память, начинаем затирать первые вершины
  if(surf->__nextvtx>=surf->nvertices) surf->__nextvtx=0;
  if(vtx) VTCOPY(surf->vertex[surf->__nextvtx],(*vtx));
  if(nrm) VTCOPY(surf->normal[surf->__nextvtx],(*nrm));
  if(clr) RGBCOPY(surf->color[surf->__nextvtx],(*clr));
  if(txc) TEXCOPY(surf->texcoord[surf->__nextvtx],(*txc));
  surf->__nextvtx++;
}

//              Расчет нормалей к граням
DECLSPEC        surfGenerateFaceNorms(struct surface_t *surf)
{
  int i,j;
  int k,l;
  int count;
  vector_t v1,v2;
  l=surf->l;
  k=surf->k;
  count=0;                // Счетчик граней
  if(surf->__validfields&SURF_FACENORMALS) return;
  for(i=0;i<(k-1);i++)
    for(j=0;j<(l-1);j++)
    {
      // Текущая грань - в i-м столбце и j-й строке. Она опирается на
      // вершины из i и i+1 - й строк и j и j+1 -го столбцов; смещение
      // вершины = l * стр + столб
      VTSUB(v1,surf->vertex[l*i+j],surf->vertex[l*i+j+1]);
      VTSUB(v2,surf->vertex[l*(i+1)+j+1],surf->vertex[l*i+j+1]);
      VTCROSS(surf->facenormal[count],v1,v2);
      ++count;
    }
  surf->__validfields|=SURF_FACENORMALS;
  surfNormalizeFNorms(surf);
}

//              Расчет нормалей к вершинам
DECLSPEC        surfGenerateNorms(struct surface_t *surf)
{
  int i,j;
  int k,l;
  int count;
  int foff, voff;
  vector_t v1,v2;
  l=surf->l;
  k=surf->k;
  if(surf->__validfields&SURF_NORMALS) return;
  // Рассчитываем нормали к граням
  surfGenerateFaceNorms(surf);
  // Четыре угловые вершины
  VTCOPY(surf->normal[0],surf->facenormal[0]);
  VTCOPY(surf->normal[l-1],surf->facenormal[l-2]);
  VTCOPY(surf->normal[l*(k-1)],surf->facenormal[(l-1)*(k-2)]);
  VTCOPY(surf->normal[k*l-1],surf->facenormal[(l-1)*(k-1)-1]);
  // Четыре серии боковых вершин. Верхняя сторона:
  for(i=1;i<(l-1);i++)
  {
    VTADD(surf->normal[i],surf->facenormal[i-1],surf->facenormal[i]);
  }
  // Нижняя сторона:
  count=(k-2)*(l-1);
  for(i=(k-1)*l+1;i<(k*l-1);i++)
  {
    VTADD(surf->normal[i],surf->facenormal[count++],surf->facenormal[count]);
  }
  // Левая сторона:
  count=0;
  for(i=l;i<((k-1)*l);i+=l)
  {
    VTADD(surf->normal[i],surf->facenormal[count],
      surf->facenormal[count+=(l-1)]);
  }
  // Правая сторона:
  count=l-2;
  for(i=l+k-1;i<(k*l-1);i+=l)
  {
    VTADD(surf->normal[i],surf->facenormal[count],
      surf->facenormal[count+=(l-1)]);
  }
  // Центральные вершины
  foff=l-1;
  voff=l;
  for(i=1;i<(k-1);i++)
  {
    for(j=1;j<(l-1);j++)
    {
      // Вершина в строке i и столбце j соприкасается с гранями из строк
      // i-1; i и столбцов j-1; j.
      // Смещение грани: (l-1) * строка + столбец
      // Смещение вершины: l * строка + столбец
      // Все что можно вычислить заранее - вычисляем заранее; умножение
      // заменяем сложением - см ниже. Я даже в момент написания с трудом
      // понимаю то, что написал :)
      VTADD(v1,surf->facenormal[foff-l+j],surf->facenormal[foff+j-1]);
      VTADD(v2,surf->facenormal[foff-l+1+j],surf->facenormal[foff+j]);
      VTADD(surf->normal[voff+j],v1,v2);
    }
    foff+=l-1;
    voff+=l;
  }
  // Модули нормалей должны быть равны единице:
  surfNormalizeNorms(surf);
  surf->__validfields|=SURF_NORMALS;
  surf->__drawfields|=SURF_NORMALS;
}

//              Нормализация нормалей к граням
DECLSPEC        surfNormalizeFNorms(struct surface_t *surf)
{
  int i,j;
  int count;
  count=0;                // Счетчик граней
  for(i=0;i<(surf->k-1);i++)
    for(j=0;j<(surf->l-1);j++)
    {
      VTNORMALIZE(surf->facenormal[count]);
      ++count;
    }
}

//              Нормализация нормалей к вершинам
DECLSPEC        surfNormalizeNorms(struct surface_t *surf)
{
  int i,j;
  int count;
  count=0;                // Счетчик граней
  for(i=0;i<surf->k;i++)
  {
    for(j=0;j<surf->l;j++)
    {
      VTNORMALIZE(surf->normal[count]);
      ++count;
    }
  }
}

//              Безусловный пересчет нормалей
DECLSPEC        surfCalculateNormals(struct surface_t *surf)
{
  surf->__validfields =0; //&= ^((int)SURF_FACENORMALS);
  surf->__validfields =0; // &= ^((int)SURF_NORMALS);
  surfGenerateNorms(surf);
}

//              Получение обрамляющей коробки
DECLSPEC        surfGetBBox(struct surface_t *surf,
                            vector_t *min, vector_t *max)
{
  int i;
  VTCOPY(*min,surf->vertex[0]);
  VTCOPY(*max,*min);
  for(i=1;i<surf->nvertices;i++)
  {
    if(min->x>surf->vertex[i].x) min->x=surf->vertex[i].x;
    else
      if(max->x<surf->vertex[i].x) max->x=surf->vertex[i].x;
    if(min->y>surf->vertex[i].y) min->y=surf->vertex[i].y;
    else
      if(max->y<surf->vertex[i].y) max->y=surf->vertex[i].y;
    if(min->z>surf->vertex[i].z) min->z=surf->vertex[i].z;
    else
      if(max->z<surf->vertex[i].z) max->z=surf->vertex[i].z;
  }
}

//              Установка обрамляющей коробки
DECLSPEC        surfSetBBox(struct surface_t *surf,
                            vector_t *min, vector_t *max)
{
  int i;
  vector_t mini,maxi,size,sizei;
  vector_t zoom;
  surfGetBBox(surf,&mini,&maxi);
  // Вектор масштабирования
  VTSUB(size,*max,*min);
  VTSUB(sizei,maxi,mini);
  // Масштаб = требуемый размер / начальный размер
  // Если один из размеров нулевой, то оставляем как есть
  for(i=0;i<3;i++)
    if(VTIND(sizei,i)==0)
      return;
  VTZOOMIF(zoom,sizei,size);
  // Последовательность преобразований:
  //   - перенос в начало координат (на -mini)
  //   - масштабирование
  //   - перенос на min
  for(i=0;i<surf->nvertices;i++)
  {
    surf->vertex[i].x=(surf->vertex[i].x-mini.x)*zoom.x+min->x;
    surf->vertex[i].y=(surf->vertex[i].y-mini.y)*zoom.y+min->y;
    surf->vertex[i].z=(surf->vertex[i].z-mini.z)*zoom.z+min->z;
  }
}

//              Перенос (в модельных координатах)
DECLSPEC        surfTranslate(struct surface_t *surf,vector_t *tr)
{
  MESHTRANSLATE(*surf,*tr);
}

//              Масштабирование (в модельных координатах)
DECLSPEC        surfScale(struct surface_t *surf,vector_t *sc)
{
  MESHSCALE(*surf,*sc);
}

