home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog Special Issue 2
/
Freelog_HS_3_Setp_Oct_Nov_2000_CD2.mdx
/
Arcade
/
Orbit
/
src
/
ac3d.c
next >
Wrap
C/C++ Source or Header
|
1999-09-28
|
27KB
|
1,343 lines
/*
ORBIT, a freeware space combat simulator
Copyright (C) 1999 Steve Belczyk <steve1@genesis.nred.ma.us>
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.
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 "orbit.h"
/*
* Stuff to load AC3D files
*
* Most of this is based on ac-to-gl, by Steve Baker <sjbaker1@airmail.net>
*/
int DoMaterial (char *s);
int DoObject (char *s);
int DoACName (char *s);
int DoData (char *s);
int DoTexture (char *s);
int DoTexrep (char *s);
int DoRot (char *s);
int DoLoc (char *s);
int DoUrl (char *s);
int DoNumvert (char *s);
int DoNumsurf (char *s);
int DoSurf (char *s);
int DoMat (char *s);
int DoRefs (char *s);
int DoKids (char *s);
int DoObjWorld (char *s);
int DoObjPoly (char *s);
int DoObjGroup (char *s);
void SkipSpaces (char **s);
void SkipQuotes (char **s);
void RemoveQuotes (char *s);
void load_sgi_texture (char *s);
#define MAX_TEXTURES 1000 /* This *ought* to be enough! */
char *texture_fnames [MAX_TEXTURES];
int acsmooth; /* Smooth surface? */
int acsurfpass; /* Pass through surface */
#define PASS_NORMAL (0) /* Get normals */
#define PASS_RENDER (1) /* Render */
struct
{
int id; /* Texture id */
char name[64]; /* Name */
} tex[MAX_TEXTURES];
int ntex;
struct ACVertex
{
float v[3]; /* Vertex coords */
float normal[3]; /* Normal */
float n; /* Number of normals to average */
} *vtab;
int num_materials = 0;
int num_textures = 0;
int nv; /* Number of vertices in vtab */
int last_flags = -1;
int last_mat;
int last_num_kids = -1;
int current_flags = -1;
int texture_enabled = 0;
int need_texture = 0;
int firsttime2;
#define PARSE_CONT 0
#define PARSE_POP 1
int matlist[1024];
int modelnum;
FILE *acfd;
struct Tag
{
char *token;
int (*func) (char *s);
};
struct Tag top_tags [] =
{
{"MATERIAL", DoMaterial},
{"OBJECT" , DoObject },
};
InitTextures()
{
int t;
for (t=0; t<MAX_TEXTURES; t++)
{
tex[t].id = (-1);
tex[t].name[0] = 0;
}
ntex = 0;
}
LoadAC3D (name)
char *name;
{
char buffer[1024], *s, fn[128];
int firsttime, m;
/* See if it's already loaded */
for (m=0; m<NMODELS; m++)
{
if (!strcmp (model[m].name, name)) return (m);
}
Log ("LoadAC3D: Loading model %s", name);
/* Find an unused model index */
m = modelnum = FindModel();
strcpy (model[m].name, name);
model[m].in_use = 1;
/* Init bounding box */
model[m].lobound[0] = model[m].lobound[1] = model[m].lobound[2] = 0.0;
model[m].hibound[0] = model[m].hibound[1] = model[m].hibound[2] = 0.0;
/* Get a list id */
model[m].list = glGenLists (1);
/* Construct file name */
sprintf (fn, "models/%s", name);
/* Open it */
if (NULL == (acfd = fopen (fn, "rt")))
{
Log ("LoadAC3D: Can't open %s, giving up!", fn);
return (-1);
}
firsttime = 1;
firsttime2 = 1;
num_materials = 0;
num_textures = 0;
vtab = NULL;
last_flags = -1 ;
last_num_kids = -1 ;
current_flags = -1 ;
texture_enabled = 0;
need_texture = 0;
last_mat = -1;
/* Look for and define textures */
ScanForTextures();
while (fgets (buffer, 1024, acfd) != NULL)
{
s = buffer ;
/* Skip leading whitespace */
SkipSpaces (&s);
/* Skip blank lines and comments */
if ( *s < ' ' && *s != '\t' ) continue;
if ( *s == '#' || *s == ';' ) continue;
if (firsttime)
{
firsttime = 0;
if (strncmp (s, "AC3D", 4) != 0)
{
Log ("LoadAC3d: %s is not an AC3D format file", fn);
return (-1);
}
}
else
{
Search (top_tags, s);
}
}
glShadeModel (GL_FLAT);
glEndList();
fclose (acfd);
Log ("LoadAC3D: Lo bounds: %lf %lf %lf", model[m].lobound[0],
model[m].lobound[1], model[m].lobound[2]);
Log ("LoadAC3D: Hi bounds: %lf %lf %lf", model[m].hibound[0],
model[m].hibound[1], model[m].hibound[2]);
/* All is well */
return m;
}
ScanForTextures()
/*
* Look through the AC3D file for all "texture" lines
*/
{
char buf[1024], fn[256];
/* Look for "texture" lines */
while (NULL != fgets (buf, 1024, acfd))
{
if (1 == sscanf (buf, "texture %s", fn))
{
RemoveQuotes (fn);
LoadTexture (fn);
}
}
/* Rewind the file */
fseek (acfd, 0, SEEK_SET);
}
LoadTexture (s)
char *s;
/*
* Load a texture file
*/
{
char fn[256];
Log ("LoadTexture: Loading texture %s", s);
/* Construct file name */
sprintf (fn, "models/%s", s);
/* Get a texture id */
glGenTextures (1, &tex[ntex].id);
strcpy (tex[ntex].name, s);
/* Define the texture */
glBindTexture (GL_TEXTURE_2D, tex[ntex].id);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
load_sgi_texture (fn);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
ntex++;
}
void ACNormalize (float *v)
{
float mag;
mag = sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
if (mag == 0.0f)
{
v[0] = v[1] = 0.0f;
v[2] = 1.0f;
return;
}
v[0] /= mag;
v[1] /= mag;
v[2] /= mag;
}
void CrossProduct (float *dst, float *a, float *b)
{
dst[0] = a[1] * b[2] - a[2] * b[1];
dst[1] = a[2] * b[0] - a[0] * b[2];
dst[2] = a[0] * b[1] - a[1] * b[0];
}
void MakeNormal (float *dst, float *a, float *b, float *c)
{
float ab[3];
float ac[3];
ab[0] = b[0] - a[0];
ab[1] = b[1] - a[1];
ab[2] = b[2] - a[2];
ACNormalize (ab);
ac[0] = c[0] - a[0];
ac[1] = c[1] - a[1];
ac[2] = c[2] - a[2];
ACNormalize (ac);
CrossProduct (dst, ab, ac);
ACNormalize (dst);
}
void SkipSpaces (char **s)
{
while (**s == ' ' || **s == '\t') (*s)++;
}
void SkipQuotes (char **s)
{
char *t;
SkipSpaces (s);
if (**s == '\"')
{
(*s)++;
t = *s;
while (*t != '\0' && *t != '\"') t++;
if ( *t != '\"' )
Log ("SkipQuotes: Mismatched double-quote in '%s'\n", *s);
*t = '\0';
}
else
{
Log ("SkipQuuotes: Expected double-quote in '%s'", *s);
}
}
void RemoveQuotes (char *s)
/*
* Remove quotes from string
*/
{
char *t;
int i, j, len;
len = strlen (s);
t = (char *) malloc (len+1);
strcpy (t, s);
j = 0;
for (i=0; i<len; i++)
{
if (t[i] != '"')
{
s[j++] = t[i];
}
}
s[j] = 0;
free (t);
}
int Search (struct Tag *tags, char *s)
{
int i;
SkipSpaces (&s);
for (i=0; tags[i].token != NULL; i++)
{
if (!strncasecmp (tags[i].token, s, strlen (tags[i].token)))
{
s += strlen (tags[i].token);
SkipSpaces (&s);
return (*(tags[i].func))(s);
}
}
Log ("Search: Unrecognised token '%s'", s);
exit (1);
return 0;
}
struct Tag object_tags[] =
{
{"name" , DoACName},
{"data" , DoData},
{"texture", DoTexture},
{"texrep" , DoTexrep},
{"rot" , DoRot},
{"loc" , DoLoc},
{"url" , DoUrl},
{"numvert", DoNumvert},
{"numsurf", DoNumsurf},
{"kids" , DoKids},
{NULL, NULL }
};
struct Tag surf_tag[] =
{
{"SURF", DoSurf},
{NULL, NULL}
};
struct Tag surface_tags[] =
{
{"mat" , DoMat },
{"refs", DoRefs},
{NULL, NULL}
};
struct Tag obj_type_tags[] =
{
{"world", DoObjWorld},
{"poly" , DoObjPoly },
{"group", DoObjGroup},
{NULL, NULL}
};
#define OBJ_WORLD 0
#define OBJ_POLY 1
#define OBJ_GROUP 2
int DoObjWorld (char *s)
{
return OBJ_WORLD;
}
int DoObjPoly (char *s)
{
return OBJ_POLY;
}
int DoObjGroup (char *s)
{
return OBJ_GROUP;
}
void DoBegin (int flags)
{
if (last_flags == -1) last_flags = ~flags;
if (((flags>>4) & 0x01) != ((last_flags>>4) & 0x01))
{
if ((flags>>4) & 0x01)
{
/* Smooth Shaded */
acsmooth = 1;
}
else
{
/* Flat Shaded */
acsmooth = 0;
}
}
/**
if (((flags>>4) & 0x02) != ((last_flags>>4) & 0x02))
{
if ((flags>>4) & 0x02)
{
if (acsurfpass == PASS_RENDER) glDisable (GL_CULL_FACE);
}
else
{
if (acsurfpass == PASS_RENDER) glEnable (GL_CULL_FACE);
}
}
if ((last_flags & 0x0F) == 0 && (flags & 0x0F) != 0)
{
if (acsurfpass == PASS_RENDER) glDisable (GL_LIGHTING);
}
else if ((last_flags & 0x0F) != 0 && (flags & 0x0F) == 0)
{
if (acsurfpass == PASS_RENDER) glEnable (GL_LIGHTING);
}
**/
last_flags = flags;
switch (flags & 0x0F)
{
case 0: if (acsurfpass == PASS_RENDER) glBegin (GL_POLYGON);
break;
case 1: if (acsurfpass == PASS_RENDER) glBegin (GL_LINE_LOOP);
break;
case 2: if (acsurfpass == PASS_RENDER) glBegin (GL_LINE_STRIP);
break;
default:
Log ("DoBegin: Illegal surface type 0x%02x", flags);
break;
}
}
int DoMaterial (char *s)
{
char name[1024], *nm;
float rgb[3], amb[3], emis[3], spec[3], trans;
int shi;
if (15 != sscanf (s,
"%s rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f",
name, &rgb[0], &rgb[1], &rgb[2], &amb[0], &amb[1], &amb[2],
&emis[0], &emis[1], &emis[2], &spec[0], &spec[1], &spec[2], &shi, &trans))
{
Log ("DoMaterial: Can't parse this MATERIAL: %s", s);
}
else
{
nm = name;
SkipQuotes (&nm);
/* Get a list for this material */
matlist[num_materials] = glGenLists (1);
glNewList (matlist[num_materials], GL_COMPILE );
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, amb);
glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, rgb);
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, emis);
glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shi);
glEndList();
}
num_materials++;
return PARSE_CONT;
}
int DoObject (char *s)
{
int i, obj_type, num_kids;
char buffer[1024];
if (firsttime2)
{
glNewList (model[modelnum].list, GL_COMPILE);
glShadeModel (GL_SMOOTH);
firsttime2 = 0;
}
obj_type = Search (obj_type_tags, s);
switch (obj_type)
{
case OBJ_WORLD:
break;
case OBJ_POLY:
break;
case OBJ_GROUP:
break;
}
need_texture = 0;
glPushMatrix();
while (NULL != fgets (buffer, 1024, acfd))
{
if (Search (object_tags, buffer) == PARSE_POP) break;
}
num_kids = last_num_kids;
for (i=0; i<num_kids; i++)
{
fgets (buffer, 1024, acfd);
Search (top_tags, buffer);
}
glPopMatrix();
return PARSE_CONT;
}
int DoACName (char *s)
{
SkipQuotes (&s);
return PARSE_CONT;
}
int DoData (char *s)
{
int i, len;
len = strtol (s, NULL, 0);
Log ("DoData: WARNING - data string encountered");
for (i=0; i<len; i++) fgetc (acfd);
return PARSE_CONT;
}
int GetTexture (char *s)
{
int t;
for (t=0; t<ntex; t++)
{
if (!strcasecmp (s, tex[t].name)) return t;
}
return 0;
}
int DoTexture (char *s)
{
static int last_tex = -1;
int t;
SkipQuotes (&s);
t = GetTexture (s);
if (t != last_tex)
{
glBindTexture (GL_TEXTURE_2D, tex[t].id);
last_tex = t;
}
need_texture = 1;
return PARSE_CONT;
}
int DoTexrep (char *s)
{
float texrep[2];
if (2 != sscanf (s, "%f %f", &texrep[0], &texrep[1]))
Log ("DoTextrep: Illegal texrep record: %s", s);
return PARSE_CONT;
}
int DoRot (char *s)
{
float mat[4][4];
mat[0][3] = mat[1][3] = mat[2][3] = mat[3][0] = mat[3][1] = mat[3][2] = 0.0f;
mat[3][3] = 1.0f;
if (9 != sscanf (s, "%f %f %f %f %f %f %f %f %f",
&mat[0][0], &mat[0][1], &mat[0][2],
&mat[1][0], &mat[1][1], &mat[1][2],
&mat[2][0], &mat[2][1], &mat[2][2]))
{
Log ("DoRot: Illegal rot record: %s", s);
}
glMultMatrixf (mat);
return PARSE_CONT ;
}
int DoLoc (char *s)
{
float loc[3];
if (3 != sscanf (s, "%f %f %f", &loc[0], &loc[1], &loc[2]))
{
Log ("DoLoc: Illegal loc record: %s", s);
}
loc[0] /= 100.0;
loc[1] /= 100.0;
loc[2] /= 100.0;
glTranslatef (loc[0], loc[1], loc[2]);
return PARSE_CONT;
}
int DoUrl (char *s)
{
SkipQuotes (&s);
return PARSE_CONT;
}
int DoNumvert (char *s)
{
char buffer[1024];
int i;
nv = strtol (s, NULL, 0);
if (vtab != NULL) free (vtab) ;
vtab = (struct ACVertex *) malloc (3*nv*sizeof(struct ACVertex));
for (i=0; i<nv; i++)
{
fgets (buffer, 1024, acfd);
if (3 != sscanf (buffer, "%f %f %f", &vtab[i].v[0], &vtab[i].v[1], &vtab[i].v[2]))
{
Log ("DoNumvert: Illegal vertex record: %s", buffer);
}
vtab[i].v[0] /= 100.0;
vtab[i].v[1] /= 100.0;
vtab[i].v[2] /= 100.0;
vtab[i].normal[0] = vtab[i].normal[1] = vtab[i].normal[2] = 0.0;
vtab[i].n = 0.0;
if (vtab[i].v[0] < model[modelnum].lobound[0]) model[modelnum].lobound[0] = vtab[i].v[0];
if (vtab[i].v[1] < model[modelnum].lobound[1]) model[modelnum].lobound[1] = vtab[i].v[1];
if (vtab[i].v[2] < model[modelnum].lobound[2]) model[modelnum].lobound[2] = vtab[i].v[2];
if (vtab[i].v[0] > model[modelnum].hibound[0]) model[modelnum].hibound[0] = vtab[i].v[0];
if (vtab[i].v[1] > model[modelnum].hibound[1]) model[modelnum].hibound[1] = vtab[i].v[1];
if (vtab[i].v[2] > model[modelnum].hibound[2]) model[modelnum].hibound[2] = vtab[i].v[2];
}
return PARSE_CONT;
}
int DoNumsurf (char *s)
{
int i, ns, v;
long pos, p;
char buffer[1024];
ns = strtol (s, NULL, 0);
/* Do first pass to average normals */
pos = ftell (acfd);
acsurfpass = PASS_NORMAL;
for (i=0; i<ns; i++)
{
p = ftell (acfd);
fgets (buffer, 1024, acfd);
Search (surf_tag, buffer);
}
/* Back to beginning of object */
fseek (acfd, pos, SEEK_SET);
/* Average normals */
for (v=0; v<nv; v++)
{
vtab[v].normal[0] /= vtab[v].n;
vtab[v].normal[1] /= vtab[v].n;
vtab[v].normal[2] /= vtab[v].n;
}
/* Now render */
acsurfpass = PASS_RENDER;
last_mat = (-1);
for (i=0; i<ns; i++)
{
p = ftell (acfd);
fgets (buffer, 1024, acfd);
Search (surf_tag, buffer);
}
return PARSE_CONT;
}
int DoSurf (char *s)
{
char buffer[1024];
current_flags = strtol (s, NULL, 0);
while (NULL != fgets (buffer, 1024, acfd))
{
if (Search (surface_tags, buffer) == PARSE_POP) break;
}
return PARSE_CONT ;
}
int DoMat (char *s)
{
int mat;
mat = strtol (s, NULL, 0);
if (mat != last_mat)
{
if (acsurfpass == PASS_RENDER) glCallList (matlist[mat]);
last_mat = mat;
}
return PARSE_CONT ;
}
int DoRefs (char *s)
{
int i;
int nrefs;
char buffer[1024] ;
int *vlist;
float *tlistu, *tlistv, nrm[3];
nrefs = strtol (s, NULL, 0);
if (nrefs == 0) return PARSE_POP;
if (need_texture && !texture_enabled)
{
if (acsurfpass == PASS_RENDER) glEnable (GL_TEXTURE_2D);
texture_enabled = 1;
}
else if (!need_texture && texture_enabled)
{
if (acsurfpass == PASS_RENDER) glDisable (GL_TEXTURE_2D);
texture_enabled = 0;
}
vlist = (int *) malloc (sizeof(int)*nrefs);
tlistu = (float *) malloc (sizeof(float)*nrefs) ;
tlistv = (float *) malloc (sizeof(float)*nrefs) ;
DoBegin (current_flags);
for (i=0; i<nrefs; i++)
{
fgets (buffer, 1024, acfd);
if (3 != sscanf (buffer, "%d %f %f", &vlist[i], &tlistu[i], &tlistv[i]))
{
Log ("DoRefs: Illegal ref record: %s", buffer);
exit (1);
}
}
if (nrefs >= 3)
{
MakeNormal (nrm, vtab[vlist[0]].v, vtab[vlist[1]].v, vtab[vlist[2]].v);
if (!acsmooth && (acsurfpass == PASS_RENDER))
{
glNormal3fv (nrm);
}
}
for (i=0; i<nrefs; i++)
{
/* Maintain normal first pass */
if (acsmooth && (acsurfpass == PASS_NORMAL))
{
MaintainNormal (vlist[i], nrm);
}
/* Render second pass */
if (acsmooth && (acsurfpass == PASS_RENDER))
{
glNormal3fv (vtab[vlist[i]].normal);
}
if (acsurfpass == PASS_RENDER)
{
glTexCoord2f (tlistu[i], tlistv[i]);
glVertex3fv (vtab[vlist[i]].v);
}
}
free (vlist);
free (tlistu);
free (tlistv);
if (acsurfpass == PASS_RENDER) glEnd();
return PARSE_POP;
}
int DoKids (char *s)
{
last_num_kids = strtol (s, NULL, 0);
return PARSE_POP;
}
MaintainNormal (i, n)
int i;
float n[3];
/*
* Maintain a vertex normal
*/
{
vtab[i].n += 1.0;
vtab[i].normal[0] += n[0];
vtab[i].normal[1] += n[1];
vtab[i].normal[2] += n[2];
}
/** ------------- The rest loads SGI texture files --------------- **/
/* Some magic constants in the file header. */
#define SGI_IMG_MAGIC 0x01DA
#define SGI_IMG_SWABBED_MAGIC 0xDA01 /* This is how it appears on a PC */
#define SGI_IMG_VERBATIM 0
#define SGI_IMG_RLE 1
static int sgi_max ;
static int sgi_min ;
static int sgi_colormap ;
static unsigned short sgi_magic ;
static char sgi_type ;
static char sgi_bpp ;
static unsigned int *sgi_start ;
static int *sgi_leng ;
static unsigned char *sgi_temp ;
static int sgi_swapped ;
static FILE *sgi_fd ;
static unsigned short sgi_dim ;
static unsigned short sgi_xsize ;
static unsigned short sgi_ysize ;
static unsigned short sgi_zsize ;
static void makeConsistentHeader ()
{
/*
Sanity checks - and a workaround for buggy RGB files generated by
the MultiGen Paint program because it will sometimes get confused
about the way to represent maps with more than one component.
eg Y > 1, Number of dimensions == 1
Z > 1, Number of dimensions == 2
*/
if ( sgi_ysize > 1 && sgi_dim < 2 ) sgi_dim = 2 ;
if ( sgi_zsize > 1 && sgi_dim < 3 ) sgi_dim = 3 ;
if ( sgi_dim < 1 ) sgi_ysize = 1 ;
if ( sgi_dim < 2 ) sgi_zsize = 1 ;
if ( sgi_dim > 3 ) sgi_dim = 3 ;
if ( sgi_zsize < 1 && sgi_ysize == 1 ) sgi_dim = 1 ;
if ( sgi_zsize < 1 && sgi_ysize != 1 ) sgi_dim = 2 ;
if ( sgi_zsize >= 1 ) sgi_dim = 3 ;
/*
A very few SGI image files have 2 bytes per component - this
tool cannot deal with those kinds of files.
*/
if ( sgi_bpp == 2 )
{
Log ("makeConsistentHeader: Can't work with SGI images with %d bpp", sgi_bpp);
exit (1);
}
sgi_bpp = 1 ;
sgi_min = 0 ;
sgi_max = 255 ;
sgi_magic = SGI_IMG_MAGIC ;
sgi_colormap = 0 ;
}
static void swab_short ( unsigned short *x )
{
if ( sgi_swapped )
*x = (( *x >> 8 ) & 0x00FF ) |
(( *x << 8 ) & 0xFF00 ) ;
}
static void swab_int ( unsigned int *x )
{
if ( sgi_swapped )
*x = (( *x >> 24 ) & 0x000000FF ) |
(( *x >> 8 ) & 0x0000FF00 ) |
(( *x << 8 ) & 0x00FF0000 ) |
(( *x << 24 ) & 0xFF000000 ) ;
}
static void swab_int_array ( int *x, int leng )
{
int i;
if ( ! sgi_swapped )
return ;
for ( i = 0 ; i < leng ; i++ )
{
*x = (( *x >> 24 ) & 0x000000FF ) |
(( *x >> 8 ) & 0x0000FF00 ) |
(( *x << 8 ) & 0x00FF0000 ) |
(( *x << 24 ) & 0xFF000000 ) ;
x++ ;
}
}
static unsigned char readByte ()
{
unsigned char x ;
fread ( & x, sizeof(unsigned char), 1, sgi_fd ) ;
return x ;
}
static unsigned short readShort ()
{
unsigned short x ;
fread ( & x, sizeof(unsigned short), 1, sgi_fd ) ;
swab_short ( & x ) ;
return x ;
}
static unsigned int readInt ()
{
unsigned int x ;
fread ( & x, sizeof(unsigned int), 1, sgi_fd ) ;
swab_int ( & x ) ;
return x ;
}
static void getRow ( unsigned char *buf, int y, int z )
{
unsigned char pixel, count ;
if ( y >= sgi_ysize ) y = sgi_ysize - 1 ;
if ( z >= sgi_zsize ) z = sgi_zsize - 1 ;
fseek ( sgi_fd, sgi_start [ z * sgi_ysize + y ], SEEK_SET ) ;
if ( sgi_type == SGI_IMG_RLE )
{
unsigned char *tmpp = sgi_temp ;
unsigned char *bufp = buf ;
fread ( sgi_temp, 1, sgi_leng [ z * sgi_ysize + y ], sgi_fd ) ;
while ( 1 )
{
pixel = *tmpp++ ;
count = ( pixel & 0x7f ) ;
if ( count == 0 )
break ;
if ( pixel & 0x80 )
{
while ( count-- )
*bufp++ = *tmpp++ ;
}
else
{
pixel = *tmpp++ ;
while ( count-- )
*bufp++ = pixel ;
}
}
}
else
fread ( buf, 1, sgi_xsize, sgi_fd ) ;
}
static void getPlane ( unsigned char *buf, int z )
{
int y;
if ( sgi_fd == NULL )
return ;
if ( z >= sgi_zsize ) z = sgi_zsize - 1 ;
for ( y = 0 ; y < sgi_ysize ; y++ )
getRow ( & buf [ y * sgi_xsize ], y, z ) ;
}
static void getImage ( unsigned char *buf )
{
int y, z;
if ( sgi_fd == NULL )
return ;
for (y = 0 ; y < sgi_ysize ; y++ )
for (z = 0 ; z < sgi_zsize ; z++ )
getRow ( & buf [ ( z * sgi_ysize + y ) * sgi_xsize ], y, z ) ;
}
void load_sgi_texture ( char *fname )
{
int i, tablen, x, y, l, maxlen;
GLubyte *texels [ 20 ], *ptr ;
unsigned char *rbuf, *gbuf, *bbuf, *abuf;
int lev, map_level;
int j, x2, y2, c;
sgi_dim = 0 ;
sgi_start = NULL ;
sgi_leng = NULL ;
sgi_temp = NULL ;
sgi_swapped = 0 ;
sgi_fd = fopen ( fname, "rb" ) ;
if ( sgi_fd == NULL )
{
Log ("load_sgi_texture: Failed to open '%s' for reading.", fname ) ;
return ;
}
sgi_magic = readShort () ;
if ( sgi_magic != SGI_IMG_MAGIC && sgi_magic != SGI_IMG_SWABBED_MAGIC )
{
Log ("load_sgi_texture: %s: Unrecognised magic number 0x%04x", fname, sgi_magic ) ;
exit ( 1 ) ;
}
if ( sgi_magic == SGI_IMG_SWABBED_MAGIC )
{
sgi_swapped = 1 ;
swab_short ( & sgi_magic ) ;
}
sgi_type = readByte () ;
sgi_bpp = readByte () ;
sgi_dim = readShort () ;
/*
This is a backstop test - if for some reason the magic number isn't swabbed, this
test will still catch a swabbed file. Of course images with more than 256 dimensions
are not catered for :-)
*/
if ( sgi_dim > 255 )
{
Log ("load_sgi_texture: %s: Bad swabbing?!?", fname ) ;
sgi_swapped = ! sgi_swapped ;
swab_short ( & sgi_dim ) ;
sgi_magic = SGI_IMG_MAGIC ;
}
sgi_xsize = readShort () ;
sgi_ysize = readShort () ;
sgi_zsize = readShort () ;
sgi_min = readInt () ;
sgi_max = readInt () ;
readInt () ; /* Dummy field */
for (i = 0 ; i < 80 ; i++ )
readByte () ; /* Name field */
sgi_colormap = readInt () ;
for (i = 0 ; i < 404 ; i++ )
readByte () ; /* Dummy field */
makeConsistentHeader () ;
tablen = sgi_ysize * sgi_zsize ;
sgi_start = (unsigned int *) malloc (tablen*sizeof(unsigned int)) ;
sgi_leng = (int *) malloc (sizeof(int)*tablen) ;
if ( sgi_type == SGI_IMG_RLE )
{
fread ( sgi_start, sizeof ( unsigned int ), tablen, sgi_fd ) ;
fread ( sgi_leng , sizeof ( int ), tablen, sgi_fd ) ;
swab_int_array ( (int *) sgi_start, tablen ) ;
swab_int_array ( (int *) sgi_leng , tablen ) ;
maxlen = 0 ;
for (i = 0 ; i < tablen ; i++ )
if ( sgi_leng [ i ] > maxlen )
maxlen = sgi_leng [ i ] ;
sgi_temp = (unsigned char *) malloc (maxlen) ;
}
else
{
sgi_temp = NULL ;
for (i = 0 ; i < sgi_zsize ; i++ )
for (j = 0 ; j < sgi_ysize ; j++ )
{
sgi_start [ i * sgi_ysize + j ] = sgi_xsize * ( i * sgi_ysize + j ) + 512 ;
sgi_leng [ i * sgi_ysize + j ] = sgi_xsize ;
}
}
if ( sgi_zsize <= 0 || sgi_zsize > 4 )
{
Log ("load_sgi_texture: %s: Not an int/inta/rgb/rgba image?!?", fname ) ;
exit ( 1 ) ;
}
for (i = 0 ; i < 20 ; i++ )
texels [ 0 ] = NULL ;
texels [ 0 ] = (GLubyte *) malloc (sizeof(GLubyte)* sgi_xsize * sgi_ysize * sgi_zsize ) ;
ptr = texels [ 0 ] ;
rbuf = malloc (sgi_xsize);
gbuf = (sgi_zsize>1) ? malloc(sgi_xsize): (unsigned char *) NULL ;
bbuf = (sgi_zsize>2) ? malloc(sgi_xsize): (unsigned char *) NULL ;
abuf = (sgi_zsize>3) ? malloc(sgi_xsize): (unsigned char *) NULL ;
for ( y = 0 ; y < sgi_ysize ; y++ )
{
switch ( sgi_zsize )
{
case 1 :
getRow ( rbuf, y, 0 ) ;
for ( x = 0 ; x < sgi_xsize ; x++ )
*ptr++ = rbuf [ x ] ;
break ;
case 2 :
getRow ( rbuf, y, 0 ) ;
getRow ( gbuf, y, 1 ) ;
for (x = 0 ; x < sgi_xsize ; x++ )
{
*ptr++ = rbuf [ x ] ;
*ptr++ = gbuf [ x ] ;
}
break ;
case 3 :
getRow ( rbuf, y, 0 ) ;
getRow ( gbuf, y, 1 ) ;
getRow ( bbuf, y, 2 ) ;
for (x = 0 ; x < sgi_xsize ; x++ )
{
*ptr++ = rbuf [ x ] ;
*ptr++ = gbuf [ x ] ;
*ptr++ = bbuf [ x ] ;
}
break ;
case 4 :
getRow ( rbuf, y, 0 ) ;
getRow ( gbuf, y, 1 ) ;
getRow ( bbuf, y, 2 ) ;
getRow ( abuf, y, 3 ) ;
for (x = 0 ; x < sgi_xsize ; x++ )
{
*ptr++ = rbuf [ x ] ;
*ptr++ = gbuf [ x ] ;
*ptr++ = bbuf [ x ] ;
*ptr++ = abuf [ x ] ;
}
break ;
}
}
for ( lev = 0 ; (( sgi_xsize >> (lev+1) ) != 0 ||
( sgi_ysize >> (lev+1) ) != 0 ) ; lev++ )
{
/* Suffix '1' is the higher level map, suffix '2' is the lower level. */
int l1 = lev ;
int l2 = lev+1 ;
int w1 = sgi_xsize >> l1 ;
int h1 = sgi_ysize >> l1 ;
int w2 = sgi_xsize >> l2 ;
int h2 = sgi_ysize >> l2 ;
if ( w1 <= 0 ) w1 = 1 ;
if ( h1 <= 0 ) h1 = 1 ;
if ( w2 <= 0 ) w2 = 1 ;
if ( h2 <= 0 ) h2 = 1 ;
texels[l2] = (GLubyte *) malloc (sizeof(GLubyte)* w2 * h2 * sgi_zsize ) ;
for (x2 = 0 ; x2 < w2 ; x2++ )
for (y2 = 0 ; y2 < h2 ; y2++ )
for (c = 0 ; c < sgi_zsize ; c++ )
{
int x1 = x2 + x2 ;
int x1_1 = ( x1 + 1 ) % w1 ;
int y1 = y2 + y2 ;
int y1_1 = ( y1 + 1 ) % h1 ;
int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * sgi_zsize + c ] ;
int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * sgi_zsize + c ] ;
int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * sgi_zsize + c ] ;
int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * sgi_zsize + c ] ;
texels [ l2 ] [ (y2 * w2 + x2) * sgi_zsize + c ] =
( t1 + t2 + t3 + t4 ) / 4 ;
}
}
texels [ lev+1 ] = NULL ;
if (rbuf != NULL) free (rbuf) ;
if (gbuf != NULL) free (gbuf) ;
if (bbuf != NULL) free (bbuf) ;
if (abuf != NULL) free (abuf) ;
if ( ! ((sgi_xsize & (sgi_xsize-1))==0) ||
! ((sgi_ysize & (sgi_ysize-1))==0) )
{
Log ("load_sgi_texture: %s: Map is not a power-of-two in size!", fname ) ;
exit ( 1 ) ;
}
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ;
map_level = 0 ;
if ( sgi_xsize > 256 || sgi_ysize > 256 )
{
while ( sgi_xsize > 256 || sgi_ysize > 256 )
{
if (texels[0] != NULL) free (texels[0]) ;
sgi_xsize >>= 1 ;
sgi_ysize >>= 1 ;
for (l = 0 ; texels [ l ] != NULL ; l++ )
texels [ l ] = texels [ l+1 ] ;
}
}
for (i = 0 ; texels [ i ] != NULL ; i++ )
{
int w = sgi_xsize>>i ;
int h = sgi_ysize>>i ;
if ( w <= 0 ) w = 1 ;
if ( h <= 0 ) h = 1 ;
glTexImage2D ( GL_TEXTURE_2D,
map_level, sgi_zsize, w, h, 0 /* Border */,
(sgi_zsize==1)?GL_LUMINANCE:
(sgi_zsize==2)?GL_LUMINANCE_ALPHA:
(sgi_zsize==3)?GL_RGB:
GL_RGBA,
GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ;
map_level++ ;
}
fclose (sgi_fd);
}