/**
 * Render debris
 */
void debris_render(object * obj)
{
	int			i, num, swapped;
	polymodel	*pm;
	debris		*db;


	swapped = -1;
	pm = NULL;	
	num = obj->instance;

	Assert(num >= 0 && num < MAX_DEBRIS_PIECES);
	db = &Debris[num];

	Assert(db->flags & DEBRIS_USED);

	texture_info *tbase = NULL;
	
	model_clear_instance( db->model_num );
	
	// Swap in a different texture depending on the species
	if (db->species >= 0)
	{
		pm = model_get( db->model_num );

		//WMC - Someday, we should have glowing debris.
		if ( pm != NULL && (pm->n_textures == 1) ) {
			tbase = &pm->maps[0].textures[TM_BASE_TYPE];
			swapped = tbase->GetTexture();
			tbase->SetTexture(Species_info[db->species].debris_texture.bitmap_id);
		}
	}

	// Only render electrical arcs if within 500m of the eye (for a 10m piece)
	if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f )	{
		for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
			if ( timestamp_valid( db->arc_timestamp[i] ) )	{
				model_add_arc( db->model_num, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_NORMAL );
			}
		}
	}

	if ( db->is_hull )	{
		MONITOR_INC(NumHullDebrisRend,1);
		submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos );
	} else {
		MONITOR_INC(NumSmallDebrisRend,1);
		submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING );
	}

	if (tbase != NULL && (swapped!=-1) && pm)	{
		tbase->SetTexture(swapped);
	}
}
Example #2
0
ubyte g3_rotate_vertex(vertex *dest,vector *src)
{
#if 0
	vector tempv;
	Assert( G3_count == 1 );
	vm_vec_sub(&tempv,src,&View_position);
	vm_vec_rotate( (vector *)&dest->x, &tempv, &View_matrix );
	dest->flags = 0;	//not projected
	return g3_code_vertex(dest);
#else
	float tx, ty, tz, x,y,z;
	ubyte codes;

	MONITOR_INC( NumRotations, 1 );	

	tx = src->xyz.x - View_position.xyz.x;
	ty = src->xyz.y - View_position.xyz.y;
	tz = src->xyz.z - View_position.xyz.z;

	x = tx * View_matrix.vec.rvec.xyz.x;
	x += ty * View_matrix.vec.rvec.xyz.y;
	x += tz * View_matrix.vec.rvec.xyz.z;

	y = tx * View_matrix.vec.uvec.xyz.x;
	y += ty * View_matrix.vec.uvec.xyz.y;
	y += tz * View_matrix.vec.uvec.xyz.z;

	z = tx * View_matrix.vec.fvec.xyz.x;
	z += ty * View_matrix.vec.fvec.xyz.y;
	z += tz * View_matrix.vec.fvec.xyz.z;

	codes = 0;

	if (x > z)			codes |= CC_OFF_RIGHT;
	if (x < -z)			codes |= CC_OFF_LEFT;
	if (y > z)			codes |= CC_OFF_TOP;
	if (y < -z)			codes |= CC_OFF_BOT;
	if (z < MIN_Z )	codes |= CC_BEHIND;

	dest->x = x;
	dest->y = y;
	dest->z = z;

	if ( G3_user_clip )	{
		// Check if behind user plane
		if ( g3_point_behind_user_plane((vector *)&dest->x))	{
			codes |= CC_OFF_USER;
		}
	}

	dest->codes = codes;

	dest->flags = 0;	// not projected

	vm_vec_copy_scale(&dest->real_pos, src,1);

	return codes;
#endif
}	
Example #3
0
ubyte g3_rotate_faraway_vertex(vertex *dest,vector *src)
{	
	 Assert( G3_count == 1 );

	MONITOR_INC( NumRotations, 1 );	

	vm_vec_rotate( (vector *)&dest->x, src, &View_matrix );
	dest->flags = 0;	//not projected
	return g3_code_vertex(dest);
}	
Example #4
0
//rotates a point. returns codes.  does not check if already rotated
ubyte g3_rotate_vector(vector *dest,vector *src)
{
	vector tempv;

	 Assert( G3_count == 1 );

	MONITOR_INC( NumRotations, 1 );	

	vm_vec_sub(&tempv,src,&View_position);
	vm_vec_rotate(dest,&tempv,&View_matrix);
	return g3_code_vector(dest);
}	
Example #5
0
/**
 * Add data for a shield hit.
 */
void add_shield_point(int objnum, int tri_num, vec3d *hit_pos)
{
    if (Num_shield_points >= MAX_SHIELD_POINTS)
        return;

    Verify(objnum < MAX_OBJECTS);

    MONITOR_INC(NumShieldHits,1);

    Shield_points[Num_shield_points].objnum = objnum;
    Shield_points[Num_shield_points].shield_tri = tri_num;
    Shield_points[Num_shield_points].hit_point = *hit_pos;

    Num_shield_points++;

    Ships[Objects[objnum].instance].shield_hits++;
}
void obj_check_all_collisions()
{
	obj_pair *parent, *tmp;	

#ifdef PAIR_STATS
	// debug info
	float avg_time_to_next_check = 0.0f;
#endif

	if (Cmdline_dis_collisions)
		return;

	if ( !(Game_detail_flags & DETAIL_FLAG_COLLISION) )
		return;


	parent = &pair_used_list;
	tmp = parent->next;

	Num_pairs_checked = 0;

	while (tmp != NULL) {
		int removed = 0;

		if ( !timestamp_elapsed(tmp->next_check_time) )
			goto NextPair;

		if ( (tmp->a) && (tmp->b) ) {
			Num_pairs_checked++;

			if ( (*tmp->check_collision)(tmp) ) {
				// We never need to check this pair again.
				#if 0	//def DONT_REMOVE_PAIRS
					// Never check it again, but keep the pair around
					// (useful for debugging)
					tmp->next_check_time = timestamp(-1);		
				#else
					// Never check it again, so remove the pair
					removed = 1;
					tmp->a->num_pairs--;	
					Assert( tmp->a->num_pairs > -1 );
					tmp->b->num_pairs--;
					Assert( tmp->b->num_pairs > -1 );
					Num_pairs--;
					// Assert(Num_pairs >= 0);
					parent->next = tmp->next;
					tmp->a = tmp->b = NULL;
					tmp->next = pair_free_list.next;
					pair_free_list.next = tmp;
					tmp = parent->next;
				#endif
			}
		} 

NextPair:
		if ( !removed ) {
			parent = tmp;
			tmp = tmp->next;

#ifdef PAIR_STATS
			// debug info
			if (tmp) {
				int add_time = timestamp_until( tmp->next_check_time );
				if (add_time > 0)
					avg_time_to_next_check += (float) add_time;
			}
#endif
		}
	}

	MONITOR_INC(NumPairs, Num_pairs);
	MONITOR_INC(NumPairsChecked, Num_pairs_checked);

#ifdef PAIR_STATS
	avg_time_to_next_check = avg_time_to_next_check / Num_pairs;
	extern int Num_hull_pieces;
	extern int Weapons_created;
	// mprintf(( "[pairs checked: %d, start_pairs: %d, num obj: %d, avg next time: %f]\n", n, org_pairs, Num_objects, avg_time_to_next_check ));
	// mprintf(( "[Num_hull_pieces: %3d, Num_weapons_created: %3d, pairs_not_created: %3d, pairs_created: %3d, percent new saved: %9.5f]\n", Num_hull_pieces, Weapons_created, pairs_not_created, Pairs_created, 100.0f*(float)pairs_not_created/(float)(pairs_not_created + Pairs_created) ));
	 mprintf(( "[pairs_created: %3d, pairs_not_created: %3d, percent saved %6.3f]\n", Pairs_created, pairs_not_created, 100.0f*pairs_not_created/(Pairs_created+pairs_not_created) ));
	pairs_not_created = 0;
	Weapons_created = 0;
	Pairs_created = 0;
#endif

	// What percent of the pairs did we check?
	// FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects.

//	if ( org_pairs > 1 )	{
//		Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs);
//	} else {
//		Object_checked_percentage = 0.0f;
//	}

}
Example #7
0
/**
 * Do various updates to debris:  check if time to die, start fireballs
 * Maybe delete debris if it's very far away from player.
 *
 * @param obj			pointer to debris object
 * @param frame_time	time elapsed since last debris_move() called
 */
void debris_process_post(object * obj, float frame_time)
{
	int i, num;
	num = obj->instance;

	int objnum = OBJ_INDEX(obj);
	Assert( Debris[num].objnum == objnum );
	debris *db = &Debris[num];

	if ( db->is_hull ) {
		MONITOR_INC(NumHullDebris,1);
		radar_plot_object( obj );

		if ( timestamp_elapsed(db->sound_delay) ) {
			obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0);
			db->sound_delay = 0;
		}
	} else {
		MONITOR_INC(NumSmallDebris,1);
	}

	if ( db->lifeleft >= 0.0f) {
		db->lifeleft -= frame_time;
		if ( db->lifeleft < 0.0f )	{
			debris_start_death_roll(obj, db);
		}
	}

	maybe_delete_debris(db);	//	Make this debris go away if it's very far away.

	// ================== DO THE ELECTRIC ARCING STUFF =====================
	if ( db->arc_frequency <= 0 )	{
		return;			// If arc_frequency <= 0, this piece has no arcs on it
	}

	if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball))	{		

		db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
		db->arc_frequency += 100;	

		if (db->is_hull)	{

			int n, n_arcs = ((rand()>>5) % 3)+1;		// Create 1-3 sparks

			vec3d v1, v2, v3, v4;

			if ( Cmdline_old_collision_sys ) {
				submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
				submodel_get_two_random_points( db->model_num, db->submodel_num, &v3, &v4 );
			} else {
				submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v1, &v2 );
				submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v3, &v4 );
			}

			n = 0;

			int a = 100, b = 1000;
			int lifetime = (myrand()%((b)-(a)+1))+(a);

			// Create the spark effects
			for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
				if ( !timestamp_valid( db->arc_timestamp[i] ) )	{

					db->arc_timestamp[i] = timestamp(lifetime);	// live up to a second

					switch( n )	{
					case 0:
						db->arc_pts[i][0] = v1;
						db->arc_pts[i][1] = v2;
						break;
					case 1:
						db->arc_pts[i][0] = v2;
						db->arc_pts[i][1] = v3;
						break;

					case 2:
						db->arc_pts[i][0] = v2;
						db->arc_pts[i][1] = v4;
						break;

					default:
						Int3();
					}
						
					n++;
					if ( n == n_arcs )
						break;	// Don't need to create anymore
				}
			}

	
			// rotate v2 out of local coordinates into world.
			// Use v2 since it is used in every bolt.  See above switch().
			vec3d snd_pos;
			vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
			vm_vec_add2(&snd_pos, &obj->pos );

			//Play a sound effect
			if ( lifetime > 750 )	{
				// 1.00 second effect
				snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_05), &snd_pos, &View_position, obj->radius );
			} else if ( lifetime >  500 )	{
				// 0.75 second effect
				snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_04), &snd_pos, &View_position, obj->radius );
			} else if ( lifetime >  250 )	{
				// 0.50 second effect
				snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_03), &snd_pos, &View_position, obj->radius );
			} else if ( lifetime >  100 )	{
				// 0.25 second effect
				snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_02), &snd_pos, &View_position, obj->radius );
			} else {
				// 0.10 second effect
				snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_01), &snd_pos, &View_position, obj->radius );
			}
		}
/**
 * @brief Will add an anim instance to the anim_render_list.  
 * This will cause the anim to be played at the x,y position specified in the parameter list.
 *
 * @param aps Compressed animation that we should make an instance from
 * @return If success pointer to instance, NULL if anim anim could not be played
 */
anim_instance *anim_play(anim_play_struct *aps)
{
	Assert( aps->anim_info != NULL );
	Assert( aps->start_at >= 0 );
	Assert( aps->stop_at < aps->anim_info->total_frames );
	Assert( !(aps->looped && aps->ping_pong) );  // shouldn't have these both set at once

	MONITOR_INC(NumANIPlayed, 1);

	anim_instance *instance;

	// Find next free anim instance slot on queue
	instance = GET_FIRST(&anim_free_list);
	Assert( instance != &anim_free_list );  // shouldn't have the dummy element

	// remove instance from the free list
	list_remove( &anim_free_list, instance );

	// insert instance onto the end of anim_render_list
	list_append( &anim_render_list, instance );

	aps->anim_info->instance_count++;
	instance->frame_num = -1;
	instance->last_frame_num = -99;
	instance->parent = aps->anim_info;
	instance->data = aps->anim_info->data;
	if ( anim_instance_is_streamed(instance) ) {
		instance->file_offset = instance->parent->file_offset;
	}
	instance->frame = (ubyte *) vm_malloc(instance->parent->width * instance->parent->height * 2);
	Assert( instance->frame != NULL );
	memset( instance->frame, 0, instance->parent->width * instance->parent->height * 2 );
	instance->time_elapsed = 0.0f;
	instance->stop_at = aps->stop_at;
	instance->x = aps->x;
	instance->y = aps->y;
	instance->world_pos = aps->world_pos;
	instance->radius = aps->radius;
	instance->framerate_independent = aps->framerate_independent;
	instance->last_bitmap = -1;
	instance->stop_now = FALSE;
	instance->screen_id = aps->screen_id;
	instance->aa_color = aps->color;
	instance->skip_frames = aps->skip_frames;
	instance->looped = aps->looped;
	instance->ping_pong = aps->ping_pong;
	instance->direction = ANIM_DIRECT_FORWARD;
	instance->paused = 0;
	instance->loop_count = 0;
	if ( aps->color == NULL ){
		instance->xlate_pal = 1;
	} else {
		instance->xlate_pal = 0;
	}

	if(aps->base_w < 0 || aps->base_h < 0) {
		instance->base_w = gr_screen.max_w_unscaled_zoomed;
		instance->base_h = gr_screen.max_h_unscaled_zoomed;
	} else {
		instance->base_w = aps->base_w;
		instance->base_h = aps->base_h;
	}

	// determining the start_at frame is more complicated, since it must be a key-frame.
	// Futhermore, need to subtract 1 from key-frame number, since frame number is always
	// incremented the first time anim_show_next_frame() is called

	instance->start_at = aps->start_at;

	if ( aps->start_at > 0 ) {
		key_frame *keyp;
		int idx;
		int key = 0;
		int offset = 0;
		int frame_num = aps->start_at;

		keyp = instance->parent->keys;
		idx = 0;
		while (idx < instance->parent->num_keys) {
			if (key == frame_num)
				break;

			key = keyp[idx].frame_num - 1;
			offset = keyp[idx].offset;

			idx++;
		}

		if (key > instance->frame_num) {  // best key is closer than current position
			instance->frame_num = key;
			if ( anim_instance_is_streamed(instance) ) {
				instance->file_offset = instance->parent->file_offset + offset;
			} else {
				instance->data = instance->parent->data + offset;
			}

		}

		instance->frame_num--;	// required
	}

	return instance;
}
// See model.h for usage.   I don't want to put the
// usage here because you need to see the #defines and structures
// this uses while reading the help.   
int model_collide(mc_info *mc_info_obj)
{
	Mc = mc_info_obj;

	MONITOR_INC(NumFVI,1);

	Mc->num_hits = 0;				// How many collisions were found
	Mc->shield_hit_tri = -1;	// Assume we won't hit any shield polygons
	Mc->hit_bitmap = -1;
	Mc->edge_hit = 0;

	if ( (Mc->flags & MC_CHECK_SHIELD) && (Mc->flags & MC_CHECK_MODEL) )	{
		Error( LOCATION, "Checking both shield and model!\n" );
		return 0;
	}

	//Fill in some global variables that all the model collide routines need internally.
	Mc_pm = model_get(Mc->model_num);
	Mc_orient = *Mc->orient;
	Mc_base = *Mc->pos;
	Mc_mag = vm_vec_dist( Mc->p0, Mc->p1 );
	Mc_edge_time = FLT_MAX;

	if ( Mc->model_instance_num >= 0 ) {
		Mc_pmi = model_get_instance(Mc->model_instance_num);
	} else {
		Mc_pmi = NULL;
	}

	// DA 11/19/98 - disable this check for rotating submodels
	// Don't do check if for very small movement
//	if (Mc_mag < 0.01f) {
//		return 0;
//	}

	float model_radius;		// How big is the model we're checking against
	int first_submodel;		// Which submodel gets returned as hit if MC_ONLY_SPHERE specified

	if ( (Mc->flags & MC_SUBMODEL) || (Mc->flags & MC_SUBMODEL_INSTANCE) )	{
		first_submodel = Mc->submodel_num;
		model_radius = Mc_pm->submodel[first_submodel].rad;
	} else {
		first_submodel = Mc_pm->detail[0];
		model_radius = Mc_pm->rad;
	}

	if ( Mc->flags & MC_CHECK_SPHERELINE ) {
		if ( Mc->radius <= 0.0f ) {
			Warning(LOCATION, "Attempting to collide with a sphere, but the sphere's radius is <= 0.0f!\n\n(model file is %s; submodel is %d, mc_flags are %d)", Mc_pm->filename, first_submodel, Mc->flags);
			return 0;
		}

		// Do a quick check on the Bounding Sphere
		if (fvi_segment_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius+Mc->radius) )	{
			if ( Mc->flags & MC_ONLY_SPHERE )	{
				Mc->hit_point = Mc->hit_point_world;
				Mc->hit_submodel = first_submodel;
				Mc->num_hits++;
				return (Mc->num_hits > 0);
			}
			// continue checking polygons.
		} else {
			return 0;
		}
	} else {
		int r;

		// Do a quick check on the Bounding Sphere
		if ( Mc->flags & MC_CHECK_RAY ) {
			r = fvi_ray_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius);
		} else {
			r = fvi_segment_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius);
		}
		if (r) {
			if ( Mc->flags & MC_ONLY_SPHERE ) {
				Mc->hit_point = Mc->hit_point_world;
				Mc->hit_submodel = first_submodel;
				Mc->num_hits++;
				return (Mc->num_hits > 0);
			}
			// continue checking polygons.
		} else {
			return 0;
		}

	}

	if ( Mc->flags & MC_SUBMODEL )	{
		// Check only one subobject
		mc_check_subobj( Mc->submodel_num );
		// Check submodel and any children
	} else if (Mc->flags & MC_SUBMODEL_INSTANCE) {
		mc_check_subobj(Mc->submodel_num);
	} else {
		// Check all the the highest detail model polygons and subobjects for intersections

		// Don't check it or its children if it is destroyed
		if (!Mc_pm->submodel[Mc_pm->detail[0]].blown_off)	{	
			mc_check_subobj( Mc_pm->detail[0] );
		}
	}


	//If we found a hit, then rotate it into world coordinates	
	if ( Mc->num_hits )	{
		if ( Mc->flags & MC_SUBMODEL )	{
			// If we're just checking one submodel, don't use normal instancing to find world points
			vm_vec_unrotate(&Mc->hit_point_world, &Mc->hit_point, Mc->orient);
			vm_vec_add2(&Mc->hit_point_world, Mc->pos);
		} else {
			if ( Mc_pmi ) {
				model_instance_find_world_point(&Mc->hit_point_world, &Mc->hit_point, Mc->model_instance_num, Mc->hit_submodel, Mc->orient, Mc->pos);
			} else {
				model_find_world_point(&Mc->hit_point_world, &Mc->hit_point, Mc->model_num, Mc->hit_submodel, Mc->orient, Mc->pos);
			}
		}
	}


	return Mc->num_hits;

}
Example #10
0
/**
 * Render a shield mesh in the global array ::Shield_hits[]
 */
void render_shield(int shield_num)
{
    int		i;
    vec3d	*centerp;
    matrix	*orient;
    object	*objp;
    ship		*shipp;
    ship_info	*si;

    if (Shield_hits[shield_num].type == SH_UNUSED)	{
        return;
    }

    Assert(Shield_hits[shield_num].objnum >= 0);

    objp = &Objects[Shield_hits[shield_num].objnum];

    if (objp->flags & OF_NO_SHIELDS)	{
        return;
    }

    //	If this object didn't get rendered, don't render its shields.  In fact, make the shield hit go away.
    if (!(objp->flags & OF_WAS_RENDERED)) {
        Shield_hits[shield_num].type = SH_UNUSED;
        return;
    }

    //	At detail levels 1, 3, animations play at double speed to reduce load.
    if ( (Detail.shield_effects == 1) || (Detail.shield_effects == 3) ) {
        Shield_hits[shield_num].start_time -= Frametime;
    }

    MONITOR_INC(NumShieldRend,1);

    shipp = &Ships[objp->instance];
    si = &Ship_info[shipp->ship_info_index];
    // objp, shipp, and si are now setup correctly

    //	If this ship is in its deathroll, make the shield hit effects go away faster.
    if (shipp->flags & SF_DYING)	{
        Shield_hits[shield_num].start_time -= fl2f(2*flFrametime);
    }

    //	Detail level stuff.  When lots of shield hits, maybe make them go away faster.
    if (Poly_count > 50) {
        if (Shield_hits[shield_num].start_time + (SHIELD_HIT_DURATION*50)/Poly_count < Missiontime) {
            Shield_hits[shield_num].type = SH_UNUSED;
            free_global_tri_records(shield_num);
            return;
        }
    } else if ((Shield_hits[shield_num].start_time + SHIELD_HIT_DURATION) < Missiontime) {
        Shield_hits[shield_num].type = SH_UNUSED;
        free_global_tri_records(shield_num);
        return;
    }

    orient = &objp->orient;
    centerp = &objp->pos;

    int bitmap_id, frame_num;

    // Do some sanity checking
    Assert( (si->species >= 0) && (si->species < (int)Species_info.size()) );

    generic_anim *sa = &Species_info[si->species].shield_anim;

    // don't try to draw if we don't have an ani
    if ( sa->first_frame >= 0 )
    {
        frame_num = fl2i( f2fl(Missiontime - Shield_hits[shield_num].start_time) * sa->num_frames );
        if ( frame_num >= sa->num_frames )	{
            frame_num = sa->num_frames - 1;
        } else if ( frame_num < 0 )	{
            mprintf(( "HEY! Missiontime went backwards! (Shield.cpp)\n" ));
            frame_num = 0;
        }
        bitmap_id = sa->first_frame + frame_num;

        float alpha = 0.9999f;
        if(The_mission.flags & MISSION_FLAG_FULLNEB) {
            alpha *= 0.85f;
        }

        gr_set_bitmap(bitmap_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );

        if ( (Detail.shield_effects == 1) || (Detail.shield_effects == 2) ) {
            if ( bitmap_id != - 1 ) {
                render_low_detail_shield_bitmap(&Global_tris[Shield_hits[shield_num].tri_list[0]], orient, centerp, Shield_hits[shield_num].rgb[0], Shield_hits[shield_num].rgb[1], Shield_hits[shield_num].rgb[2]);
            }
        } else {
            if ( bitmap_id != - 1 ) {
                for (i=0; i<Shield_hits[shield_num].num_tris; i++) {
                    render_shield_triangle(&Global_tris[Shield_hits[shield_num].tri_list[i]], orient, centerp, Shield_hits[shield_num].rgb[0], Shield_hits[shield_num].rgb[1], Shield_hits[shield_num].rgb[2]);
                }
            }
        }
    }
}
void particle_render_all()
{
	ubyte flags;
	float pct_complete;
	float alpha;
	vertex pos;
	vec3d ts, te, temp;
	int rotate = 1;
	int framenum, cur_frame;
	bool render_batch = false;
	int tmap_flags = TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD;

	if ( !Particles_enabled )
		return;

	MONITOR_INC( NumParticlesRend, Num_particles );	

	if ( Particles.empty() )
		return;

	for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p) {
		particle* part = *p;
		// skip back-facing particles (ripped from fullneb code)
		// Wanderer - add support for attached particles
		vec3d p_pos;
		if (part->attached_objnum >= 0) {
			vm_vec_unrotate(&p_pos, &part->pos, &Objects[part->attached_objnum].orient);
			vm_vec_add2(&p_pos, &Objects[part->attached_objnum].pos);
		} else {
			p_pos = part->pos;
		}

		if ( vm_vec_dot_to_point(&Eye_matrix.vec.fvec, &Eye_position, &p_pos) <= 0.0f ) {
			continue;
		}

		// calculate the alpha to draw at
		alpha = get_current_alpha(&p_pos);

		// if it's transparent then just skip it
		if (alpha <= 0.0f) {
			continue;
		}

		// make sure "rotate" is enabled for this particle
		rotate = 1;

		// if this is a tracer style particle, calculate tracer vectors
		if (part->tracer_length > 0.0f) {			
			ts = p_pos;
			temp = part->velocity;
			vm_vec_normalize_quick(&temp);
			vm_vec_scale_add(&te, &ts, &temp, part->tracer_length);

			// don't bother rotating
			rotate = 0;
		}

		// rotate the vertex
		if (rotate) {
			flags = g3_rotate_vertex( &pos, &p_pos );

			if ( flags ) {
				continue;
			}

			if (!Cmdline_nohtl)
				g3_transfer_vertex(&pos, &p_pos);
		}

		// pct complete for the particle
		pct_complete = part->age / part->max_life;

		// figure out which frame we should be using
		if (part->nframes > 1) {
			framenum = fl2i(pct_complete * part->nframes + 0.5);
			CLAMP(framenum, 0, part->nframes-1);

			cur_frame = part->reverse ? (part->nframes - framenum - 1) : framenum;
		} else {
			cur_frame = 0;
		}

		if (part->type == PARTICLE_DEBUG) {
			gr_set_color( 255, 0, 0 );
			g3_draw_sphere_ez( &p_pos, part->radius );
		} else {
			framenum = part->optional_data;

			Assert( cur_frame < part->nframes );

			// if this is a tracer style particle
			if (part->tracer_length > 0.0f) {
				batch_add_laser( framenum + cur_frame, &ts, part->radius, &te, part->radius );
			}
			// draw as a regular bitmap
			else {
				batch_add_bitmap( framenum + cur_frame, tmap_flags, &pos, part->particle_index % 8, part->radius, alpha );
			}

			render_batch = true;
		}
	}

	profile_begin("Batch Render");
	if (render_batch) {
		batch_render_all(Particle_buffer_object);
	}
	profile_end("Batch Render");
}
void particle_move_all(float frametime)
{
	MONITOR_INC( NumParticles, Num_particles );	

	if ( !Particles_enabled )
		return;

	if ( Particles.empty() )
		return;

	for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); )
	{	
		particle* part = *p;
		if (part->age == 0.0f) {
			part->age = 0.00001f;
		} else {
			part->age += frametime;
		}

		bool remove_particle = false;

		// if its time expired, remove it
		if (part->age > part->max_life) {
			// special case, if max_life is 0 then we want it to render at least once
			if ( (part->age > frametime) || (part->max_life > 0.0f) ) {
				remove_particle = true;
			}
		}

		// if the particle is attached to an object which has become invalid, kill it
		if (part->attached_objnum >= 0) {
			// if the signature has changed, or it's bogus, kill it
			if ( (part->attached_objnum >= MAX_OBJECTS) || (part->attached_sig != Objects[part->attached_objnum].signature) ) {
				remove_particle = true;
			}
		}

		if (remove_particle)
		{
			part->signature = 0;
			delete part;

			// if we're sitting on the very last particle, popping-back will invalidate the iterator!
			if (p + 1 == Particles.end())
			{
				Particles.pop_back();
				break;
			}
			else
			{
				*p = Particles.back();
				Particles.pop_back();
				continue;
			}
		}

		// move as a regular particle
		vm_vec_scale_add2( &part->pos, &part->velocity, frametime );

		// next particle
		++p;
	}
}