/*  image.c */


/*
 * Vis5D system for visualizing five dimensional gridded data sets.
 * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
 * Dave Santek, and Andre Battaiola.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * As a special exception to the terms of the GNU General Public
 * License, you are permitted to link Vis5D with (and distribute the
 * resulting source and executables) the LUI library (copyright by
 * Stellar Computer Inc. and licensed for distribution with Vis5D),
 * the McIDAS library, and/or the NetCDF library, where those
 * libraries are governed by the terms of their own licenses.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../config.h"
/* Texture/image mapping */



#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_SGI_GL
#  include <X11/Xlib.h>
#  include <gl/gl.h>
#endif
#ifdef HAVE_OPENGL
#  include <GL/gl.h>
#  include <GL/glu.h>
#endif
#include "globals.h"
#include "graphics.h"
#include "rgb.h"



#ifdef HAVE_SGI_GL
/*
 * Lighting and texturing properties:
 */
static float mat2[] = {
   AMBIENT, 0.5, 0.5, 0.5,   /* surface ambient color */
   DIFFUSE, 0.9, 0.9, 0.9,   /* surface diffuse color */
   LMNULL
};
static float lmodel2[] = {
   AMBIENT, 0.5, 0.5, 0.5,         /* ambient color */
   LOCALVIEWER, 0.0,               /* infinite viewpoint */
   TWOSIDE, 0,
   LMNULL
};
static float texprops[] = { TX_MINFILTER, TX_POINT,
                            TX_MAGFILTER, TX_POINT,
                            TX_NULL };
static float tevprops[] = { TV_MODULATE, TV_NULL };
static float subdiv_params[] = { 0.0, 0.0, 10.0 };
#endif /* HAVE_SGI_GL */



#ifdef HAVE_MCIDAS
/*
 * McIDAS stuff
 */
/* API - not to globals.c:
   only one call to init_mcidas_kludge for all Vis5D contexts */
int uc[1000];
int neguc[200];
int ttyfd[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

#define OPNARA F77_FUNC(opnara,OPNARA)
#define READD F77_FUNC(readd,READD)
#define RHELP F77_FUNC(rhelp,RHELP)
#define CLSARA F77_FUNC(clsara,CLSARA)
#define REDARA F77_FUNC(redara,REDARA)
#define KLTWIN F77_FUNC(kltwin,KLTWIN)
#define LWINIT F77_FUNC(lwinit,LWINIT)

extern void OPNARA( int * );
extern void READD( int *, int * );
extern void RHELP( int *, int * );
extern void CLSARA( int * );
extern void REDARA( int *, int*, int *, int *, int *, void *);

#ifndef MCIDAS_SIDECAR
extern void KLTWIN( void );
extern void LWINIT( void );

static void init_mcidas_kludge( void ) /* API - call once for all contexts */
{
  int i;

  for (i=0; i<1000; i++) uc[i] = 0;
  for (i=0; i<200; i++) neguc[i] = 0;
  KLTWIN();
  LWINIT();
}
#endif /* end of ifndef MCIDAS_SIDECAR */


void F77_FUNC(zdest,ZDEST)( char *c, int *n, int len )
{
  int l;
  char s[100];

  l = (len < 100) ? len : 99;
  strncpy(s, c, l);
  s[l] = 0;
  printf("%s %d\n", s, *n);
}



/*
 * Load a mcidas area and store it in packed ABGR format.
 * Input:  area_num - number of mcidas area
 * Output:  width, height - size of image read
 *          data - pointer to image data
 * Return:  1 = ok, 0 = error.
 */
static int read_area( Display_Context dtx, int area_num,
                      int *width, int *height, unsigned char **data )
{
   int idir[64], bytes, e, band;
   unsigned char data1[80000];
   int x, y;
   int h, w;
   unsigned char *d;
   int ix;

#ifdef NAV
  float dx, dy;
  int qr, qc, i, j, status;
  float x, y, z;
  float lat, lon, hgt;
  float xline, xelem, xdum, aline, aelem, lpct, epct;
  static float zero = 0.0;
  int line, elem, lres, eres;

  /* get area navigation */
  if ((status = F77_FUNC(nvset,NVSET)("AREA        ", &area_num, 12)) != 0) {
    if (status == -1) printf("no area %d\n", area_num);
    else if (status == -2) printf("gen_nav: no navigation for area %d\n", area_num);
    else printf("gen_nav: navigation problem for area %d %d\n", area_num, status);
    return 0; /* **** or just don't navigate area **** */
  }
#endif

   /*printf("area_num %d\n", area_num);*/
   READD(&area_num, idir);
   if (idir[0] < 0) {
     /* unable to read area */
     printf("Error:  couldn't read AREA%04d, no areas read.\n",area_num);
     return 0;
   }

   OPNARA(&area_num);

#ifdef NAV
  line = idir[5] + line * idir[11]; /* 5 - area line, 11 - area line res */
  elem = idir[6] + elem * idir[12]; /* 6 - area elem, 12 - area elem res */

  if (lres == 0) {
    printf("gen_nav: bad line res %d\n", lres);
    return 0;
  }
  else if (lres > 0) lres = idir[11] * lres;
  else lres = idir[11] / (-lres);

  if (eres == 0) {
    printf("gen_nav: bad elem res %d\n", eres);
    return 0;
  }
  else if (eres > 0) eres = idir[12] * eres;
  else eres = idir[12] / (-eres);
#endif

   *height = h = idir[8];
/*   if (h > 768) *height = h = 768; */
   *width = w = idir[9];
   bytes = idir[10];
   d = (unsigned char *) malloc( w * h * sizeof(unsigned char));
   *data = d;

   e = 0;
   band = (bytes == 1) ? 0 : 8;
   /*printf("h %d w %d bytes %d band %d\n", h, w, bytes, band);*/

   /* new area stuff */
   bytes = 1;
   RHELP(&area_num, &bytes);

#ifdef NAV
  qr = dtx->qrows;
  qc = dtx->qcols;
  dx = (dtx->Xmax-dtx->Xmin) / (float) (qc-1);
  dy = (dtx->Ymax-dtx->Ymin) / (float) (qr-1);

  y = dtx->Ymax;
  for (i=0; i<qr; i++) {
    x = dtx->Xmin;
    for (j=0; j<qc; j++) {
      xyzPRIME_to_geo( dtx, -1, -1, x, y, 0.0, &lat, &lon, &hgt );

      if(F77_FUNC(nv1eas,NVLEAS)(&lat, &lon, &zero, &xline, &xelem, &xdum) == 0) {
        aline = (xline - line) / lres;
        aelem = (xelem - elem) / eres;
        GET min and max line and elem
        USE to calculate area sector
        TRANSFORM area coordinates to 0.0 to 1.0 coordinates
      }
      else {

      }
      x += dx;
    }
    y -= dy;
  }
#endif

   ix = 0;
   for (y=0;y<h;y++) {
      /* int yy = (h-y-1) * w; */
      if (bytes == 1) {
         REDARA(&area_num, &y, &e, width, &band, data1);
         for (x=0; x<w; x++) {
            d[ix++] = data1[x];
         }
      }
      else {
#ifdef LEAVEOUT
         REDARA(&area_num, &y, &e, width, &band, data2);
         for (x=0; x<w; x+=3) {
            d[ix++] = data2[x] / 2;
            d[ix++] = data2[x+1] / 2;
            d[ix++] = data2[x+2] / 2;
            d[ix++] = 0;;
         }
#endif
      }
   }

   CLSARA(&area_num);
   return 1;
}

#endif /* ifdef HAVE_MCIDAS */



/*
 * Find the value nearest to n which is also a power of two.
 */
static int round2( int n )
{
   int m;

   for (m=1; m<n; m*=2)
     ;

   /* m>=n */
   if (m-n <= n-m/2) {
      return m;
   }
   else {
      return m/2;
   }
}



/**********************************************************************/
/**********************************************************************/
/*****                    Public Functions                        *****/
/**********************************************************************/
/**********************************************************************/



/*
 * Init global variables.
 */
void init_image( Display_Context dtx )
{
   int i;

   for (i=0;i<dtx->NumTimes;i++) {
      dtx->TexWidth[i] = dtx->TexHeight[i] = 0;
      dtx->TexComponents[i] = 0;
      free(dtx->TexImage[i]);
      dtx->TexImage[i] = NULL;
      dtx->TexImageNew[i] = 1; 
   }
}



/*
 * Define the texture to be used for the given timestep.  The image data
 * is not copied; just the pointer to it is saved.
 */
void define_texture( Display_Context dtx, int time, int width, int height,
                     int components, void *image )
{
   assert( time>=0 && time<=dtx->NumTimes );

   dtx->TexWidth[time] = width;
   dtx->TexHeight[time] = height;
   dtx->TexComponents[time] = components;
   if (dtx->TexImage[time]){
      free(dtx->TexImage[time]);
   }
   dtx->TexImage[time] = image;
}

/*
 * Read a texture map from the named file.  The image can be any SGI
 * .rgb file.
 * Input:  filename - name of image file
 * Return:  1 = ok, 0 = error.
 */
int read_texture_image( Display_Context dtx, char *filename )
{
   RGB_IMAGE *img;
   int width, height;
   int i;
   unsigned int *image;

   img = ReadRGB( filename );
   if (!img)
      return 0;

   width = img->sizeX;
   height = img->sizeY;
   if (width>1024) {
      /* too wide */
      FreeRGB( img );
      return 0;
   }

   image = (unsigned int *) img->data;

#ifdef HAVE_OPENGL
   /* The texture size *MUST* be a power of two.  Rescale now if needed. */
   {
      int width2, height2, max;

      width2 = round2( width );
      height2 = round2( height );
      glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max );
      if (width2>max)  width2 = max;
      if (height2>max)  height2 = max;
      if (width!=width2 || height!=height2) {
         unsigned int *image2;
         image2 = (unsigned int *) malloc( width2 * height2 * 4 );
         gluScaleImage( GL_RGBA,
                        width, height, GL_UNSIGNED_BYTE, image,
                        width2, height2, GL_UNSIGNED_BYTE, image2 );
         width = width2;
         height = height2;
         image = image2;
      }
      check_gl_error( "read_texture_image" );
   }
#endif  /*ifdef HAVE_OPENGL*/

   /* use same texture for all timesteps */
   for (i=0;i<dtx->NumTimes;i++) {
      define_texture( dtx, i, width, height, 4, image );
   }

/*   FreeRGB(img);*/
   return 1;
}



/* WLH 11-25-94
 * Read a sequence of images from a file, to use for texture mapping.
 * Input:  name - name of file.
 * Return:  1 = ok, 0 = error.
 */
int read_texture_sequence( Display_Context dtx, char *name )
{
  int i, length, fd, head[3];
  int width, height, max;
  unsigned char *data;

  if ((fd = open(name, O_RDONLY, 0)) == -1) {
    return 0; /* cannot open file */
  }
  length = 3 * sizeof(int);
  if (read(fd, head, length) != length) {
    return 0; /* cannot read file header */
  }
  if (head[0] < dtx->NumTimes) {
    return 0; /* not enough time steps in file */
  }

  for (i=0;i<dtx->NumTimes;i++) {
    height = head[1];
    width = head[2];

    length = width * height * sizeof(unsigned char);
    data = (unsigned char *) malloc(length);

    if (read(fd, data, length) != length) {
      return 0; /* cannot read image from file */
    }

#ifdef HAVE_OPENGL
    /* The texture size *MUST* be a power of two.  Rescale now if needed. */
    {
       int width2, height2;
       unsigned char *data2;
       width2 = round2( width );
       height2 = round2( height );
       glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max );
       if (width2>max)  width2 = max;
       if (height2>max)  height2 = max;
       if (width!=width2 || height!=height2) {
          data2 = (unsigned char *) malloc( width2 * height2 );
          gluScaleImage( GL_LUMINANCE,
                         width, height, GL_UNSIGNED_BYTE, data,
                         width2, height2, GL_UNSIGNED_BYTE, data2 );
          free( data );
          width = width2;
          height = height2;
          data = data2;
       }
       check_gl_error( "read_texture_sequence" );
    }
#endif

    define_texture( dtx, i, width, height, 1, data );

  }
  return 1;
}



#ifdef HAVE_MCIDAS
/*
 * Read a sequence of McIDAS area files to use for texture mapping.
 * Input:  first - number of first AREA file in the sequence.
 * Return:  1 = ok, 0 = error.
 */
int read_texture_areas( Display_Context dtx, int first )
{
   int i;
   int width, height;
   unsigned int *image;

#ifndef MCIDAS_SIDECAR
   init_mcidas_kludge();
#endif

   for (i=0;i<dtx->NumTimes;i++) {
      if (!read_area( dtx, first+i, &width, &height,
                      (unsigned char **) &image )) {
         /* error */
         return 0;
      }
#ifdef HAVE_OPENGL
      /* The texture size *MUST* be a power of two.  Rescale now if needed. */
      {
         int width2, height2, max;
         unsigned int *image2;

         width2 = round2( width );
         height2 = round2( height );
         glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max );
         if (width2>max)  width2 = max;
         if (height2>max)  height2 = max;
         if (width!=width2 || height!=height2) {
            unsigned int *image2;
            image2 = (unsigned int *) malloc( width2 * height2 * 4 );
            gluScaleImage( GL_LUMINANCE,
                           width, height, GL_UNSIGNED_BYTE, image,
                           width2, height2, GL_UNSIGNED_BYTE, image2 );
            free( image );
            width = width2;
            height = height2;
            image = image2;
         }
         check_gl_error( "read_texture_areas" );
      }
#endif
      define_texture( dtx, i, width, height, 1, image );
   }
  return 1;
}
#endif  /* ifdef HAVE_MCIDAS */




/*
 * Specify which texture to use.  If time==-1, disable texturing.
 */
int use_texture( Display_Context dtx, int time )
{

   assert( time>=0 && time<dtx->NumTimes );


   /* Do one-time initializations */
   if (dtx->init_flag) {  
#ifdef HAVE_SGI_GL
      /* define special material and lighting model for texturing */
      lmdef( DEFMATERIAL, 11, 0, mat2 );
      lmdef( DEFLMODEL, 31, 0, lmodel2 );
#endif
#ifdef HAVE_OPENGL
      glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
      glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
      check_gl_error( "use_texture (glTexParameter)" );
#endif
      dtx->init_flag = 0;
   } 

   /* Give the texture to the graphics library / hardware */
   if (dtx->TexImage[time]) {
      if (dtx->prev_time==-1 || dtx->TexImage[dtx->prev_time]!=dtx->TexImage[time] ||
          dtx->TexImageNew[time] == 1) {
#ifdef HAVE_SGI_GL
         texdef2d( 1, dtx->TexComponents[time],
                   dtx->TexWidth[time], dtx->TexHeight[time],
                   (unsigned long *) dtx->TexImage[time],
                   5, texprops );
         tevdef( 1, 0, tevprops );
#endif
#ifdef HAVE_OPENGL
         if (dtx->TexComponents[time]==1) {
            glTexImage2D( GL_TEXTURE_2D, 0,
                          1, dtx->TexWidth[time], dtx->TexHeight[time],
                          0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
                          dtx->TexImage[time] );
         }
         else {
            glTexImage2D( GL_TEXTURE_2D, 0,
                          dtx->TexComponents[time],
                          dtx->TexWidth[time], dtx->TexHeight[time],
                          0, GL_RGBA, GL_UNSIGNED_BYTE, dtx->TexImage[time] );
         }
         check_gl_error( "use_texture (glTexImage2D)" );
#endif
      dtx->TexImageNew[time] = 0;
      }
   }

   dtx->prev_time = time;

   return 0;
}



/*
 * Enable or disable texture mapping.
 */
static void enable_texture( int enable )
{
   
   if (enable) {
#ifdef HAVE_SGI_GL
      texbind( TX_TEXTURE_0, 1 );
      tevbind( TV_ENV0, 1 );
      scrsubdivide( SS_DEPTH, subdiv_params );
      /* special lighting parameters */
      lmbind( MATERIAL, 11 );
      lmbind( LMODEL, 31 );
      /*lmcolor( LMC_AD );     Added on 4-11-95 by BEP */
      cpack( 0xffffffff );
#endif
#ifdef HAVE_OPENGL
      glEnable( GL_TEXTURE_2D );
      glEnable( GL_LIGHTING );
      glColor4f( 1.0, 1.0, 1.0, 1.0 );
      check_gl_error( "enable_texture (glEnable)" );
#endif
   }
   else {
      /* disable */
#ifdef HAVE_SGI_GL
      texbind( TX_TEXTURE_0, 0 );
      tevbind( TV_ENV0, 0 );
      scrsubdivide( SS_OFF, subdiv_params );
      /* regular lighting */
      lmbind( MATERIAL, 10 );
      lmbind( LMODEL, 30 );
#endif
#ifdef HAVE_OPENGL
      glDisable( GL_TEXTURE_2D );
      glDisable( GL_LIGHTING );
      check_gl_error( "enable_texture (glDisable)" );
#endif
   }
}



/*
 * Draw a texture mapped quadrilateral mesh using the currently defined
 * texture.
 */
int texture_quadmeshnorm( int rows, int cols, float vert[][3],
                          float norm[][3], float texcoord[][2] )
{
   int i, j, base1, base2;

   /* turn on texture mapping */
   enable_texture(1);

   /* break mesh into strips */
   for (i=0;i<rows-1;i++) {
      base1 = i * cols;
      base2 = (i+1) * cols;
#ifdef HAVE_SGI_GL
      if (norm) {
         bgnqstrip();
         for (j=0;j<cols;j++) {

            t2f( texcoord[base1+j] );
            n3f( norm[base1+j] );
            v3f( vert[base1+j] );

            t2f( texcoord[base2+j] );
            n3f( norm[base2+j] );
            v3f( vert[base2+j] );
         }
         endqstrip();
      }
      else {
         /* no normals */
         float n[3];
         n[0] = 0.0;  n[1] = 0.0;  n[2] = 1.0;
         n3f(n);
         bgnqstrip();
         for (j=0;j<cols;j++) {
            t2f( texcoord[base1+j] );
            v3f( vert[base1+j] );
            t2f( texcoord[base2+j] );
            v3f( vert[base2+j] );
         }
         endqstrip();
      }
#endif
#ifdef HAVE_OPENGL
      glFinish();
      if (norm) {
         glBegin( GL_QUAD_STRIP );
         for (j=0;j<cols;j++) {
            glTexCoord2fv( texcoord[base1+j] );
            glNormal3fv( norm[base1+j] );
            glVertex3fv( vert[base1+j] );

            glTexCoord2fv( texcoord[base2+j] );
            glNormal3fv( norm[base2+j] );
            glVertex3fv( vert[base2+j] );
         }
         glEnd();
      }
      else {
         /* no normals */
         glNormal3f( 0.0, 0.0, 1.0 );
         glBegin( GL_QUAD_STRIP );
         for (j=0;j<cols;j++) {
            glTexCoord2fv( texcoord[base1+j] );
            glVertex3fv( vert[base1+j] );

            glTexCoord2fv( texcoord[base2+j] );
            glVertex3fv( vert[base2+j] );
         }
         glEnd();
      }
      glFinish();
      check_gl_error( "texture_quadmeshnorm" );
#endif
   }

   /* turn off texture mapping */
   enable_texture(0);

   return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1