[BACK]Return to smoke.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfutil

File: [Development] / performer / src / lib / libpfutil / smoke.c (download)

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

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

/*
 * Copyright 1993, 1994, 1995, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * UNPUBLISHED -- Rights reserved under the copyright laws of the United
 * States.   Use of a copyright notice is precautionary only and does not
 * imply publication or disclosure.
 *
 * U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to restrictions
 * as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
 * in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
 * in similar or successor clauses in the FAR, or the DOD or NASA FAR
 * Supplement.  Contractor/manufacturer is Silicon Graphics, Inc.,
 * 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
 *
 * THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
 * INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
 * DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
 * PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
 * GRAPHICS, INC.
 */

#include <math.h>

#include <Performer/pf.h>
#include <Performer/pfutil.h>

#define MAX_SMOKE	16
#define MAX_PUFFS	32
#define MAX_RAND	256	/* Must be power of 2 */

#define RAND_VEC3	RandVec3[pfuRandomLong() & (MAX_RAND - 1)]

#define MAXSMOKES	256

typedef struct _pfuPuff
{
    float	t;
    float	transp;
    double	startTime;

    float	radius;
    pfVec3	origin;

    pfMatrix	rotMat;

    pfVec3	coords[4];
    pfVec3	tcoords[4];

    float	speed;
    pfVec3	direction;	/* Direction of travel with random factor */

} pfuPuff;

struct _pfuSmoke
{
    pfVec3	bgnColor, deltaColor;

    float	dissipation;	/* Dissipation rate in seconds */
    float	density;
    float	puffInterval;
    float	duration;

    double	startTime;
    double	prevTime;
    double	lastPuffTime;	

    int	type;
    int	mode;
    float	frequency;

    pfVec3	direction;	/* Direction of travel */
    float	speed;
    float	turbulence;

    pfVec3	origin;
    float	radius;
    float	expansion;
    float	offset;		
    
    pfTexture	*tex;

    pfuPuff	*puffs[MAX_PUFFS];
    int	startPuff;
    int	numPuffs;

};

static void	initPuff(pfuPuff *puff, pfuSmoke *smoke);
#if 0
static void	getRandVec3(pfVec3 vec);
#endif
static void	drawPuff(pfuPuff *puff, pfuSmoke *smoke, pfVec3 eye);
static void	drawSmoke(pfuSmoke *smoke, pfVec3 eye);

static int	InitRand = 0;
static pfVec3	RandVec3[MAX_RAND], smokeDir;

static int	*smokeCount;
static pfuSmoke	**smokeList;

static pfTexture *smokeTex, *fireTex;
static pfTexEnv  *smokeTEnv;


	/*----------------------------------------------------------*/

#if 0
#define NSQR 	512		/* must be a power of 2 */
#define NSQR1F	511.0f		/* float version of NSQR-1 */

#define SQR_LOOKUP(_x)  	(sqrArr[((int)(_x))&(NSQR-1)])

static float sqrArr[NSQR];

static void
initSqrTable(void)
{
    int 	i;
    float 	delta ;

    delta = 1.0f / NSQR;
    for (i = 0 ; i < NSQR ; i++)
        sqrArr[i] = pfSqrt((i+0.5f)*delta);
}
#endif

#if 0
static void
getRandVec3(pfVec3 vec)
{
    int	i = pfuRandomLong() & (MAX_RAND - 1);

    PFCOPY_VEC3(vec, RandVec3[i]);
}
#endif

static void
initPuff(pfuPuff *puff, pfuSmoke *smoke)
{
    float 	*rv, r;
    pfVec3	offset;
    pfMatrix	mat;
    int	i;

    if(InitRand == 0)
    {
	for(i=0; i<MAX_RAND; i++)
	{
	    RandVec3[i][0] = 2.0f*pfuRandomFloat() - 1.0f;
	    RandVec3[i][1] = 2.0f*pfuRandomFloat() - 1.0f;
	    RandVec3[i][2] = 2.0f*pfuRandomFloat() - 1.0f;

	    pfNormalizeVec3(RandVec3[i]);
	}

	InitRand = 1;
    }

    puff->radius = smoke->radius;
    pfCopyVec3(puff->origin, smoke->origin);
    puff->transp = 0.0f;
    puff->startTime = smoke->lastPuffTime;

    /* Add random velocity modifier */
    if(smoke->type != PFUSMOKE_MISSLE)
    {
        rv = RAND_VEC3;
    	pfAddScaledVec3(puff->direction, smoke->direction, .15f, rv);
	pfScaleVec3(offset, smoke->expansion * smoke->radius * .05f, rv);
	PFADD_VEC3(puff->origin, puff->origin, offset);
    }
    else
    	PFCOPY_VEC3(puff->direction, smoke->direction);

    r = (pfuRandomFloat() - .5f) * 4.0f * smoke->turbulence;

    pfMakeTransMat(puff->rotMat, -0.5f, -0.5f, 0.0f);
    pfMakeRotMat(mat, r, 0.0f, 0.0f, 1.0f);
    pfPostMultMat(puff->rotMat, mat);
    pfMakeTransMat(mat, 0.5f, 0.5f, 0.0f);
    pfPostMultMat(puff->rotMat, mat);

    r = 1.0f + (pfuRandomFloat() - 0.5f) * .1f;

    puff->speed = r * smoke->speed;
}

void
pfuInitSmokes(void)
{
    smokeList = (pfuSmoke**)pfCalloc(1, sizeof(pfuSmoke*) * MAXSMOKES, pfGetSharedArena());
    smokeCount = (int*)pfCalloc(1, sizeof(int), pfGetSharedArena());

    smokeDir[0] = 0.1f;
    smokeDir[1] = 0.3f;
    smokeDir[2] = 1.0f;

    pfNormalizeVec3(smokeDir);

    smokeTex = pfNewTex(pfGetSharedArena());
    pfLoadTexFile(smokeTex, "smoke.tex");
    pfTexRepeat(smokeTex, PFTEX_WRAP, PFTEX_CLAMP);
    fireTex = pfNewTex(pfGetSharedArena());
    pfLoadTexFile(fireTex, "fire.tex");
    pfTexRepeat(fireTex, PFTEX_WRAP, PFTEX_CLAMP);

    smokeTEnv = pfNewTEnv(pfGetSharedArena());
}


pfuSmoke*
pfuNewSmoke(void)
{
    void	*arena = pfGetSharedArena();
    int	i;
    pfuSmoke 	*smoke;

    smoke = (pfuSmoke*)pfMalloc(sizeof(pfuSmoke), arena);

    smoke->radius = 1.0f;
    pfSetVec3(smoke->origin, 0.0f, 0.0f, 0.0f);
    pfCopyVec3(smoke->direction, smokeDir);
    smoke->speed = .1f;	
    smoke->turbulence = 1.0f;
    smoke->mode = PFUSMOKE_STOP;
    smoke->type = PFUSMOKE_SMOKE;
    smoke->startPuff = smoke->numPuffs = 0;
    smoke->prevTime = -1.0f;
    smoke->startTime = -1.0f;
    smoke->tex = NULL;
    smoke->duration = -1.0f;
    smoke->lastPuffTime = -1.;

    pfuSmokeType(smoke, smoke->type);

    for(i=0; i<MAX_PUFFS; i++)
    {
	int	j, r;

	static pfVec2 tc[4][4] = {
		{{0.0f, 0.0f},
		{1.0f, 0.0f},
		{1.0f, 1.0f},
		{0.0f, 1.0f}},

		{{1.0f, 0.0f},
		{0.0f, 0.0f},
		{0.0f, 1.0f},
		{1.0f, 1.0f}},

		{{0.0f, 1.0f},
		{1.0f, 1.0f},
		{1.0f, 0.0f},
		{0.0f, 0.0f}},

		{{0.0f, 0.0f},
		{1.0f, 0.0f},
		{1.0f, 1.0f},
		{0.0f, 1.0f}}
	}; 

	initPuff(smoke->puffs[i] = (pfuPuff*)pfMalloc(sizeof(pfuPuff), arena), smoke);

	r = (int)(pfuRandomLong() & 0x3);

	for(j=0; j<4; j++)
	{
	    pfCopyVec2(smoke->puffs[i]->tcoords[j], tc[r][j]);
	    smoke->puffs[i]->tcoords[j][2] = 0.0f;
	}
    }
    if(*smokeCount >= MAXSMOKES)
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, "Too many smokes");
	return smokeList[*smokeCount-1];
    }
    else
    {
	smokeList[*smokeCount] = smoke;
	*smokeCount = *smokeCount + 1;
    }

    return smoke;
}

void
pfuSmokeType(pfuSmoke *smoke, int type)
{
    static pfVec3	wht = {1.0f, 1.0f, 1.0f};
    static pfVec3	blk = {.01f, .01f, .01f};
    static pfVec3	brn = {.2f, .1f, .05f};

    smoke->type = type;

    switch(type) {
	case PFUSMOKE_MISSLE:
	    smoke->speed = 32.3f;
    	    pfuSmokeDensity(smoke, 0.52f, .05f, .17f);
    	    pfuSmokeColor(smoke, wht, blk);
    	    pfuSmokeTex(smoke, fireTex);
	    break;
	case PFUSMOKE_EXPLOSION:
	    smoke->speed = 0.0f;
    	    pfuSmokeDensity(smoke, 1.0f, .2f, 4.0f);
    	    pfuSmokeDuration(smoke, .2f);
    	    pfuSmokeColor(smoke, wht, wht);
    	    pfuSmokeTex(smoke, fireTex);
	    break;
	case PFUSMOKE_FIRE:
	    smoke->speed = 1.73f;
    	    pfuSmokeDensity(smoke, 0.11f, .32f, 1.13f);
    	    pfuSmokeColor(smoke, wht, blk);
    	    pfuSmokeTex(smoke, fireTex);
	    break;
	case PFUSMOKE_SMOKE:
	    smoke->speed = 1.73f;
    	    pfuSmokeDensity(smoke, 0.33f, 2.42f, 1.83f);
    	    pfuSmokeColor(smoke, wht, blk);
    	    pfuSmokeTex(smoke, smokeTex);
	    break;
	case PFUSMOKE_DUST:
	    smoke->speed = .8f;
    	    pfuSmokeDensity(smoke, 0.1f, 2.0f, 2.0f);
    	    pfuSmokeColor(smoke, brn, brn);
    	    pfuSmokeTex(smoke, smokeTex);
	    break;
	default:
	    smoke->speed = 1.73f;
    	    pfuSmokeDensity(smoke, 0.33f, 2.42f, 1.83f);
    	    pfuSmokeColor(smoke, wht, blk);
    	    pfuSmokeTex(smoke, smokeTex);
	    break;
    }
}

void
pfuSmokeOrigin(pfuSmoke *smoke, pfVec3 origin, float radius)
{
    pfCopyVec3(smoke->origin, origin);
    smoke->radius = radius;
}

void
pfuSmokeMode(pfuSmoke *smoke, int mode)
{
    smoke->mode = mode;
    if (smoke->mode == PFUSMOKE_START)
    {
	smoke->startTime = -1.;
	smoke->lastPuffTime = -1.;
    }
}

void
pfuSmokeDuration(pfuSmoke *smoke, float duration)
{
    smoke->duration = duration;
}    

void
pfuSmokeDir(pfuSmoke *smoke, pfVec3 dir) 
{
    pfCopyVec3(smoke->direction, dir);
}
    
void
pfuSmokeVelocity(pfuSmoke *smoke, float turbulence, float speed)
{
    smoke->speed = speed;
    smoke->turbulence = turbulence;
}

void
pfuGetSmokeVelocity(pfuSmoke *smoke, float *turbulence, float *speed)
{
    *speed = smoke->speed;
    *turbulence = smoke->turbulence;
}

void
pfuSmokeColor(pfuSmoke *smoke, pfVec3 bgn, pfVec3 end)
{
    pfCopyVec3(smoke->bgnColor, bgn);
    pfSubVec3(smoke->deltaColor, end, smoke->bgnColor);
}

void
pfuSmokeTex(pfuSmoke *smoke, pfTexture *tex)
{
    smoke->tex = tex;
}


void
pfuSmokeDensity(pfuSmoke *smoke, float dens, float diss, float expansion)
{
    if (dens <= 0.0f || diss <= 0.0f || expansion <= 0.0f)
	return;
    smoke->density = dens;
    smoke->dissipation = diss;
    smoke->puffInterval = diss / (float)MAX_PUFFS / dens; 
    smoke->expansion = expansion;
}    

void
pfuGetSmokeDensity(pfuSmoke *smoke, float *dens, float *diss, float *expansion)
{
    *dens = smoke->density;
    *diss = smoke->dissipation;
    *expansion = smoke->expansion;
}    


void
pfuDrawSmokes(pfVec3 eye)
{
    int	i, n;

    pfPushState();
    pfBasicState();
    pfEnable(PFEN_TEXTURE); 
    pfApplyTEnv(smokeTEnv);
    pfAlphaFunc((4.0f/255.0f), PFAF_GREATER);
    pfTransparency(PFTR_HIGH_QUALITY | PFTR_NO_OCCLUDE);
/*      zwritemask(0);
  blendfunction(BF_SA, BF_MSA);*/

    n = *smokeCount;
    for(i=0; i<n; i++)
        if(smokeList[i]->mode != PFUSMOKE_STOP)
	    drawSmoke(smokeList[i], eye);

/*    blendfunction(BF_ONE, BF_ZERO);
zwritemask(0xffffffff);*/

    pfPopState();
    
}

static void
drawSmoke(pfuSmoke *smoke, pfVec3 eye)
{
    double 	now = pfGetTime();
    double	deltaTime, age = 1.0f;
    float	speed, *tmp;
    float	puffInterval, puffDiss;
    pfVec3	dist;
    int	i, n, startPuff;

    /* Compute time elapsed from previous pfDrawSmoke */
    if(smoke->prevTime < 0.0f)
	deltaTime = 0.0f;
    else
    	deltaTime = now - smoke->prevTime;

    smoke->prevTime = now;

    if(smoke->startTime < 0.0f)
	smoke->startTime = now;

    if(smoke->duration > 0.0f)
    {
    	age = 1.0 - (now - smoke->startTime) / smoke->duration;

    	if(age <= 0.0)
    	{
	    smoke->mode = PFUSMOKE_STOP;
	    smoke->startTime = -1.;
	    smoke->lastPuffTime = -1.;
	    return;
        }

	age += .01f;
    }

    if (smoke->type != PFUSMOKE_EXPLOSION)
    {
	puffInterval = smoke->puffInterval / age;
	puffDiss = smoke->dissipation * age;
    }
    else
    {
	puffInterval = smoke->puffInterval;
	puffDiss = smoke->dissipation;
    }
   
    /* Initialize new puff of smoke if necessary */
    if(now - smoke->lastPuffTime >= puffInterval)
    {
	pfuPuff	*puff;

	smoke->lastPuffTime = now;
	puff = smoke->puffs[(smoke->startPuff + smoke->numPuffs) % MAX_PUFFS];
						
	initPuff(puff, smoke);

	smoke->numPuffs++;
    }

    if(smoke->tex)
    	pfApplyTex(smoke->tex);

    n = smoke->numPuffs;
    startPuff = smoke->startPuff;

    for(i=n-1; i>=0; i--)
    {
	pfuPuff *puff = smoke->puffs[(i+startPuff) % MAX_PUFFS];

	/* 
	 * Dissipate puff based on dissipation rate 
	*/
	puff->t = (now - puff->startTime) / puffDiss;

	/* Get rid of puff if completely transparent */
	if(puff->t >= 1.0f)
	{	    
	    smoke->startPuff = (smoke->startPuff + 1) % MAX_PUFFS;
	    smoke->numPuffs--;
	    continue;
	}

	/* 
	 * Transparency is cubed so it quickly dissipates as dissipation
	 * time is neared.
	*/
	puff->transp = puff->t * puff->t * puff->t * puff->t;

	if(smoke->type == PFUSMOKE_EXPLOSION)
	{
	    /* Radius is linear interpolation between 0 and expansion */
	    puff->radius = (now - puff->startTime) / puffDiss * 
				smoke->expansion * smoke->radius;
	}
	else
	{
	    /* Radius is linear interpolation between radius and expansion */
	    puff->radius = (1.0f - puff->transp) * smoke->radius + 
			puff->transp * smoke->expansion * smoke->radius;
	}

    	speed = puff->speed * deltaTime;
   	PFSCALE_VEC3(dist, speed, puff->direction);

	/* Translate puff based on velocity vector */
	{
 	    tmp = puff->origin;
	    PFADD_VEC3(tmp, tmp, dist);
	}

	/* Fade in brand new puff */
	if(i == smoke->numPuffs - 1 
	   && smoke->type != PFUSMOKE_EXPLOSION && smoke->type != PFUSMOKE_FIRE)
	{
	    puff->transp = 1.0f - (now - puff->startTime) / puffInterval;
	}

	drawPuff(puff, smoke, eye);
    }
}

#define SQRT3_2	.86602540378443864676f

static void
drawPuff(pfuPuff *puff, pfuSmoke *smoke, pfVec3 eye)
{
    pfVec4    	opaque;
    int	i;
#if 0
    float	r = puff->radius, *org = puff->origin;
    pfVec3    	vert;
#endif

    static const pfVec3 quad[] = {
	{-1.0f, 0.0f, -1.0f},
	{1.0f, 0.0f, -1.0f},
	{1.0f, 0.0f, 1.0f},
	{-1.0f, 0.0f, 1.0f}
    }; 

    pfVec3 		dquad[4], toEye;
    pfMatrix		mat;
    static pfVec3	vec = {0.0f, -1.0f, 0.0f};

    /* Fade fire to black smoke */
    if (smoke->type == PFUSMOKE_FIRE)
    {
   	 pfAddScaledVec3(opaque, smoke->bgnColor, 
			  puff->transp, smoke->deltaColor);
    	opaque[3] = 1.0f;
    }
    else
    {
   	 pfAddScaledVec3(opaque, smoke->bgnColor, 
			  puff->t, smoke->deltaColor);

    	opaque[3] = 1.0f - puff->transp;
    }

    /* 
     * Compute matrix which rotates puff to face the eyepoint 
    */
    PFSUB_VEC3(toEye, eye, puff->origin);

    pfNormalizeVec3(toEye);
    pfMakeVecRotVecMat(mat, vec, toEye);

    glColor4fv(opaque);
    glBegin(GL_QUADS);
    for(i=0; i<4; i++)
    {
	pfVec3		squad;

	/* Rotate texture coordinates to simulate turbulence */
	pfXformPt3(puff->tcoords[i], puff->tcoords[i], puff->rotMat);
	glTexCoord2fv(puff->tcoords[i]);

	/* Scale quad to current radius */
	PFSCALE_VEC3(squad, puff->radius, quad[i]);

	/* Offset puff based on radius */
	squad[1] -= smoke->radius;

	/* Rotate puff to follow viewer */
	pfXformPt3(dquad[i], squad, mat);

	/* Translate puff to origin */
    	PFADD_VEC3(dquad[i], dquad[i], puff->origin);
	glVertex3fv(dquad[i]);
    }
    glEnd();
}

#if 0
    bgnpolygon();
    vert[PF_Y] = puff->origin[PF_Y];
    vert[PF_X] = puff->origin[PF_X] - puff->radius;
    vert[PF_Z] = puff->origin[PF_Z] - puff->radius;
    t2f(tc[0]);
    v3f(vert);
    vert[PF_X] = puff->origin[PF_X] + puff->radius;
    t2f(tc[1]);
    v3f(vert);
    vert[PF_Z] = puff->origin[PF_Z] + puff->radius;
    t2f(tc[2]);
    v3f(vert);
    vert[PF_X] = puff->origin[PF_X] - puff->radius;
    t2f(tc[3]);
    v3f(vert);
    endpolygon();


	float	*org = puff->origin;
	float	*dh = dquad[i], *h = quad[i];
	float	sqLen, sqToView, ch, sh, cp, sp;

	PFSUB_VEC3(toView, eye, org);

	sqToView[0] = toView[0] * toView[0];
	sqToView[1] = toView[1] * toView[1];
	sqToView[2] = toView[2] * toView[2];
	
	sqLen = (NSQR1F)/(sqToView[0] + sqToView[1] + sqToView[2]);
	
	ch = SQR_LOOKUP(sqLen / sqToView[1]);
	if (sqToView[1] > 0.0f)
	    ch = -ch;
	
	sh = -SQR_LOOKUP(lensq1 * sqToView[0]);
	if (sqToView[0] < 0.0f)
	    sh = -sh;

	cp = SQR_LOOKUP(sqLen / sqToView[1]);
	if (sqToView[1] > 0.0f)
	    cp = -cp;
	
	sp = -SQR_LOOKUP(lensq1 * sqToView[0]);
	if (sqToView[0] < 0.0f)
	    sp = -sp;

    	/* prefetch values (as have regs for) to avoid repeated loads */
    	org0 = org[0];
    	org1 = org[1];

	s00 = quad[0][0] * r; 
        dquad[0][0] = c * s00 + org0;
        dquad[0][1] = org1 - s * s00;
        dquad[0][2] = org1 - s * s00;

	s10 = quad[1][0] * r;
        dquad[1][0] = c * s10 + org0;
        dquad[1][1] = org1 - s * s10;

	s20 = quad[2][0] * r; 
        dquad[2][0] = c * s20 + org0; 
        dquad[2][1] = org1 - s * s20;

	s30 = quad[3][0] * r;
        dquad[3][0] = c * s30 + org0; 
        dquad[3][1] = org1 - s * s30;
#endif