home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Encyclopedia of Graphics File Formats Companion
/
GFF_CD.ISO
/
formats
/
off
/
code
/
packobj.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-20
|
9KB
|
405 lines
/*
*
* Description
* Pack an OFF object in memory
* ``Packing'' means to rearrange the data structure so common vertices
* are shared. Since in typical objects every vertex
* is shared by three or more polygons, this can often result in a
* substantial reduction in storage and increase in rendering
* efficiency (because shared vertices need only be transformed once).
*
* Input
* Obj Pointer to object structure in which data is stored.
* Output
* obj Same as input pointer, but underlying object structure
* is modified.
*
* Diagnostics
* None. (If unsuccessful, the object is returned unchanged.)
*
* Author
* Allen Akin
* Digital Equipment Corp.
* Workstation Systems Engineering
* Palo Alto, CA
*
* History
* 20-Feb-87 Created
* 11-Oct-89 Ability to pack normals was removed when conventions
* for dealing with normals were changed.
*
* Notes
* In order to do this properly, one should really treat all of the
* attributes at a vertex simultaneously - a vertex is redundant only
* if all of the attributes at that vertex are redundant (vertex
* coordinate, color, vertex normal, etc.). This routine will just
* pack the geometry property. It is currently up to the application to
* regenerate per-vertex colors or normals.
*/
#include <stdio.h>
#include "off.h"
typedef struct {float x, y, z;} OFFxyz;
static qsort();
static turtlesort();
static int precedes();
static int follows();
static int equals();
OFFPackObj(obj)
OFFObjDesc *obj;
{
register OFFProperty *prop;
for (prop = obj->FirstProp; prop; prop = prop->NextProp)
{
if (strcmp(prop->PropName, "geometry") == 0
&& strcmp(prop->DataFormat, "fff") == 0)
OFFPackProperty(prop);
}
}
OFFPackProperty(prop)
OFFProperty *prop;
{
register int newI;
register int *vlist;
register OFFxyz *newVertices;
register int *ip;
register int *indexMap;
register int oldI;
int nVertices;
int nCounts;
int nIndices;
OFFxyz *oldVertices;
short *oldCounts;
short *oldIndices;
char *newData;
OFFxyz *xyzp;
short *sp;
int vertex;
char *malloc();
ip = (int *) prop->PropData;
nVertices = *ip++;
nCounts = *ip++;
nIndices = *ip++;
oldVertices = (OFFxyz *) ip;
oldCounts = (short *) (oldVertices + nVertices);
oldIndices = oldCounts + nCounts;
newVertices = NULL;
vlist = indexMap = NULL;
if (!(newVertices = (OFFxyz *) malloc(nVertices * sizeof(OFFxyz)))
|| !(vlist = (int *) malloc(nVertices * sizeof(int)))
|| !(indexMap = (int *) malloc(nVertices * sizeof(int))) )
{
fprintf(stderr, "OFFPackProperty: insufficient memory\n");
if (newVertices)
free((char *) newVertices);
if (vlist)
free((char *) vlist);
if (indexMap)
free((char *) indexMap);
return;
}
/* Set up indices of original vertices: */
for (newI = 0; newI < nVertices; ++newI)
vlist[newI] = newI;
/* Sort original vertices by swapping indices in vlist: */
qsort(oldVertices, vlist, vlist + (nVertices - 1));
/*
* The following code does two things:
* (1) Copies vertices from oldVertices to newVertices,
* throwing away duplicates.
* (2) Builds a mapping from old vertex indices to new
* vertex indices.
* Because vlist gives us ordered access to oldVertices, it's
* efficient to do these operations in parallel.
*/
newI = -1;
for (vertex = 0, oldI = vlist[vertex]; vertex < nVertices; )
{
/*
* At this point, we have a new unique vertex. Store it
* in the next position in the newVertices array, and
* update the indexMap to show how the old vertex index
* maps into the new one.
*/
newVertices[++newI] = oldVertices[oldI];
indexMap[oldI] = newI;
/*
* Now scan through succeeding oldVertices (continuing in
* sorted order). If the old vertex equals the current new
* vertex, discard it and make the old vertex index map to
* the new vertex index. If the old vertex doesn't equal
* the current new vertex, go back to the outer loop and
* store the new vertex.
*/
for (++vertex; vertex < nVertices; ++vertex)
{
oldI = vlist[vertex];
if (!equals(oldVertices + oldI, newVertices + newI))
break;
indexMap[oldI] = newI;
}
}
/*
* newI contains the index of the last-used element of newVertices.
* Use this information plus the old data sizes to compute the size
* of the new data block, and allocate it.
*/
++newI;
if (!(newData = malloc(
3 * sizeof(int)
+ newI * sizeof(OFFxyz)
+ nCounts * sizeof(short)
+ nIndices * sizeof(short) )))
{
free((char *) newVertices);
free((char *) vlist);
free((char *) indexMap);
fprintf(stderr, "OFFPackProperty: insufficient memory\n");
return;
}
/*
* Stuff the new values into the new data record, mapping old
* vertex indices into new ones as we go.
*/
ip = (int *) newData;
*ip++ = newI;
*ip++ = nCounts;
*ip++ = nIndices;
bcopy((char *) newVertices, (char *) ip, newI * sizeof(OFFxyz));
xyzp = (OFFxyz *) ip;
xyzp += newI;
sp = (short *) xyzp;
bcopy((char *) oldCounts, (char *) sp, nCounts * sizeof(int));
sp += nCounts;
while (--nIndices >= 0)
*sp++ = indexMap[*oldIndices++ - 1] + 1;
/*
* Throw away the old and auxiliary data structures, and store the
* new ones.
*/
free(prop->PropData);
prop->PropData = newData;
free((char *) newVertices);
free((char *) vlist);
free((char *) indexMap);
}
#define PRECEDES(a,b) precedes(&(a), &(b))
#define FOLLOWS(a,b) follows(&(a), &(b))
#define QTHRESH 10 /* threshold for linear sort versus quicksort */
static turtlesort(v, first, last)
OFFxyz *v;
register int *first;
register int *last;
{
int temp;
OFFxyz min_key;
OFFxyz cur_key;
register int *current;
for (; first < last; ++first)
{
min_key = v[*first];
for (current = first + 1; current <= last; ++current)
{
cur_key = v[*current];
if (PRECEDES(cur_key, min_key))
{
min_key = cur_key;
temp = *first;
*first = *current;
*current = temp;
}
}
}
}
static qsort(v, first, last)
OFFxyz *v;
register int *first;
register int *last;
{
OFFxyz pivot_key;
int *pivot;
register int *high;
register int *low;
int temp;
reenter:
if (first + QTHRESH >= last)
{
turtlesort(v, first, last);
return;
}
/* Find median of first, middle, and last keys: */
{
int *mid;
OFFxyz f, m, l;
mid = first + ((last - first) >> 1);
f = v[*first];
m = v[*mid];
l = v[*last];
if (PRECEDES(f, l))
{
if (PRECEDES(f, m))
pivot = (PRECEDES(m, l))? mid: last;
else
pivot = first;
}
else
{
if (FOLLOWS(f, m))
pivot = (PRECEDES(m, l))? last: mid;
else
pivot = first;
}
}
/* Swap the pivot element into sentinel position at the beginning: */
pivot_key = v[*pivot];
temp = *first;
*first = *pivot;
*pivot = temp;
/* Partition the array around the pivot: */
low = first;
high = last;
while (low < high)
{
while (FOLLOWS(v[*high], pivot_key))
--high;
while (low < high && !(FOLLOWS(v[*low], pivot_key)))
++low;
if (low < high)
{
temp = *low;
*low = *high;
*high = temp;
}
}
/* Swap the pivot into its proper place in the middle: */
temp = *first;
*first = *high;
*high = temp;
/*
* Recurse to handle subarrays. Use direct recursion on shortest
* subarray, then unwind tail recursion to handle second subarray.
* (This minimizes worst-case stack requirements.) (Note: the
* casts below eliminate two divisions.)
*/
if ((char *)high - (char *)first < (char *)last - (char *)high)
{
qsort(v, first, high - 1);
first = high + 1;
goto reenter;
}
else
{
qsort(v, high + 1, last);
last = high - 1;
goto reenter;
}
}
static int precedes(a, b)
register OFFxyz *a;
register OFFxyz *b;
{
if (a->x < b->x)
return 1;
if (a->x > b->x)
return 0;
if (a->y < b->y)
return 1;
if (a->y > b->y)
return 0;
return a->z < b->z;
}
static int follows(a, b)
register OFFxyz *a;
register OFFxyz *b;
{
if (a->x > b->x)
return 1;
if (a->x < b->x)
return 0;
if (a->y > b->y)
return 1;
if (a->y < b->y)
return 0;
return a->z > b->z;
}
static int equals(a, b)
register OFFxyz *a;
register OFFxyz *b;
{
return a->x == b->x && a->y == b->y && a->z == b->z;
}