コード例 #1
0
ファイル: cg_atmospheric.c プロジェクト: yfcz/etlegacy
/**
 * @brief Check rain particle visibility
 * @details Check the snowflake is visible and still going, wrapping if necessary.
 */
static qboolean CG_SnowParticleCheckVisible(cg_atmosphericParticle_t *particle)
{
    float  moved;
    vec2_t distance;

    if (!particle || particle->active == ACT_NOT)
    {
        return qfalse;
    }

    // units moved since last frame
    moved = (cg.time - cg_atmFx.lastRainTime) * 0.001;
    VectorMA(particle->pos, moved, particle->delta, particle->pos);

    if (particle->pos[2] < BG_GetSkyGroundHeightAtPoint(particle->pos))
    {
        return CG_SetParticleActive(particle, ACT_NOT);
    }

    distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0];
    distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1];

    if ((distance[0] * distance[0] + distance[1] * distance[1]) > Square(MAX_ATMOSPHERIC_DISTANCE))
    {
        // just nuke this particle, let it respawn
        return CG_SetParticleActive(particle, ACT_NOT);
    }

    return qtrue;
}
コード例 #2
0
static qboolean CG_SnowParticleCheckVisible(cg_atmosphericParticle_t *particle)
{
	// Check the snowflake is visible and still going, wrapping if necessary.

	float  moved;
	vec2_t distance;
	//int msec = trap_Milliseconds();

	//n_checkvisibletime++;

	if (!particle || particle->active == ACT_NOT)
	{
//      checkvisibletime += trap_Milliseconds() - msec;
		return(qfalse);
	}

	moved = (cg.time - cg_atmFx.lastRainTime) * 0.001;    // Units moved since last frame
	VectorMA(particle->pos, moved, particle->delta, particle->pos);
	if (particle->pos[2] < BG_GetSkyGroundHeightAtPoint(particle->pos))
	{
		//checkvisibletime += trap_Milliseconds() - msec;
		return CG_SetParticleActive(particle, ACT_NOT);
	}

	distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0];
	distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1];
	if ((distance[0] * distance[0] + distance[1] * distance[1]) > Square(MAX_ATMOSPHERIC_DISTANCE))
	{
		// just nuke this particle, let it respawn
		return CG_SetParticleActive(particle, ACT_NOT);
	}

	//checkvisibletime += trap_Milliseconds() - msec;
	return(qtrue);
}
コード例 #3
0
ファイル: cg_atmospheric.c プロジェクト: yfcz/etlegacy
/**
 * @brief Generate rain particle
 * @details Attempt to 'spot' a raindrop somewhere below a sky texture.
 */
static qboolean CG_RainParticleGenerate(cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight)
{
    float angle = random() * 2 * M_PI, distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();
    float groundHeight, skyHeight;

    particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance;
    particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance;

    // choose a spawn point randomly between sky and ground
    skyHeight = BG_GetSkyHeightAtPoint(particle->pos);
    if (skyHeight >= MAX_ATMOSPHERIC_HEIGHT)
    {
        return qfalse;
    }
    groundHeight = BG_GetSkyGroundHeightAtPoint(particle->pos);
    if (groundHeight + particle->height + ATMOSPHERIC_PARTICLE_OFFSET >= skyHeight)
    {
        return qfalse;
    }
    particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight);

    // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
    if (cg_atmFx.baseHeightOffset > 0)
    {
        if (particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset)
        {
            particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset;

            if (particle->pos[2] < groundHeight)
            {
                return qfalse;
            }
        }
    }

    // rain goes in bursts - allow max raindrops every 10 seconds
    if (cg_atmFx.oldDropsActive > (0.50 * cg_atmFx.numDrops + 0.001 * cg_atmFx.numDrops * (10000 - (cg.time % 10000))))
    {
        return qfalse;
    }

    CG_SetParticleActive(particle, ACT_FALLING);
    particle->colour[0] = 0.6 + 0.2 * random() * 0xFF;
    particle->colour[1] = 0.6 + 0.2 * random() * 0xFF;
    particle->colour[2] = 0.6 + 0.2 * random() * 0xFF;
    VectorCopy(currvec, particle->delta);
    particle->delta[2] += crandom() * 100;
    VectorCopy(particle->delta, particle->deltaNormalized);
    VectorNormalizeFast(particle->deltaNormalized);
    particle->height       = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100;
    particle->weight       = currweight;
    particle->effectshader = &cg_atmFx.effectshaders[0];
    //particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ];

    return qtrue;
}
コード例 #4
0
static qboolean CG_SnowParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ) {
	// Attempt to 'spot' a snowflake somewhere below a sky texture.

	float angle, distance;
	float groundHeight, skyHeight;
//	int msec = trap_Milliseconds();

//	n_generatetime++;

	angle = random() * 2 * M_PI;
	distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();

	particle->pos[0] = cg.refdef.vieworg[0] + sin( angle ) * distance;
	particle->pos[1] = cg.refdef.vieworg[1] + cos( angle ) * distance;

	// ydnar: choose a spawn point randomly between sky and ground
	skyHeight = BG_GetSkyHeightAtPoint( particle->pos );
	if ( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) {
		return qfalse;
	}
	groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos );
	if ( groundHeight >= skyHeight ) {
		return qfalse;
	}
	particle->pos[2] = groundHeight + random() * ( skyHeight - groundHeight );

	// make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
	if ( cg_atmFx.baseHeightOffset > 0 ) {
		if ( particle->pos[2] - cg.refdef.vieworg[2] > cg_atmFx.baseHeightOffset ) {
			particle->pos[2] = cg.refdef.vieworg[2] + cg_atmFx.baseHeightOffset;
			if ( particle->pos[2] < groundHeight ) {
				return qfalse;
			}
		}
	}

	CG_SetParticleActive( particle, ACT_FALLING );
	VectorCopy( currvec, particle->delta );
	particle->delta[2] += crandom() * 25;
	VectorCopy( particle->delta, particle->deltaNormalized );
	VectorNormalizeFast( particle->deltaNormalized );
	particle->height = ATMOSPHERIC_SNOW_HEIGHT + random() * 2;
	particle->weight = particle->height * 0.5f;

	if (cg_atmFx.numEffectShaders > 1) {
		particle->effectshader = &cg_atmFx.effectshaders[ rand()%cg_atmFx.numEffectShaders ];
	} else {
		particle->effectshader = &cg_atmFx.effectshaders[0];
	}

//	generatetime += trap_Milliseconds() - msec;
	return( qtrue );
}
コード例 #5
0
ファイル: cg_atmospheric.c プロジェクト: yfcz/etlegacy
/**
 * @brief Generate a snowflake
 * @details Attempt to 'spot' a snowflake somewhere below a sky texture.
 */
static qboolean CG_SnowParticleGenerate(cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight)
{
    float angle = random() * 2 * M_PI, distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();
    float groundHeight, skyHeight;

    particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance;
    particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance;

    // choose a spawn point randomly between sky and ground
    skyHeight = BG_GetSkyHeightAtPoint(particle->pos);

    if (skyHeight >= MAX_ATMOSPHERIC_HEIGHT)
    {
        return qfalse;
    }

    groundHeight = BG_GetSkyGroundHeightAtPoint(particle->pos);

    if (groundHeight + particle->height + ATMOSPHERIC_PARTICLE_OFFSET >= skyHeight)
    {
        return qfalse;
    }

    particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight);

    // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
    if (cg_atmFx.baseHeightOffset > 0)
    {
        if (particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset)
        {
            particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset;
            if (particle->pos[2] < groundHeight)
            {
                return qfalse;
            }
        }
    }

    CG_SetParticleActive(particle, ACT_FALLING);

    VectorCopy(currvec, particle->delta);
    particle->delta[2] += crandom() * 25;

    VectorCopy(particle->delta, particle->deltaNormalized);
    VectorNormalizeFast(particle->deltaNormalized);
    particle->height       = ATMOSPHERIC_SNOW_HEIGHT + random() * 2;
    particle->weight       = particle->height * 0.5f;
    particle->effectshader = &cg_atmFx.effectshaders[0];
    //particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ];

    return qtrue;
}
コード例 #6
0
static qboolean CG_SnowParticleCheckVisible( cg_atmosphericParticle_t *particle )
{
	// Check the snowflake is visible and still going, wrapping if necessary.

	float moved;
	vec2_t distance;
//	int msec = trap_Milliseconds();

//	n_checkvisibletime++;

	if( !particle || particle->active == ACT_NOT ) {
//		checkvisibletime += trap_Milliseconds() - msec;
		return( qfalse );
	}

	moved = (cg.time - cg_atmFx.lastRainTime) * 0.001;	// Units moved since last frame
	VectorMA( particle->pos, moved, particle->delta, particle->pos );
	if( particle->pos[2] < BG_GetSkyGroundHeightAtPoint( particle->pos ) ) {
//		checkvisibletime += trap_Milliseconds() - msec;
		return CG_SetParticleActive( particle, ACT_NOT );
	}

	distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0];
	distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1];
 	if( (distance[0] * distance[0] + distance[1] * distance[1]) > Square( MAX_ATMOSPHERIC_DISTANCE ) )
 	{
		// ydnar: just nuke this particle, let it respawn
		return CG_SetParticleActive( particle, ACT_NOT );
		
		/*
		// Attempt to respot the particle at our other side
		particle->pos[0] -= 1.85f * distance[0];
		particle->pos[1] -= 1.85f * distance[1];

		// ydnar: place particle in random position between ground and sky
		groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos );
		skyHeight = BG_GetSkyHeightAtPoint( particle->pos );
		if( skyHeight == MAX_ATMOSPHERIC_HEIGHT )
			return CG_SetParticleActive( particle, ACT_NOT );
		particle->pos[ 2 ] = groundHeight + random() * (skyHeight - groundHeight);
		
		// ydnar: valid spot?
		if( particle->pos[ 2 ] <= groundHeight || particle->pos[ 2 ] >= skyHeight )
			return CG_SetParticleActive( particle, ACT_NOT );
		*/
	}

//	checkvisibletime += trap_Milliseconds() - msec;
	return( qtrue );
}
コード例 #7
0
static qboolean CG_RainParticleCheckVisible( cg_atmosphericParticle_t *particle )
{
	// Check the raindrop is visible and still going, wrapping if necessary.

	float moved;
	vec2_t distance;
//	int msec = trap_Milliseconds();

	if( !particle || particle->active == ACT_NOT ) {
//		checkvisibletime += trap_Milliseconds() - msec;
		return( qfalse );
	}

	moved = (cg.time - cg_atmFx.lastRainTime) * 0.001;	// Units moved since last frame
	VectorMA( particle->pos, moved, particle->delta, particle->pos );
 	if( particle->pos[2] + particle->height < BG_GetSkyGroundHeightAtPoint( particle->pos ) ) {
//		checkvisibletime += trap_Milliseconds() - msec;
		return CG_SetParticleActive( particle, ACT_NOT );
	}

	distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0];
	distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1];
 	if( (distance[0] * distance[0] + distance[1] * distance[1]) > Square( MAX_ATMOSPHERIC_DISTANCE ) )
 	{
		// ydnar: just nuke this particle, let it respawn
		return CG_SetParticleActive( particle, ACT_NOT );
		
		/*
		// Attempt to respot the particle at our other side
		particle->pos[0] -= 1.85f * distance[0];
		particle->pos[1] -= 1.85f * distance[1];

		// Valid spot?
		pointHeight = BG_GetSkyHeightAtPoint( particle->pos );
		if( pointHeight == MAX_ATMOSPHERIC_HEIGHT ) {
//			checkvisibletime += trap_Milliseconds() - msec;
			return CG_SetParticleActive( particle, ACT_NOT );
		}

		pointHeight = BG_GetSkyGroundHeightAtPoint( particle->pos );
		if( pointHeight == MAX_ATMOSPHERIC_HEIGHT || pointHeight >= particle->pos[2] ) {
//			checkvisibletime += trap_Milliseconds() - msec;
			return CG_SetParticleActive( particle, ACT_NOT );
		}
		*/
	}

//	checkvisibletime += trap_Milliseconds() - msec;
	return( qtrue );
}
コード例 #8
0
ファイル: cg_atmospheric.c プロジェクト: yfcz/etlegacy
/**
 * @brief Draw a snowflake
 * @details Render a snow particle.
 */
static void CG_SnowParticleRender(cg_atmosphericParticle_t *particle)
{
    vec3_t     forward, right;
    polyVert_t verts[3];
    vec2_t     line;
    float      len, sinTumbling, cosTumbling, particleWidth, dist;
    vec3_t     start, finish;
    float      groundHeight;

    if (particle->active == ACT_NOT)
    {
        return;
    }

    if (CG_CullPoint(particle->pos))
    {
        return;
    }

    VectorCopy(particle->pos, start);

    sinTumbling = sin(particle->pos[2] * 0.03125f * (0.5f * particle->weight));
    cosTumbling = cos((particle->pos[2] + particle->pos[1]) * 0.03125f * (0.5f * particle->weight));
    start[0]   += 24 * (1 - particle->deltaNormalized[2]) * sinTumbling;
    start[1]   += 24 * (1 - particle->deltaNormalized[2]) * cosTumbling;

    // make sure it doesn't clip through surfaces
    groundHeight = BG_GetSkyGroundHeightAtPoint(start);
    len          = particle->height;

    if (start[2] - len - ATMOSPHERIC_PARTICLE_OFFSET <= groundHeight)
    {
        return;
        // stop snow going through surfaces
        //len = particle->height - groundHeight + start[2];
        //VectorMA(start, len - particle->height, particle->deltaNormalized, start);
    }

    if (len <= 0)
    {
        return;
    }

    line[0] = particle->pos[0] - cg.refdef_current->vieworg[0];
    line[1] = particle->pos[1] - cg.refdef_current->vieworg[1];

    dist = DistanceSquared(particle->pos, cg.refdef_current->vieworg);

    // dist becomes scale
    if (dist > Square(500.f))
    {
        dist = 1.f + ((dist - Square(500.f)) * (10.f / Square(2000.f)));
    }
    else
    {
        dist = 1.f;
    }

    len *= dist;

    VectorCopy(particle->deltaNormalized, forward);
    VectorMA(start, -(len /* sinTumbling */), forward, finish);

    line[0] = DotProduct(forward, cg.refdef_current->viewaxis[1]);
    line[1] = DotProduct(forward, cg.refdef_current->viewaxis[2]);

    VectorScale(cg.refdef_current->viewaxis[1], line[1], right);
    VectorMA(right, -line[0], cg.refdef_current->viewaxis[2], right);
    VectorNormalize(right);

    particleWidth = dist * (/* cosTumbling */ particle->weight);

    VectorMA(finish, -particleWidth, right, verts[0].xyz);
    verts[0].st[0]       = 0;
    verts[0].st[1]       = 0;
    verts[0].modulate[0] = 255;
    verts[0].modulate[1] = 255;
    verts[0].modulate[2] = 255;
    verts[0].modulate[3] = 255;

    VectorMA(start, -particleWidth, right, verts[1].xyz);
    verts[1].st[0]       = 0;
    verts[1].st[1]       = 1;
    verts[1].modulate[0] = 255;
    verts[1].modulate[1] = 255;
    verts[1].modulate[2] = 255;
    verts[1].modulate[3] = 255;

    VectorMA(start, particleWidth, right, verts[2].xyz);
    verts[2].st[0]       = 1;
    verts[2].st[1]       = 1;
    verts[2].modulate[0] = 255;
    verts[2].modulate[1] = 255;
    verts[2].modulate[2] = 255;
    verts[2].modulate[3] = 255;

    CG_AddPolyToPool(*particle->effectshader, verts);
}
コード例 #9
0
ファイル: cg_atmospheric.c プロジェクト: yfcz/etlegacy
/**
 * @brief Draw a raindrop
 * @details Render a rain particle.
 */
static void CG_RainParticleRender(cg_atmosphericParticle_t *particle)
{
    vec3_t     forward, right;
    polyVert_t verts[3];
    vec2_t     line;
    float      len, dist;
    vec3_t     start, finish;
    float      groundHeight;

    if (particle->active == ACT_NOT)
    {
        return;
    }

    if (CG_CullPoint(particle->pos))
    {
        return;
    }

    VectorCopy(particle->pos, start);

    dist = DistanceSquared(particle->pos, cg.refdef_current->vieworg);

    // make sure it doesn't clip through surfaces
    groundHeight = BG_GetSkyGroundHeightAtPoint(start);
    len          = particle->height;

    if (start[2] - ATMOSPHERIC_PARTICLE_OFFSET <= groundHeight)
    {
        // stop rain going through surfaces
        len = particle->height - groundHeight + start[2];
        VectorMA(start, len - particle->height, particle->deltaNormalized, start);
    }

    if (len <= 0)
    {
        return;
    }

    // fade nearby rain particles
    if (dist < Square(128.f))
    {
        dist = .25f + .75f * (dist / Square(128.f));
    }
    else
    {
        dist = 1.0f;
    }

    VectorCopy(particle->deltaNormalized, forward);
    VectorMA(start, -len, forward, finish);

    line[0] = DotProduct(forward, cg.refdef_current->viewaxis[1]);
    line[1] = DotProduct(forward, cg.refdef_current->viewaxis[2]);

    VectorScale(cg.refdef_current->viewaxis[1], line[1], right);
    VectorMA(right, -line[0], cg.refdef_current->viewaxis[2], right);
    VectorNormalize(right);

    VectorCopy(finish, verts[0].xyz);
    verts[0].st[0]       = 0.5f;
    verts[0].st[1]       = 0;
    verts[0].modulate[0] = particle->colour[0];
    verts[0].modulate[1] = particle->colour[1];
    verts[0].modulate[2] = particle->colour[2];
    verts[0].modulate[3] = 100 * dist;

    VectorMA(start, -particle->weight, right, verts[1].xyz);
    verts[1].st[0]       = 0;
    verts[1].st[1]       = 1;
    verts[1].modulate[0] = particle->colour[0];
    verts[1].modulate[1] = particle->colour[1];
    verts[2].modulate[2] = particle->colour[2];
    verts[1].modulate[3] = 200 * dist;

    VectorMA(start, particle->weight, right, verts[2].xyz);
    verts[2].st[0]       = 1;
    verts[2].st[1]       = 1;
    verts[2].modulate[0] = particle->colour[0];
    verts[2].modulate[1] = particle->colour[1];
    verts[2].modulate[2] = particle->colour[2];
    verts[2].modulate[3] = 200 * dist;

    CG_AddPolyToPool(*particle->effectshader, verts);
}
コード例 #10
0
static void CG_RainParticleRender( cg_atmosphericParticle_t *particle )
{
	// Draw a raindrop

	vec3_t		forward, right;
	polyVert_t	verts[3];
	vec2_t		line;
	float		len, frac, dist;
	vec3_t		start, finish;
	float		groundHeight;
//	int			msec = trap_Milliseconds();

//	n_rendertime++;

	if( particle->active == ACT_NOT ) {
//		rendertime += trap_Milliseconds() - msec;
		return;
	}

	if( CG_CullPoint( particle->pos ) ) {
		return;
	}

	VectorCopy( particle->pos, start );

	dist = DistanceSquared( particle->pos, cg.refdef_current->vieworg );

	// Make sure it doesn't clip through surfaces
	groundHeight = BG_GetSkyGroundHeightAtPoint( start );
	len = particle->height;
	if( start[2] <= groundHeight ) {
		// Stop snow going through surfaces.
		len = particle->height - groundHeight + start[2];
		frac = start[2];
		VectorMA( start, len - particle->height, particle->deltaNormalized, start );
	}

	if( len <= 0 ) {
//		rendertime += trap_Milliseconds() - msec;
		return;
	}

	// fade nearby rain particles
	if( dist < Square( 128.f ) )
		dist = .25f + .75f * ( dist / Square( 128.f ) );
	else
		dist = 1.0f;

	VectorCopy( particle->deltaNormalized, forward );
	VectorMA( start, -len, forward, finish );

	line[0] = DotProduct( forward, cg.refdef_current->viewaxis[1] );
	line[1] = DotProduct( forward, cg.refdef_current->viewaxis[2] );

	VectorScale( cg.refdef_current->viewaxis[1], line[1], right );
	VectorMA( right, -line[0], cg.refdef_current->viewaxis[2], right );
	VectorNormalize( right );
	
	// dist = 1.0;
	
	VectorCopy( finish, verts[0].xyz );	
	verts[0].st[0] = 0.5f;
	verts[0].st[1] = 0;
	verts[0].modulate[0] = particle->colour[0];
	verts[0].modulate[1] = particle->colour[1];
	verts[0].modulate[2] = particle->colour[2];
	verts[0].modulate[3] = 100 * dist;

	VectorMA( start, -particle->weight, right, verts[1].xyz );
	verts[1].st[0] = 0;
	verts[1].st[1] = 1;
	verts[1].modulate[0] = particle->colour[0];
	verts[1].modulate[1] = particle->colour[1];
	verts[2].modulate[2] = particle->colour[2];
	verts[1].modulate[3] = 200 * dist;

	VectorMA( start, particle->weight, right, verts[2].xyz );
	verts[2].st[0] = 1;
	verts[2].st[1] = 1;
	verts[2].modulate[0] = particle->colour[0];
	verts[2].modulate[1] = particle->colour[1];
	verts[2].modulate[2] = particle->colour[2];
	verts[2].modulate[3] = 200 * dist;

	CG_AddPolyToPool( *particle->effectshader, verts );

//	rendertime += trap_Milliseconds() - msec;
}
コード例 #11
0
static qboolean CG_RainParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight )
{
	// Attempt to 'spot' a raindrop somewhere below a sky texture.

	float angle, distance;
	float groundHeight, skyHeight;
//	int msec = trap_Milliseconds();

//	n_generatetime++;
	
	angle = random() * 2*M_PI;
	distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();

	particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance;
	particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance;
	
	// ydnar: choose a spawn point randomly between sky and ground
	skyHeight = BG_GetSkyHeightAtPoint( particle->pos );
	if( skyHeight == MAX_ATMOSPHERIC_HEIGHT )
		return qfalse;
	groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos );
	if( groundHeight >= skyHeight )
		return qfalse;
	particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight);

	// make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
	if( cg_atmFx.baseHeightOffset > 0 ) {
		if( particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset ) {
			particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset;

			if( particle->pos[2] < groundHeight ) {
				return qfalse;
			}
		}
	}
	
	// ydnar: rain goes in bursts
	{
		float		maxActiveDrops;
		
		// every 10 seconds allow max raindrops
		maxActiveDrops = 0.50 * cg_atmFx.numDrops + 0.001 * cg_atmFx.numDrops * (10000 - (cg.time % 10000));
		if( cg_atmFx.oldDropsActive > maxActiveDrops )
			return qfalse;
	}
	
	CG_SetParticleActive( particle, ACT_FALLING );
	particle->colour[0] = 0.6 + 0.2 * random() * 0xFF;
	particle->colour[1] = 0.6 + 0.2 * random() * 0xFF;
	particle->colour[2] = 0.6 + 0.2 * random() * 0xFF;
	VectorCopy( currvec, particle->delta );
	particle->delta[2] += crandom() * 100;
	VectorCopy( particle->delta, particle->deltaNormalized );
	VectorNormalizeFast( particle->deltaNormalized );
	particle->height = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100;
	particle->weight = currweight;
	particle->effectshader = &cg_atmFx.effectshaders[0];
//	particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ];

//	generatetime += trap_Milliseconds() - msec;
	return( qtrue );
}