/*
======================================================================
vmap.c

Vertex map functions for an LWO2 reader.

Ernie Wright  17 Sep 00
====================================================================== */

#include "../picointernal.h"
#include "lwo2.h"


/*
======================================================================
lwFreeVMap()

Free memory used by an lwVMap.
====================================================================== */

void lwFreeVMap( lwVMap *vmap )
{
   if ( vmap ) {
      if ( vmap->name ) _pico_free( vmap->name );
      if ( vmap->vindex ) _pico_free( vmap->vindex );
      if ( vmap->pindex ) _pico_free( vmap->pindex );
      if ( vmap->val ) {
         if ( vmap->val[ 0 ] ) _pico_free( vmap->val[ 0 ] );
         _pico_free( vmap->val );
      }
      _pico_free( vmap );
   }
}


/*
======================================================================
lwGetVMap()

Read an lwVMap from a VMAP or VMAD chunk in an LWO2.
====================================================================== */

lwVMap *lwGetVMap( picoMemStream_t *fp, int cksize, int ptoffset, int poloffset,
   int perpoly )
{
   unsigned char *buf, *bp;
   lwVMap *vmap;
   float *f;
   int i, j, npts, rlen;


   /* read the whole chunk */

   set_flen( 0 );
   buf = getbytes( fp, cksize );
   if ( !buf ) return NULL;

   vmap = _pico_calloc( 1, sizeof( lwVMap ));
   if ( !vmap ) {
      _pico_free( buf );
      return NULL;
   }

   /* initialize the vmap */

   vmap->perpoly = perpoly;

   bp = buf;
   set_flen( 0 );
   vmap->type = sgetU4( &bp );
   vmap->dim  = sgetU2( &bp );
   vmap->name = sgetS0( &bp );
   rlen = get_flen();

   /* count the vmap records */

   npts = 0;
   while ( bp < buf + cksize ) {
      i = sgetVX( &bp );
      if ( perpoly )
         i = sgetVX( &bp );
      bp += vmap->dim * sizeof( float );
      ++npts;
   }

   /* allocate the vmap */

   vmap->nverts = npts;
   vmap->vindex = _pico_calloc( npts, sizeof( int ));
   if ( !vmap->vindex ) goto Fail;
   if ( perpoly ) {
      vmap->pindex = _pico_calloc( npts, sizeof( int ));
      if ( !vmap->pindex ) goto Fail;
   }

   if ( vmap->dim > 0 ) {
      vmap->val = _pico_calloc( npts, sizeof( float * ));
      if ( !vmap->val ) goto Fail;
      f = _pico_alloc( npts * vmap->dim * sizeof( float ));
      if ( !f ) goto Fail;
      for ( i = 0; i < npts; i++ )
         vmap->val[ i ] = f + i * vmap->dim;
   }

   /* fill in the vmap values */

   bp = buf + rlen;
   for ( i = 0; i < npts; i++ ) {
      vmap->vindex[ i ] = sgetVX( &bp );
      if ( perpoly )
         vmap->pindex[ i ] = sgetVX( &bp );
      for ( j = 0; j < vmap->dim; j++ )
         vmap->val[ i ][ j ] = sgetF4( &bp );
   }

   _pico_free( buf );
   return vmap;

Fail:
   if ( buf ) _pico_free( buf );
   lwFreeVMap( vmap );
   return NULL;
}


/*
======================================================================
lwGetPointVMaps()

Fill in the lwVMapPt structure for each point.
====================================================================== */

int lwGetPointVMaps( lwPointList *point, lwVMap *vmap )
{
   lwVMap *vm;
   int i, j, n;

   /* count the number of vmap values for each point */

   vm = vmap;
   while ( vm ) {
      if ( !vm->perpoly )
         for ( i = 0; i < vm->nverts; i++ )
            ++point->pt[ vm->vindex[ i ]].nvmaps;
      vm = vm->next;
   }

   /* allocate vmap references for each mapped point */

   for ( i = 0; i < point->count; i++ ) {
      if ( point->pt[ i ].nvmaps ) {
         point->pt[ i ].vm = _pico_calloc( point->pt[ i ].nvmaps, sizeof( lwVMapPt ));
         if ( !point->pt[ i ].vm ) return 0;
         point->pt[ i ].nvmaps = 0;
      }
   }

   /* fill in vmap references for each mapped point */

   vm = vmap;
   while ( vm ) {
      if ( !vm->perpoly ) {
         for ( i = 0; i < vm->nverts; i++ ) {
            j = vm->vindex[ i ];
            n = point->pt[ j ].nvmaps;
            point->pt[ j ].vm[ n ].vmap = vm;
            point->pt[ j ].vm[ n ].index = i;
            ++point->pt[ j ].nvmaps;
         }
      }
      vm = vm->next;
   }

   return 1;
}


/*
======================================================================
lwGetPolyVMaps()

Fill in the lwVMapPt structure for each polygon vertex.
====================================================================== */

int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap )
{
   lwVMap *vm;
   lwPolVert *pv;
   int i, j;

   /* count the number of vmap values for each polygon vertex */

   vm = vmap;
   while ( vm ) {
      if ( vm->perpoly ) {
         for ( i = 0; i < vm->nverts; i++ ) {
            for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) {
               pv = &polygon->pol[ vm->pindex[ i ]].v[ j ];
               if ( vm->vindex[ i ] == pv->index ) {
                  ++pv->nvmaps;
                  break;
               }
            }
         }
      }
      vm = vm->next;
   }

   /* allocate vmap references for each mapped vertex */

   for ( i = 0; i < polygon->count; i++ ) {
      for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) {
         pv = &polygon->pol[ i ].v[ j ];
         if ( pv->nvmaps ) {
            pv->vm = _pico_calloc( pv->nvmaps, sizeof( lwVMapPt ));
            if ( !pv->vm ) return 0;
            pv->nvmaps = 0;
         }
      }
   }

   /* fill in vmap references for each mapped point */

   vm = vmap;
   while ( vm ) {
      if ( vm->perpoly ) {
         for ( i = 0; i < vm->nverts; i++ ) {
            for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) {
               pv = &polygon->pol[ vm->pindex[ i ]].v[ j ];
               if ( vm->vindex[ i ] == pv->index ) {
                  pv->vm[ pv->nvmaps ].vmap = vm;
                  pv->vm[ pv->nvmaps ].index = i;
                  ++pv->nvmaps;
                  break;
               }
            }
         }
      }
      vm = vm->next;
   }

   return 1;
}
