[BACK]Return to pyramid.C CVS log [TXT][DIR] Up to [Development] / performer / src / sample / C / fakeclipfly

File: [Development] / performer / src / sample / C / fakeclipfly / pyramid.C (download)

Revision 1.1, Tue Nov 21 21:39:43 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1996, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 *
 */


#include <math.h>
#include <assert.h>
#include <Performer/pr/pfLinMath.h>
#include "pyramid.h"


#define FOR(i,n) for ((i) = (0); (i) < (n); ++(i))
#define INRANGE(foo,bar,baz) ((foo(bar))&&((bar)baz))
#define EXPAND2(v) (v)[0], (v)[1]
#define EXPAND3(v) (v)[0], (v)[1], (v)[2]

static float
non_retarded_fmodf(float num, float denom)
{
    float result = fmodf(num, denom);
    return result < 0 ? result + denom : result;
}

static int
GetQuadFromSeg3d(pfVec3 quad[4], float x0, float y0, float x1, float y1)
{
    if (x0 == x1 || y0 == y1) return 0; /* failure-- zero-width or zero-height*/
    assert(x0 < x1 && y0 < y1);
    quad[0].set(x0,y0,0);
    quad[1].set(x1,y0,0);
    quad[2].set(x1,y1,0);
    quad[3].set(x0,y1,0);
    return 1; /* success */
}
static inline int
GetQuadFromSeg2d(pfVec2 quad[4], float x0, float y0, float x1, float y1)
{
    if (x0 == x1 || y0 == y1) return 0; /* failure-- zero-width or zero-height*/
    assert(x0 < x1 && y0 < y1);
    quad[0].set(x0,y0);
    quad[1].set(x1,y0);
    quad[2].set(x1,y1);
    quad[3].set(x0,y1);
    return 1; /* success */
}

static inline void
offsetAndScale(int nquads, pfVec3 quads[][4], pfVec3 offset, float scale)
{
    int i, j, k;
    FOR (i, nquads)
	FOR (j, 4)
	    FOR (k, 3)
	    {
		quads[i][j][k] = quads[i][j][k]*scale + offset[k];
		assert(INRANGE(-1e6f <, quads[i][j][k], < 1e6f));
	    }
}

/* XXX for dbx brain damage */
#define N_valid_quads _N_valid_quads
#define N_invalid_quads _N_invalid_quads

/*
 * Get the vertex cordinates and texture coordinates
 * needed to draw a picture of the physical layout of
 * texture memory occupied by a given clip level.
 */
extern "C" void
GetClippedLevelCoords(
	const pfVec2 center,	/* in texture coords */
	int virtual_size,
	int level_size,
	int clip_size,
	float border,
	int do_roam,
	int do_wrap,
	const pfVec3 level_lower_left,
	pfVec3 valid_vcoords_quads[4][4],  /* returns at most 4 valid quads */
	pfVec2 valid_tcoords_quads[4][4],  /* returns at most 4 valid quads */
	pfVec3 invalid_vcoords_quads[8][4],/* returns at most 8 invalid quads */
	int *N_valid_quads,	      /* number of valid quads returned */
	int *N_invalid_quads	      /* number of invalid quads returned */
	)
{
    if (level_size <= clip_size)
    {
	/*
	 * It's a pyramid level.
	 * This is a very simple problem--
	 * there is no border and no roaming or wrapping ever needed;
	 * return 1 valid quad and no invalid ones
	 */
	*N_valid_quads = 1;
	*N_invalid_quads = 0;

	valid_vcoords_quads[0][0].set(0,0,0);
	valid_vcoords_quads[0][1].set(level_size,0,0);
	valid_vcoords_quads[0][2].set(level_size,level_size,0);
	valid_vcoords_quads[0][3].set(0,level_size,0);

	valid_tcoords_quads[0][0].set(0.,0.);
	valid_tcoords_quads[0][1].set(1.,0.);
	valid_tcoords_quads[0][2].set(1.,1.);
	valid_tcoords_quads[0][3].set(0.,1.);

	offsetAndScale(*N_valid_quads, valid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
	offsetAndScale(*N_invalid_quads, invalid_vcoords_quads, level_lower_left, 1.0f/virtual_size);

	return;
    }

    pfVec2 clip_radius2(clip_size/2, clip_size/2);
    pfVec2 border2(border, border);

    /*
     * Coordinates of the valid region w.r.t. the level in texels,
     * i.e. in the range 0..level_size
     */
    pfVec2 level_center = center * level_size;	/* center of valid window */
    level_center[0] = PF_CLAMP(level_center[0], clip_size/2, level_size-clip_size/2);
    level_center[1] = PF_CLAMP(level_center[1], clip_size/2, level_size-clip_size/2);
    pfVec2 level_clip_ll = level_center - clip_radius2;
    pfVec2 level_clip_ur = level_center + clip_radius2;
    pfVec2 level_valid_ll = level_clip_ll + border2;
    pfVec2 level_valid_ur = level_clip_ur - border2;

    if (border >= clip_size/2)
    {
	/*
	 * It's all border
	 */
	*N_valid_quads = 0;
	*N_invalid_quads = 1;
	invalid_vcoords_quads[0][0].set(0,0,0);
	invalid_vcoords_quads[0][1].set(clip_size,0,0);
	invalid_vcoords_quads[0][2].set(clip_size,clip_size,0);
	invalid_vcoords_quads[0][3].set(0,clip_size,0);
	if (do_roam)
	{
	    int j;
	    FOR (j, 4)
		invalid_vcoords_quads[0][j] +=
		    pfVec3(level_clip_ll[PF_X], level_clip_ll[PF_Y], 0);
	}
	offsetAndScale(*N_valid_quads, valid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
	offsetAndScale(*N_invalid_quads, invalid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
	return;
    }

    if (!do_wrap)
    {
	/*
	 * It's still pretty simple.
	 * Return one valid quad,
	 * and four invalid ones forming the border around it.
	 * XXX Could use tuning (get rid of the zero-area quads)
	 * but then again who really cares
	 */

	*N_valid_quads = 1;
	*N_invalid_quads = 4;

	valid_vcoords_quads[0][0].set(border, border, 0);
	valid_vcoords_quads[0][1].set(clip_size-border, border, 0);
	valid_vcoords_quads[0][2].set(clip_size-border, clip_size-border, 0);
	valid_vcoords_quads[0][3].set(border, clip_size-border,0);
	valid_tcoords_quads[0][0].set(level_valid_ll[0] / (float)level_size,
				      level_valid_ll[1] / (float)level_size);
	valid_tcoords_quads[0][1].set(level_valid_ur[0] / (float)level_size,
				      level_valid_ll[1] / (float)level_size);
	valid_tcoords_quads[0][2].set(level_valid_ur[0] / (float)level_size,
				      level_valid_ur[1] / (float)level_size);
	valid_tcoords_quads[0][3].set(level_valid_ll[0] / (float)level_size,
				      level_valid_ur[1] / (float)level_size);

	/* bottom */
	invalid_vcoords_quads[0][0].set(0,0,0);
	invalid_vcoords_quads[0][1].set(clip_size,0,0);
	invalid_vcoords_quads[0][2].set(clip_size-border, border, 0);
	invalid_vcoords_quads[0][3].set(border, border, 0);

	/* left */
	invalid_vcoords_quads[1][0].set(clip_size-border, border, 0);
	invalid_vcoords_quads[1][1].set(clip_size, 0, 0);
	invalid_vcoords_quads[1][2].set(clip_size, clip_size, 0);
	invalid_vcoords_quads[1][3].set(clip_size-border, clip_size-border, 0);

	/* top */
	invalid_vcoords_quads[2][0].set(border, clip_size-border, 0);
	invalid_vcoords_quads[2][1].set(clip_size-border, clip_size-border, 0);
	invalid_vcoords_quads[2][2].set(clip_size, clip_size, 0);
	invalid_vcoords_quads[2][3].set(0, clip_size, 0);

	/* right */
	invalid_vcoords_quads[3][0].set(0,0, 0);
	invalid_vcoords_quads[3][1].set(border, border, 0);
	invalid_vcoords_quads[3][2].set(border, clip_size-border, 0);
	invalid_vcoords_quads[3][3].set(0, clip_size, 0);

	/*
	 * If do_roam, return coords in space of the level;
	 * otherwise return coords in space of the little image we
	 * are drawing.  Units are texels of this level in any case.
	 */
	if (do_roam)
	{
	    int i, j;
	    FOR (i, *N_valid_quads)
		FOR (j, 4)
		    valid_vcoords_quads[i][j] +=
			pfVec3(level_clip_ll[PF_X], level_clip_ll[PF_Y], 0);
	    FOR (i, *N_invalid_quads)
		FOR (j, 4)
		    invalid_vcoords_quads[i][j] +=
			pfVec3(level_clip_ll[PF_X], level_clip_ll[PF_Y], 0);
	}

	offsetAndScale(*N_valid_quads, valid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
	offsetAndScale(*N_invalid_quads, invalid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
	return;
    }

    /*
     * Coordinates of the valid region w.r.t. this texture piece in texels,
     * i.e. wrapped to the range 0..clip_size
     */
    pfVec2 clip_valid_ll(non_retarded_fmodf(level_valid_ll[PF_X], clip_size),
			 non_retarded_fmodf(level_valid_ll[PF_Y], clip_size));
    pfVec2 clip_valid_ur(non_retarded_fmodf(level_valid_ur[PF_X], clip_size),
			 non_retarded_fmodf(level_valid_ur[PF_Y], clip_size));
    if (border == 0)
    {
	/* don't risk them coming out a little different... it
	   was too much headache to do approximate equality testing. */
	clip_valid_ur = clip_valid_ll;
    }

    if (clip_valid_ur[PF_X] == 0)
	clip_valid_ur[PF_X] = clip_size;
    if (clip_valid_ur[PF_Y] == 0)
	clip_valid_ur[PF_Y] = clip_size;

    pfVec2 level_wrap_pos(0,0);
    /* XXX slow uningenious way... */
    while (level_wrap_pos[PF_X] < level_clip_ll[PF_X])
	level_wrap_pos[PF_X] += clip_size;
    while (level_wrap_pos[PF_Y] < level_clip_ll[PF_Y])
	level_wrap_pos[PF_Y] += clip_size;

    /*
     * The problem is separable.  First solve the one-dimensional
     * problem in x and in y...
     * (coordinates are all w.r.t. this texture piece in texels)
     */
    pfVec2 valid_vcoords_segs[2][2];	/* 2 segments, begin&end */
    pfVec2 valid_tcoords_segs[2][2];	/* tex coords of those segments */
    pfVec2 invalid_vcoords_segs[2][2];	/* 2 segments, begin&end */

    int n_valid_segs[2]; /* number of valid segs in each coord direction */
    int n_invalid_segs[2]; /* number of invalid segs in each coord direction */

    for (int ax = PF_X; ax <= PF_Y; ++ax)
    {
	n_valid_segs[ax] = n_invalid_segs[ax] = 0;
/* XXX this problem will probably go away when we round borders denominators to
   powers of two or something */
#define LT(a,b,eps) ((b)-(a)>(eps))
	if (LT(clip_valid_ll[ax], clip_valid_ur[ax], level_size*1e-4))
	{
	    /*
	     * The wrap position in coordinate ax is in the invalid part,
	     * so the valid part is contiguous.
	     */
	    valid_vcoords_segs[0][0][ax] = clip_valid_ll[ax];	/* begin seg */
	    valid_vcoords_segs[0][1][ax] = clip_valid_ur[ax];	/* end seg */
	    valid_tcoords_segs[0][0][ax] = level_valid_ll[ax]/(float)level_size;
	    valid_tcoords_segs[0][1][ax] = level_valid_ur[ax]/(float)level_size;
	    n_valid_segs[ax] = 1;

	    invalid_vcoords_segs[0][0][ax] = clip_valid_ur[ax]; /* begin seg 0*/
	    invalid_vcoords_segs[0][1][ax] = clip_size;		/* end seg 0 */
	    invalid_vcoords_segs[1][0][ax] = 0;			/* begin seg 1*/
	    invalid_vcoords_segs[1][1][ax] = clip_valid_ll[ax]; /* end seg 1 */
	    n_invalid_segs[ax] = 2;			
	}
	else
	{
	    /*
	     * The wrap position in coordinate ax is in the valid part,
	     * so the invalid part is contiguous
	     */
	    valid_vcoords_segs[0][0][ax] = clip_valid_ll[ax];	/* begin seg 0*/
	    valid_vcoords_segs[0][1][ax] = clip_size;		/* end seg 0 */
	    valid_vcoords_segs[1][0][ax] = 0;			/* begin seg 1*/
	    valid_vcoords_segs[1][1][ax] = clip_valid_ur[ax];	/* end seg 1 */

	    valid_tcoords_segs[0][0][ax] = level_valid_ll[ax]/(float)level_size;
	    valid_tcoords_segs[0][1][ax] = level_wrap_pos[ax]/(float)level_size;
	    valid_tcoords_segs[1][0][ax] = level_wrap_pos[ax]/(float)level_size;
	    valid_tcoords_segs[1][1][ax] = level_valid_ur[ax]/(float)level_size;

	    n_valid_segs[ax] = 2;

	    invalid_vcoords_segs[0][0][ax] = clip_valid_ur[ax]; /* begin seg */
	    invalid_vcoords_segs[0][1][ax] = clip_valid_ll[ax]; /* end seg */
	    n_invalid_segs[ax] = 1;
	}
    }

    /*
     * Now cross each segment in y with each segment in x
     * to get a whole mess of quads.
     * A quad is valid iff both of its factor segments is valid.
     */
    int n_valid_quads = 0;
    int n_invalid_quads = 0;
    int i, j;
    /* valid x and y -> valid quads */
    FOR (i, n_valid_segs[PF_X])
    FOR (j, n_valid_segs[PF_Y])
    {
	assert(n_valid_quads < 4);
	int n0 = 
	 GetQuadFromSeg3d(valid_vcoords_quads[n_valid_quads],
	    valid_vcoords_segs[i][0][PF_X], valid_vcoords_segs[j][0][PF_Y],
	    valid_vcoords_segs[i][1][PF_X], valid_vcoords_segs[j][1][PF_Y]);
	int n1 =
	 GetQuadFromSeg2d(valid_tcoords_quads[n_valid_quads],
	    valid_tcoords_segs[i][0][PF_X], valid_tcoords_segs[j][0][PF_Y],
	    valid_tcoords_segs[i][1][PF_X], valid_tcoords_segs[j][1][PF_Y]);
	assert(n0 == n1);
	n_valid_quads += n0;
    }
    /* valid x, invalid y -> invalid quads */
    FOR (i, n_valid_segs[PF_X])
    FOR (j, n_invalid_segs[PF_Y])
    {
	assert(n_invalid_quads < 8);
	n_invalid_quads +=
	  GetQuadFromSeg3d(invalid_vcoords_quads[n_invalid_quads], valid_vcoords_segs[i][0][PF_X], invalid_vcoords_segs[j][0][PF_Y],
	    valid_vcoords_segs[i][1][PF_X], invalid_vcoords_segs[j][1][PF_Y]);
    }
    /* invalid x, valid y -> invalid quads */
    FOR (i, n_invalid_segs[PF_X])
    FOR (j, n_valid_segs[PF_Y])
    {
	assert(n_invalid_quads < 8);
	n_invalid_quads +=
	  GetQuadFromSeg3d(invalid_vcoords_quads[n_invalid_quads],
	    invalid_vcoords_segs[i][0][PF_X], valid_vcoords_segs[j][0][PF_Y],
	    invalid_vcoords_segs[i][1][PF_X], valid_vcoords_segs[j][1][PF_Y]);
    }
    /* invalid x and y -> invalid quads */
    FOR (i, n_invalid_segs[PF_X])
    FOR (j, n_invalid_segs[PF_Y])
    {
	assert(n_invalid_quads < 8);
	n_invalid_quads +=
	  GetQuadFromSeg3d(invalid_vcoords_quads[n_invalid_quads],
	    invalid_vcoords_segs[i][0][PF_X], invalid_vcoords_segs[j][0][PF_Y],
	    invalid_vcoords_segs[i][1][PF_X], invalid_vcoords_segs[j][1][PF_Y]);
    }

    if (border == 0)
    {
	assert(n_valid_quads == 4
	    || n_valid_quads == 2
	    || n_valid_quads == 1);
	if (n_invalid_quads != 0)
	    fprintf(stderr, "UH OH... n_invalid_quads = %d\n", n_invalid_quads);
	assert(n_invalid_quads == 0);
    }
    else if (clip_size-2*border == 0) /* no valid part */
    {
	assert(n_valid_quads == 0);
	assert(n_invalid_quads == 1
	    || n_invalid_quads == 4);
    }
    else
    {
	assert(n_valid_quads == 4   /* wrap inside valid win in both dirs*/
	    || n_valid_quads == 2   /* wrap inside valid win in one dir */
	    || n_valid_quads == 1); /* wrap not inside valid win in both */

	assert(n_invalid_quads == 8
	    || n_invalid_quads == 7
	    || n_invalid_quads == 5
	    || n_invalid_quads == 4
	    || n_invalid_quads == 3); /* wrap right at the corner of valid */
    }

    *N_valid_quads = n_valid_quads;
    *N_invalid_quads = n_invalid_quads;

    /*
     * If do_roam, return coords in space of the level;
     * otherwise return coords in space of the clip texture memory.
     * Units are texels of this level in any case.
     */
    if (do_roam)
    {
	FOR (i, n_valid_quads)
	    FOR (j, 4)
		valid_vcoords_quads[i][j] +=
		    pfVec3(level_clip_ll[PF_X], level_clip_ll[PF_Y], 0);
	FOR (i, n_invalid_quads)
	    FOR (j, 4)
		invalid_vcoords_quads[i][j] +=
		    pfVec3(level_clip_ll[PF_X], level_clip_ll[PF_Y], 0);
    }
    offsetAndScale(*N_valid_quads, valid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
    offsetAndScale(*N_invalid_quads, invalid_vcoords_quads, level_lower_left, 1.0f/virtual_size);
}

#ifdef MAIN /* little test program */

#include <stdio.h>
#include <stdlib.h>

main(int argc, char **argv)
{
    if (argc != 9)
    {
	fprintf(stderr, "Usage: %s centerx centery level_size clip_size border do_roam do_wrap z\n",
		argv[0]);
	exit(1);
    }

    /* Inputs... */
    pfVec2 center(atof(argv[1]), atof(argv[2]));
    int level_size = atoi(argv[3]);
    int clip_size = atoi(argv[4]);
    int border = atoi(argv[5]);
    int do_roam = atoi(argv[6]);
    int do_wrap = atoi(argv[7]);
    float z = atof(argv[8]);

#define print(x) printf("%s = %d\n", #x, x)
#define prfloat(x) printf("%s = %f\n", #x, x)

    printf("===============================");
    prfloat(center[0]);
    prfloat(center[1]);
    print(level_size);
    print(clip_size);
    print(border);
    print(do_roam);
    print(do_wrap);
    prfloat(z);

    /* Outputs... */
    pfVec3 valid_vcoords[4][4];
    pfVec2 valid_tcoords[4][4];
    pfVec3 invalid_vcoords[8][4];
    int n_valid_quads;
    int n_invalid_quads;

    GetClippedLevelCoords(
        center, level_size, clip_size, border, do_roam, do_wrap,pfVec3(0,0,z),
	valid_vcoords, valid_tcoords, invalid_vcoords,
	&n_valid_quads, &n_invalid_quads);

    /* Print out results... */

    int i;
    printf("    %d valid quads:\n", n_valid_quads);
    FOR (i, n_valid_quads)
    {
	printf("\t%2d vc: %g,%g,%g .. %g,%g,%g\n",
		i,
		EXPAND3(valid_vcoords[i][0]),
		EXPAND3(valid_vcoords[i][2]));
	printf("\t%2d tc: %g,%g .. %g,%g\n",
		i,
		EXPAND2(valid_tcoords[i][0]),
		EXPAND2(valid_tcoords[i][2]));
	assert(valid_vcoords[i][1] == pfVec3(valid_vcoords[i][2][PF_X],
					     valid_vcoords[i][0][PF_Y], z));
	assert(valid_vcoords[i][3] == pfVec3(valid_vcoords[i][0][PF_X],
					     valid_vcoords[i][2][PF_Y], z));
	assert(valid_tcoords[i][1] == pfVec2(valid_tcoords[i][2][PF_X],
					     valid_tcoords[i][0][PF_Y]));
	assert(valid_tcoords[i][3] == pfVec2(valid_tcoords[i][0][PF_X],
					     valid_tcoords[i][2][PF_Y]));
    }
    printf("    %d invalid quads:\n", n_invalid_quads);
    FOR (i, n_invalid_quads)
    {
	printf("\t%2d vc: %g,%g,%g %g,%g,%g",
		i,
		EXPAND3(invalid_vcoords[i][0]),
		EXPAND3(invalid_vcoords[i][2]));
	/*
	XXX should have some kind of assertion here,
	but this one fails because stuff is not square
	assert(invalid_vcoords[i][1] == pfVec3(invalid_vcoords[i][2][PF_X],
					       invalid_vcoords[i][0][PF_Y], z));
	assert(invalid_vcoords[i][3] == pfVec3(invalid_vcoords[i][0][PF_X],
					       invalid_vcoords[i][2][PF_Y], z));
	*/
	if (!(invalid_vcoords[i][1] == pfVec3(invalid_vcoords[i][2][PF_X], invalid_vcoords[i][0][PF_Y], z) && invalid_vcoords[i][3] == pfVec3(invalid_vcoords[i][0][PF_X], invalid_vcoords[i][2][PF_Y], z)))
	    printf(" (not rectangular)");
	printf("\n");
    }

    return 0;
}
#endif