Exemple #1
0
/**
 * Render one triangle of a shield hit effect on one ship.
 * Each frame, the triangle needs to be rotated into global coords.
 *
 * @param trip		pointer to triangle in global array
 * @param orient	orientation of object shield is associated with
 * @param pos		center point of object
 * @param r			Red colour
 * @param g			Green colour
 * @param b			Blue colour
 */
void render_shield_triangle(gshield_tri *trip, matrix *orient, vec3d *pos, ubyte r, ubyte g, ubyte b)
{
    int		j;
    vec3d	pnt;
    vertex	*verts[3];
    vertex	points[3];

    if (trip->trinum == -1)
        return;	//	Means this is a quad, must have switched detail_level.

    for (j=0; j<3; j++ )	{
        // Rotate point into world coordinates
        vm_vec_unrotate(&pnt, &trip->verts[j].pos, orient);
        vm_vec_add2(&pnt, pos);

        // Pnt is now the x,y,z world coordinates of this vert.
        // For this example, I am just drawing a sphere at that point.

        if (!Cmdline_nohtl) g3_transfer_vertex(&points[j],&pnt);
        else g3_rotate_vertex(&points[j], &pnt);

        points[j].texture_position.u = trip->verts[j].u;
        points[j].texture_position.v = trip->verts[j].v;
        Assert((trip->verts[j].u >= 0.0f) && (trip->verts[j].u <= UV_MAX));
        Assert((trip->verts[j].v >= 0.0f) && (trip->verts[j].v <= UV_MAX));
        verts[j] = &points[j];
    }

    verts[0]->r = r;
    verts[0]->g = g;
    verts[0]->b = b;
    verts[1]->r = r;
    verts[1]->g = g;
    verts[1]->b = b;
    verts[2]->r = r;
    verts[2]->g = g;
    verts[2]->b = b;

    vec3d	norm;
    Poly_count++;
    vm_vec_perp(&norm,&verts[0]->world,&verts[1]->world,&verts[2]->world);

    int flags=TMAP_FLAG_TEXTURED | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD;
    if (!Cmdline_nohtl) flags |= TMAP_HTL_3D_UNLIT;

    if ( vm_vec_dot(&norm,&verts[1]->world ) >= 0.0 )	{
        vertex	*vertlist[3];
        vertlist[0] = verts[2];
        vertlist[1] = verts[1];
        vertlist[2] = verts[0];
        g3_draw_poly( 3, vertlist, flags);
    } else {
        g3_draw_poly( 3, verts, flags);
    }
}
void HudGaugeRadarDradis::drawContact(vec3d *pnt, int idx, int clr_idx, float  /*dist*/, float alpha, float scale_factor)
{
	vec3d  p;
	int h, w;
	vertex vert;
	float aspect_mp;

	if ((sub_y_clip && (pnt->xyz.y > 0)) || ((!sub_y_clip) && (pnt->xyz.y <= 0)))
		return;

    memset(&vert, 0, sizeof(vert));
    
	vm_vec_rotate(&p, pnt,  &vmd_identity_matrix); 
	g3_transfer_vertex(&vert, &p);
	
	float sizef = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f) * scale_factor;

    if ( clr_idx >= 0 ) {
        bm_get_info(clr_idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        //gr_set_bitmap(clr_idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        //g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);
		material mat_params;
		material_set_unlit_color(&mat_params, clr_idx, &Color_bright_white, alpha, true, false);
		g3_render_rect_oriented(&mat_params, &p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f);
    }
    
    if ( idx >= 0 ) {
        bm_get_info(idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        //gr_set_bitmap(idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        //g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_FLAG_BW_TEXTURE | TMAP_HTL_3D_UNLIT);
		material mat_params;
		material_set_unlit_color(&mat_params, idx, &gr_screen.current_color, alpha, true, false);
		g3_render_rect_oriented(&mat_params, &p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f);
    }
}
Exemple #3
0
void render_low_detail_shield_bitmap(gshield_tri *trip, matrix *orient, vec3d *pos, ubyte r, ubyte g, ubyte b)
{
    int		j;
    vec3d	pnt;
    vertex	verts[4];

    for (j=0; j<4; j++ )	{
        // Rotate point into world coordinates
        vm_vec_unrotate(&pnt, &trip->verts[j].pos, orient);
        vm_vec_add2(&pnt, pos);

        // Pnt is now the x,y,z world coordinates of this vert.
        if(!Cmdline_nohtl) g3_transfer_vertex(&verts[j], &pnt);
        else g3_rotate_vertex(&verts[j], &pnt);
        verts[j].texture_position.u = trip->verts[j].u;
        verts[j].texture_position.v = trip->verts[j].v;
    }

    verts[0].r = r;
    verts[0].g = g;
    verts[0].b = b;
    verts[1].r = r;
    verts[1].g = g;
    verts[1].b = b;
    verts[2].r = r;
    verts[2].g = g;
    verts[2].b = b;
    verts[3].r = r;
    verts[3].g = g;
    verts[3].b = b;

    vec3d	norm;
    vm_vec_perp(&norm, &trip->verts[0].pos, &trip->verts[1].pos, &trip->verts[2].pos);
    vertex	*vertlist[4];
    if ( vm_vec_dot(&norm, &trip->verts[1].pos ) < 0.0 )	{
        vertlist[0] = &verts[3];
        vertlist[1] = &verts[2];
        vertlist[2] = &verts[1];
        vertlist[3] = &verts[0];
        g3_draw_poly( 4, vertlist, TMAP_FLAG_TEXTURED | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_HTL_3D_UNLIT);
    } else {
        vertlist[0] = &verts[0];
        vertlist[1] = &verts[1];
        vertlist[2] = &verts[2];
        vertlist[3] = &verts[3];
        g3_draw_poly( 4, vertlist, TMAP_FLAG_TEXTURED | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_HTL_3D_UNLIT);
    }
}
// ------------------------------------------------------------------------------------
// shockwave_render()
//
//	Draw the shockwave identified by handle
//
//	input:	objp	=>		pointer to shockwave object
//
void shockwave_render(object *objp)
{
	shockwave		*sw;
	shockwave_info	*si;
	vertex			p;

	Assert(objp->type == OBJ_SHOCKWAVE);
	Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);

	sw = &Shockwaves[objp->instance];
	si = &Shockwave_info[sw->shockwave_info_index];

	if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){
		return;
	}

	if ( (sw->current_bitmap < 0) && (sw->model_id < 0) )
		return;

	// turn off fogging
	if(The_mission.flags & MISSION_FLAG_FULLNEB){
		gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
	}

	if (sw->model_id > -1) {
		float model_Interp_scale_xyz = sw->radius / 50.0f;

		model_set_warp_globals( model_Interp_scale_xyz, model_Interp_scale_xyz, model_Interp_scale_xyz, -1, 1.0f - (sw->radius/sw->outer_radius) );
		
		float dist = vm_vec_dist_quick( &sw->pos, &Eye_position );

		model_set_detail_level((int)(dist / (sw->radius * 10.0f)));
		model_render( sw->model_id, &Objects[sw->objnum].orient, &sw->pos, MR_NO_LIGHTING | MR_NO_FOGGING | MR_NORMAL | MR_CENTER_ALPHA | MR_NO_CULL, sw->objnum);

		model_set_warp_globals();
	}else{
		if (!Cmdline_nohtl) {
			g3_transfer_vertex(&p, &sw->pos);
		} else {
			g3_rotate_vertex(&p, &sw->pos);
		}
	
		gr_set_bitmap(sw->current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f );
		g3_draw_rotated_bitmap(&p, fl_radian(sw->rot_angles.p), sw->radius, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);	
	}
}
void HudGaugeRadarDradis::drawContact(vec3d *pnt, int idx, int clr_idx, float dist, float alpha, float scale_factor)
{
	vec3d  p;
	int h, w;
	vertex vert;
	float aspect_mp;

	if ((sub_y_clip && (pnt->xyz.y > 0)) || ((!sub_y_clip) && (pnt->xyz.y <= 0)))
		return;

    memset(&vert, 0, sizeof(vert));
    
	vm_vec_rotate(&p, pnt,  &vmd_identity_matrix); 
	g3_transfer_vertex(&vert, &p);
	
	float sizef = fl_sqrt(vm_vec_dist(&Orb_eye_position, pnt) * 8.0f) * scale_factor;

    if ( clr_idx >= 0 ) {
        bm_get_info(clr_idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        gr_set_bitmap(clr_idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);
    }
    
    if ( idx >= 0 ) {
        bm_get_info(idx, &w, &h);
        
        if (h == w) {
            aspect_mp = 1.0f;
        } else {
            aspect_mp = (((float) h) / ((float) w));
        }
        
        gr_set_bitmap(idx, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha);
        g3_draw_polygon(&p, &vmd_identity_matrix, sizef/35.0f, aspect_mp*sizef/35.0f, TMAP_FLAG_TEXTURED | TMAP_FLAG_BW_TEXTURE | TMAP_HTL_3D_UNLIT);
    }
}
/*
0----1
|\   |
|  \ |
3----2
*/
void geometry_batcher::draw_bitmap(vertex *pnt, int orient, float rad, float depth)
{
	float radius = rad;
	rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and height rad

	vec3d PNT(pnt->world);
	vec3d p[4];
	vec3d fvec, rvec, uvec;
	vertex *P = &vert[n_to_render * 3];
	float *R = &radius_list[n_to_render * 3];

	// get the direction from the point to the eye
	vm_vec_sub(&fvec, &View_position, &PNT);
	vm_vec_normalize_safe(&fvec);

	// get an up vector in the general direction of what we want
	uvec = View_matrix.vec.uvec;

	// make a right vector from the f and up vector, this r vec is exactly what we want, so...
	vm_vec_cross(&rvec, &View_matrix.vec.fvec, &uvec);
	vm_vec_normalize_safe(&rvec);

	// fix the u vec with it
	vm_vec_cross(&uvec, &View_matrix.vec.fvec, &rvec);

	// move the center of the sprite based on the depth parameter
	if ( depth != 0.0f )
		vm_vec_scale_add(&PNT, &PNT, &fvec, depth);

	// move one of the verts to the left
	vm_vec_scale_add(&p[0], &PNT, &rvec, rad);

	// and one to the right
	vm_vec_scale_add(&p[2], &PNT, &rvec, -rad);

	// now move all oof the verts to were they need to be
	vm_vec_scale_add(&p[1], &p[2], &uvec, rad);
	vm_vec_scale_add(&p[3], &p[0], &uvec, -rad);
	vm_vec_scale_add(&p[0], &p[0], &uvec, rad);
	vm_vec_scale_add(&p[2], &p[2], &uvec, -rad);


	//move all the data from the vecs into the verts
	//tri 1
	g3_transfer_vertex(&P[5], &p[3]);
	g3_transfer_vertex(&P[4], &p[2]);
	g3_transfer_vertex(&P[3], &p[1]);

	//tri 2
	g3_transfer_vertex(&P[2], &p[3]);
	g3_transfer_vertex(&P[1], &p[1]);
	g3_transfer_vertex(&P[0], &p[0]);

	// set up the UV coords
	if ( orient & 1 ) {
		// tri 1
		P[5].texture_position.u = 1.0f;
		P[4].texture_position.u = 0.0f;
		P[3].texture_position.u = 0.0f;
		// tri 2
		P[2].texture_position.u = 1.0f;
		P[1].texture_position.u = 0.0f;
		P[0].texture_position.u = 1.0f;
	} else {
		// tri 1
		P[5].texture_position.u = 0.0f;
		P[4].texture_position.u = 1.0f;
		P[3].texture_position.u = 1.0f;
		// tri 2
		P[2].texture_position.u = 0.0f;
		P[1].texture_position.u = 1.0f;
		P[0].texture_position.u = 0.0f;
	}

	if ( orient & 2 ) {
		// tri 1
		P[5].texture_position.v = 1.0f;
		P[4].texture_position.v = 1.0f;
		P[3].texture_position.v = 0.0f;
		// tri 2
		P[2].texture_position.v = 1.0f;
		P[1].texture_position.v = 0.0f;
		P[0].texture_position.v = 0.0f;
	} else {
		// tri 1
		P[5].texture_position.v = 0.0f;
		P[4].texture_position.v = 0.0f;
		P[3].texture_position.v = 1.0f;
		// tri 2
		P[2].texture_position.v = 0.0f;
		P[1].texture_position.v = 1.0f;
		P[0].texture_position.v = 1.0f;
	}

	for (int i = 0; i < 6 ; i++) {
		P[i].r = pnt->r;
		P[i].g = pnt->g;
		P[i].b = pnt->b;
		P[i].a = pnt->a;

		R[i] = radius;
	}

	n_to_render += 2;
}
void shockwave_render(object *objp, draw_list *scene)
{
	shockwave		*sw;
	vertex			p;

	Assert(objp->type == OBJ_SHOCKWAVE);
	Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);

	sw = &Shockwaves[objp->instance];

	if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){
		return;
	}

	if ( (sw->current_bitmap < 0) && (sw->model_id < 0) )
		return;

	if (sw->model_id > -1) {
		vec3d scale;
		scale.xyz.x = scale.xyz.y = scale.xyz.z = sw->radius / 50.0f;

		model_render_params render_info;

		render_info.set_warp_params(-1, 1.0f - (sw->radius/sw->outer_radius), scale);

		float dist = vm_vec_dist_quick( &sw->pos, &Eye_position );

		render_info.set_detail_level_lock((int)(dist / (sw->radius * 10.0f)));
		render_info.set_flags(MR_NO_LIGHTING | MR_NO_FOGGING | MR_NORMAL | MR_CENTER_ALPHA | MR_NO_CULL | MR_NO_BATCH);
		render_info.set_object_number(sw->objnum);

		model_render_queue( &render_info, scene, sw->model_id, &Objects[sw->objnum].orient, &sw->pos);

		if ( Cmdline_fb_explosions ) {
			g3_transfer_vertex(&p, &sw->pos);

			distortion_add_bitmap_rotated(
				Shockwave_info[1].bitmap_id+shockwave_get_framenum(objp->instance, 94), 
				TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, 
				&p, 
				fl_radians(sw->rot_angles.p), 
				sw->radius,
				((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f
				);
		}
	} else {
		if (!Cmdline_nohtl) {
			g3_transfer_vertex(&p, &sw->pos);
		} else {
			g3_rotate_vertex(&p, &sw->pos);
		}

		if ( Cmdline_fb_explosions ) {
			distortion_add_bitmap_rotated(
				sw->current_bitmap, 
				TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, 
				&p, 
				fl_radians(sw->rot_angles.p), 
				sw->radius,
				((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f
			);
		}

		batch_add_bitmap_rotated(
			sw->current_bitmap, 
			TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
			&p, 
			fl_radians(sw->rot_angles.p), 
			sw->radius
		);
	}
}
/**
 * Draw the shockwave identified by handle
 *
 * @param objp	pointer to shockwave object
 */
void shockwave_render_DEPRECATED(object *objp)
{
	shockwave		*sw;
	vertex			p;

	Assert(objp->type == OBJ_SHOCKWAVE);
	Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);

    memset(&p, 0, sizeof(p));
	sw = &Shockwaves[objp->instance];

	if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){
		return;
	}

	if ( (sw->current_bitmap < 0) && (sw->model_id < 0) )
		return;

	// turn off fogging
	if(The_mission.flags & MISSION_FLAG_FULLNEB){
		gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
	}

	if (sw->model_id > -1) {
		float model_Interp_scale_xyz = sw->radius / 50.0f;

		model_set_warp_globals( model_Interp_scale_xyz, model_Interp_scale_xyz, model_Interp_scale_xyz, -1, 1.0f - (sw->radius/sw->outer_radius) );
		
		float dist = vm_vec_dist_quick( &sw->pos, &Eye_position );

		model_set_detail_level((int)(dist / (sw->radius * 10.0f)));
		model_render_DEPRECATED( sw->model_id, &Objects[sw->objnum].orient, &sw->pos, MR_DEPRECATED_NO_LIGHTING | MR_DEPRECATED_NO_FOGGING | MR_DEPRECATED_NORMAL | MR_DEPRECATED_CENTER_ALPHA | MR_DEPRECATED_NO_CULL, sw->objnum);

		model_set_warp_globals();
		if(Cmdline_fb_explosions)
		{
			g3_transfer_vertex(&p, &sw->pos);
				
			distortion_add_bitmap_rotated(
				Shockwave_info[1].bitmap_id+shockwave_get_framenum(objp->instance, 94), 
				TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, 
				&p, 
				fl_radians(sw->rot_angles.p), 
				sw->radius,
				((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f
			);
		}
	}else{
		if (!Cmdline_nohtl) {
			g3_transfer_vertex(&p, &sw->pos);
		} else {
			g3_rotate_vertex(&p, &sw->pos);
		}
		if(Cmdline_fb_explosions)
		{
			distortion_add_bitmap_rotated(
				sw->current_bitmap, 
				TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, 
				&p, 
				fl_radians(sw->rot_angles.p), 
				sw->radius,
				((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f
			);
		}
		batch_add_bitmap_rotated(
			sw->current_bitmap, 
			TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
			&p, 
			fl_radians(sw->rot_angles.p), 
			sw->radius
		);
	}
}
int batch_add_polygon(int texture, int tmap_flags, vec3d *pos, matrix *orient, float width, float height, float alpha)
{
	//idiot-proof
	if(width == 0 || height == 0)
		return 0;

	Assert(pos != NULL);
	Assert(orient != NULL);

	//Let's begin.

	const int NUM_VERTICES = 4;
	vec3d p[NUM_VERTICES] = { ZERO_VECTOR };
	vertex v[NUM_VERTICES] = { vertex() };

	p[0].xyz.x = width;
	p[0].xyz.y = height;

	p[1].xyz.x = -width;
	p[1].xyz.y = height;

	p[2].xyz.x = -width;
	p[2].xyz.y = -height;

	p[3].xyz.x = width;
	p[3].xyz.y = -height;

	for(int i = 0; i < NUM_VERTICES; i++)
	{
		vec3d tmp = vmd_zero_vector;

		//Rotate correctly
		vm_vec_unrotate(&tmp, &p[i], orient);
		//Move to point in space
		vm_vec_add2(&tmp, pos);

		//Convert to vertex
		g3_transfer_vertex(&v[i], &tmp);
	}

	v[0].texture_position.u = 1.0f;
	v[0].texture_position.v = 0.0f;

	v[1].texture_position.u = 0.0f;
	v[1].texture_position.v = 0.0f;

	v[2].texture_position.u = 0.0f;
	v[2].texture_position.v = 1.0f;

	v[3].texture_position.u = 1.0f;
	v[3].texture_position.v = 1.0f;

	if (texture < 0) {
		Int3();
		return 1;
	}

	batch_item *item = NULL;

	SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);

	if ( !geometry_map.empty() && it != geometry_map.end() ) {
		item = &it->second;
	} else {
		item = &geometry_map[texture];
		item->texture = texture;
	}

	Assertion( (item->laser == false), "Particle effect %s used as laser glow or laser bitmap\n", bm_get_filename(texture) );

	item->tmap_flags = tmap_flags;
	item->alpha = alpha;

	item->batch.add_allocate(1);

	item->batch.draw_quad(v);

	return 0;
}
Exemple #10
0
float g3_draw_laser_htl(vec3d* p0, float width1, vec3d* p1, float width2, int r, int g, int b, uint tmap_flags)
{
	width1 *= 0.5f;
	width2 *= 0.5f;
	vec3d uvec, fvec, rvec, center, reye;

	vm_vec_sub(&fvec, p0, p1);
	vm_vec_normalize_safe(&fvec);

	vm_vec_avg(&center, p0, p1); //needed for the return value only
	vm_vec_sub(&reye, &Eye_position, &center);
	vm_vec_normalize(&reye);

	vm_vec_crossprod(&uvec, &fvec, &reye);
	vm_vec_normalize(&uvec);
	vm_vec_crossprod(&fvec, &uvec, &reye);
	vm_vec_normalize(&fvec);

	//	if(vm_vec_mag_squared(&uvec)==0) uvec.xyz.y = 1.0f; //in case fvec is exactly equal to matrx.rvec, stick some arbitrary value in uvec
	//normalize new perpendicular vector
	//	vm_vec_normalize(&uvec);

	//now recompute right vector, in case it wasn't entirely perpendiclar
	vm_vec_crossprod(&rvec, &uvec, &fvec);

	// Now have uvec, which is up vector and rvec which is the normal
	// of the face.

	int i;
	vec3d start, end;
	vm_vec_scale_add(&start, p0, &fvec, -width1);
	vm_vec_scale_add(&end, p1, &fvec, width2);
	vec3d vecs[4];
	vertex pts[4];
	vertex* ptlist[8] =
	{
		&pts[3],
		&pts[2],
		&pts[1],
		&pts[0],
		&pts[0],
		&pts[1],
		&pts[2],
		&pts[3]
	};

	vm_vec_scale_add(&vecs[0], &start, &uvec, width1);
	vm_vec_scale_add(&vecs[1], &end, &uvec, width2);
	vm_vec_scale_add(&vecs[2], &end, &uvec, -width2);
	vm_vec_scale_add(&vecs[3], &start, &uvec, -width1);

	for (i = 0; i < 4; i++)
	{
		g3_transfer_vertex(&pts[i], &vecs[i]);
	}
	ptlist[0]->u = 0.0f;
	ptlist[0]->v = 0.0f;
	ptlist[1]->u = 1.0f;
	ptlist[1]->v = 0.0f;
	ptlist[2]->u = 1.0f;
	ptlist[2]->v = 1.0f;
	ptlist[3]->u = 0.0f;
	ptlist[3]->v = 1.0f;
	ptlist[0]->r = (ubyte)r;
	ptlist[0]->g = (ubyte)g;
	ptlist[0]->b = (ubyte)b;
	ptlist[0]->a = 255;
	ptlist[1]->r = (ubyte)r;
	ptlist[1]->g = (ubyte)g;
	ptlist[1]->b = (ubyte)b;
	ptlist[1]->a = 255;
	ptlist[2]->r = (ubyte)r;
	ptlist[2]->g = (ubyte)g;
	ptlist[2]->b = (ubyte)b;
	ptlist[2]->a = 255;
	ptlist[3]->r = (ubyte)r;
	ptlist[3]->g = (ubyte)g;
	ptlist[3]->b = (ubyte)b;
	ptlist[3]->a = 255;

	gr_tmapper(4, ptlist, tmap_flags | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT);

	return center.xyz.z;
}
void geometry_batcher::draw_beam(vec3d *start, vec3d *end, float width, float intensity, float offset)
{
	vec3d p[4];
	vertex *P = &vert[n_to_render * 3];
	float *R = &radius_list[n_to_render * 3];

	vec3d fvec, uvecs, uvece, evec;

	vm_vec_sub(&fvec, start, end);
	vm_vec_normalize_safe(&fvec);

	vm_vec_sub(&evec, &View_position, start);
	vm_vec_normalize_safe(&evec);

	vm_vec_cross(&uvecs, &fvec, &evec);
	vm_vec_normalize_safe(&uvecs);

	vm_vec_sub(&evec, &View_position, end);
	vm_vec_normalize_safe(&evec);

	vm_vec_cross(&uvece, &fvec, &evec);
	vm_vec_normalize_safe(&uvece);


	vm_vec_scale_add(&p[0], start, &uvecs, width);
	vm_vec_scale_add(&p[1], end, &uvece, width);
	vm_vec_scale_add(&p[2], end, &uvece, -width);
	vm_vec_scale_add(&p[3], start, &uvecs, -width);


	//move all the data from the vecs into the verts
	//tri 1
	g3_transfer_vertex(&P[0], &p[3]);
	g3_transfer_vertex(&P[1], &p[2]);
	g3_transfer_vertex(&P[2], &p[1]);

	//tri 2
	g3_transfer_vertex(&P[3], &p[3]);
	g3_transfer_vertex(&P[4], &p[1]);
	g3_transfer_vertex(&P[5], &p[0]);

	//set up the UV coords
	//tri 1
	P[0].texture_position.u = 0.0f;	P[0].texture_position.v = 0.0f;
	P[1].texture_position.u = 1.0f;	P[1].texture_position.v = 0.0f;
	P[2].texture_position.u = 1.0f;	P[2].texture_position.v = 1.0f;

	//tri 2
	P[3].texture_position.u = 0.0f;	P[3].texture_position.v = 0.0f;
	P[4].texture_position.u = 1.0f;	P[4].texture_position.v = 1.0f;
	P[5].texture_position.u = 0.0f;	P[5].texture_position.v = 1.0f;

	ubyte _color = (ubyte)(255.0f * intensity);

	for(int i = 0; i < 6; i++){
		P[i].r = P[i].g = P[i].b = P[i].a = _color;
		if(offset > 0.0f) {
			R[i] = offset;
		} else {
			R[i] = width;
		}
	}

	n_to_render += 2;
	use_radius = true;
}
float geometry_batcher::draw_laser(vec3d *p0, float width1, vec3d *p1, float width2, int r, int g, int b)
{
	width1 *= 0.5f;
	width2 *= 0.5f;

	vec3d uvec, fvec, rvec, center, reye;

	vm_vec_sub( &fvec, p0, p1 );
	vm_vec_normalize_safe( &fvec );

	vm_vec_avg( &center, p0, p1 ); // needed for the return value only
	vm_vec_sub(&reye, &Eye_position, &center);
	vm_vec_normalize(&reye);

	// compute the up vector
	vm_vec_cross(&uvec, &fvec, &reye);
	vm_vec_normalize_safe(&uvec);
	// ... the forward vector
	vm_vec_cross(&fvec, &uvec, &reye);
	vm_vec_normalize_safe(&fvec);
	// now recompute right vector, in case it wasn't entirely perpendiclar
	vm_vec_cross(&rvec, &uvec, &fvec);

	// Now have uvec, which is up vector and rvec which is the normal
	// of the face.

	vec3d start, end;

	vm_vec_scale_add(&start, p0, &fvec, -width1);
	vm_vec_scale_add(&end, p1, &fvec, width2);

	vec3d vecs[4];

	vertex *pts = &vert[n_to_render * 3];

	vm_vec_scale_add( &vecs[0], &end, &uvec, width2 );
	vm_vec_scale_add( &vecs[1], &start, &uvec, width1 );
	vm_vec_scale_add( &vecs[2], &start, &uvec, -width1 );
	vm_vec_scale_add( &vecs[3], &end, &uvec, -width2 );

	g3_transfer_vertex( &pts[0], &vecs[0] );
	g3_transfer_vertex( &pts[1], &vecs[1] );
	g3_transfer_vertex( &pts[2], &vecs[2] );

	g3_transfer_vertex( &pts[3], &vecs[0] );
	g3_transfer_vertex( &pts[4], &vecs[2] );
	g3_transfer_vertex( &pts[5], &vecs[3] );

	pts[0].texture_position.u = 1.0f;
	pts[0].texture_position.v = 0.0f;
	pts[1].texture_position.u = 0.0f;
	pts[1].texture_position.v = 0.0f;
	pts[2].texture_position.u = 0.0f;
	pts[2].texture_position.v = 1.0f;

	pts[3].texture_position.u = 1.0f;
	pts[3].texture_position.v = 0.0f;
	pts[4].texture_position.u = 0.0f;
	pts[4].texture_position.v = 1.0f;
	pts[5].texture_position.u = 1.0f;
	pts[5].texture_position.v = 1.0f;

	pts[0].r = (ubyte)r;
	pts[0].g = (ubyte)g;
	pts[0].b = (ubyte)b;
	pts[0].a = 255;
	pts[1].r = (ubyte)r;
	pts[1].g = (ubyte)g;
	pts[1].b = (ubyte)b;
	pts[1].a = 255;
	pts[2].r = (ubyte)r;
	pts[2].g = (ubyte)g;
	pts[2].b = (ubyte)b;
	pts[2].a = 255;
	pts[3].r = (ubyte)r;
	pts[3].g = (ubyte)g;
	pts[3].b = (ubyte)b;
	pts[3].a = 255;
	pts[4].r = (ubyte)r;
	pts[4].g = (ubyte)g;
	pts[4].b = (ubyte)b;
	pts[4].a = 255;
	pts[5].r = (ubyte)r;
	pts[5].g = (ubyte)g;
	pts[5].b = (ubyte)b;
	pts[5].a = 255;


	n_to_render += 2;
	use_radius = false;

	return center.xyz.z;
}
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 geometry_batcher::draw_bitmap(vertex *pnt, float rad, float angle, float depth)
{
	float radius = rad;
	rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and height rad

	extern float Physics_viewer_bank;
	angle -= Physics_viewer_bank;

	if ( angle < 0.0f )
		angle += PI2;
	else if ( angle > PI2 )
		angle -= PI2;

	vec3d PNT(pnt->world);
	vec3d p[4];
	vec3d fvec, rvec, uvec;
	vertex *P = &vert[n_to_render * 3];
	float *R = &radius_list[n_to_render * 3];

	vm_vec_sub(&fvec, &View_position, &PNT);
	vm_vec_normalize_safe(&fvec);

	vm_rot_point_around_line(&uvec, &View_matrix.vec.uvec, angle, &vmd_zero_vector, &View_matrix.vec.fvec);

	vm_vec_cross(&rvec, &View_matrix.vec.fvec, &uvec);
	vm_vec_normalize_safe(&rvec);
	vm_vec_cross(&uvec, &View_matrix.vec.fvec, &rvec);

	vm_vec_scale_add(&PNT, &PNT, &fvec, depth);
	vm_vec_scale_add(&p[0], &PNT, &rvec, rad);
	vm_vec_scale_add(&p[2], &PNT, &rvec, -rad);

	vm_vec_scale_add(&p[1], &p[2], &uvec, rad);
	vm_vec_scale_add(&p[3], &p[0], &uvec, -rad);
	vm_vec_scale_add(&p[0], &p[0], &uvec, rad);
	vm_vec_scale_add(&p[2], &p[2], &uvec, -rad);


	//move all the data from the vecs into the verts
	//tri 1
	g3_transfer_vertex(&P[5], &p[3]);
	g3_transfer_vertex(&P[4], &p[2]);
	g3_transfer_vertex(&P[3], &p[1]);

	//tri 2
	g3_transfer_vertex(&P[2], &p[3]);
	g3_transfer_vertex(&P[1], &p[1]);
	g3_transfer_vertex(&P[0], &p[0]);

	//tri 1
	P[5].texture_position.u = 0.0f;	P[5].texture_position.v = 0.0f;
	P[4].texture_position.u = 1.0f;	P[4].texture_position.v = 0.0f;
	P[3].texture_position.u = 1.0f;	P[3].texture_position.v = 1.0f;

	//tri 2
	P[2].texture_position.u = 0.0f;	P[2].texture_position.v = 0.0f;
	P[1].texture_position.u = 1.0f;	P[1].texture_position.v = 1.0f;
	P[0].texture_position.u = 0.0f;	P[0].texture_position.v = 1.0f;

	for (int i = 0; i < 6 ; i++) {
		P[i].r = pnt->r;
		P[i].g = pnt->g;
		P[i].b = pnt->b;
		P[i].a = pnt->a;

		R[i] = radius;
	}

	n_to_render += 2;
}
void warpin_render(object *obj, matrix *orient, vec3d *pos, int texture_bitmap_num, float radius, float life_percent, float max_radius, int warp_3d)
{
	vec3d center;
	vec3d vecs[5];
	vertex verts[5];
	int saved_gr_zbuffering = gr_zbuffer_get();

	gr_zbuffer_set(GR_ZBUFF_READ);

	vm_vec_scale_add( &center, pos, &orient->vec.fvec, -(max_radius/2.5f)/3.0f );


	if (Warp_glow_bitmap >= 0) {
		float r = radius;
		bool render_it = true;

		#define OUT_PERCENT1 0.80f
		#define OUT_PERCENT2 0.90f

		#define IN_PERCENT1 0.10f
		#define IN_PERCENT2 0.20f

		if (Cmdline_warp_flash)
		{
			if ( (life_percent >= IN_PERCENT1) && (life_percent < IN_PERCENT2) ) {
				r *= (life_percent - IN_PERCENT1) / (IN_PERCENT2 - IN_PERCENT1);
				//render_it = true;
			} else if ( (life_percent >= OUT_PERCENT1) && (life_percent < OUT_PERCENT2) ) {
				r *= (OUT_PERCENT2 - life_percent) / (OUT_PERCENT2 - OUT_PERCENT1);
				//render_it = true;
			}
		}

		if (render_it) {
			// Add in noise 
			int noise_frame = fl2i(Missiontime/15.0f) % NOISE_NUM_FRAMES;

			r *= (0.40f + Noise[noise_frame] * 0.30f);

			// Bobboau's warp thingie, toggled by cmdline
			if (Cmdline_warp_flash) {
				r += powf((2.0f * life_percent) - 1.0f, 24.0f) * max_radius * 1.5f;
			}

			vecs[4] = center;
			verts[4].texture_position.u = 0.5f; verts[4].texture_position.v = 0.5f; 

			if (Cmdline_nohtl) {
				g3_rotate_vertex( &verts[4], &vecs[4] );
			} else {
				g3_transfer_vertex( &verts[4], &vecs[4] );
			}

			float alpha = (The_mission.flags & MISSION_FLAG_FULLNEB) ? (1.0f - neb2_get_fog_intensity(obj)) : 1.0f;
			gr_set_bitmap( Warp_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );

			g3_draw_bitmap( &verts[4], 0, r, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT );
		}
	}

	if ( (Warp_model >= 0) && (warp_3d || Cmdline_3dwarp) ) {
		float scale = radius / 25.0f;
		model_set_warp_globals(scale, scale, scale, texture_bitmap_num, (radius/max_radius) );

		float dist = vm_vec_dist_quick( pos, &Eye_position );
		model_set_detail_level((int)(dist / (radius * 10.0f)));

		model_render( Warp_model, orient, pos, MR_LOCK_DETAIL | MR_NO_LIGHTING | MR_NORMAL | MR_NO_FOGGING | MR_NO_CULL );

		model_set_warp_globals();
	} else {
		float Grid_depth = radius/2.5f;

		gr_set_bitmap( texture_bitmap_num, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f );	

		vm_vec_scale_add( &vecs[0], &center, &orient->vec.uvec, radius );
		vm_vec_scale_add2( &vecs[0], &orient->vec.rvec, -radius );
		vm_vec_scale_add2( &vecs[0], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[1], &center, &orient->vec.uvec, radius );
		vm_vec_scale_add2( &vecs[1], &orient->vec.rvec, radius );
		vm_vec_scale_add2( &vecs[1], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[2], &center, &orient->vec.uvec, -radius );
		vm_vec_scale_add2( &vecs[2], &orient->vec.rvec, radius );
		vm_vec_scale_add2( &vecs[2], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[3], &center, &orient->vec.uvec, -radius );
		vm_vec_scale_add2( &vecs[3], &orient->vec.rvec, -radius );
		vm_vec_scale_add2( &vecs[3], &orient->vec.fvec, Grid_depth );

	//	vm_vec_scale_add( &vecs[4], ¢er, &orient->vec.fvec, -Grid_depth );
		vecs[4] = center;

		verts[0].texture_position.u = 0.01f;
		verts[0].texture_position.v = 0.01f;
		
		verts[1].texture_position.u = 0.99f;
		verts[1].texture_position.v = 0.01f;

		verts[2].texture_position.u = 0.99f;
		verts[2].texture_position.v = 0.99f;

		verts[3].texture_position.u = 0.01f;
		verts[3].texture_position.v = 0.99f;

		verts[4].texture_position.u = 0.5f;
		verts[4].texture_position.v = 0.5f; 

		if (Cmdline_nohtl) {
			g3_rotate_vertex( &verts[0], &vecs[0] );
			g3_rotate_vertex( &verts[1], &vecs[1] );
			g3_rotate_vertex( &verts[2], &vecs[2] );
			g3_rotate_vertex( &verts[3], &vecs[3] );
			g3_rotate_vertex( &verts[4], &vecs[4] );
		} else {
			g3_transfer_vertex( &verts[0], &vecs[0] );
			g3_transfer_vertex( &verts[1], &vecs[1] );
			g3_transfer_vertex( &verts[2], &vecs[2] );
			g3_transfer_vertex( &verts[3], &vecs[3] );
			g3_transfer_vertex( &verts[4], &vecs[4] );
		}

		int cull = gr_set_cull(0); // fixes rendering problem in D3D - taylor
		draw_face( &verts[0], &verts[4], &verts[1] );
		draw_face( &verts[1], &verts[4], &verts[2] );
		draw_face( &verts[4], &verts[3], &verts[2] );
		draw_face( &verts[0], &verts[3], &verts[4] );
		gr_set_cull(cull);
	}

	if (Warp_ball_bitmap > -1 && Cmdline_warp_flash == 1) {
		flash_ball warp_ball(20, .1f,.25f, &vmd_z_vector, &vmd_zero_vector, 4.0f, 0.5f);
		float adg = (2.0f * life_percent) - 1.0f;
		float pct = (powf(adg, 4.0f) - powf(adg, 128.0f)) * 4.0f;

		if (pct > 0.00001f) {
			g3_start_instance_matrix(pos, orient, true);

			gr_set_bitmap(Warp_ball_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.9999f);		

			warp_ball.render(max_radius * pct * 0.5f, adg * adg, adg * adg * 6.0f);

			g3_done_instance(true);
		}
	}

	gr_zbuffer_set( saved_gr_zbuffering );
}
float g3_draw_laser_htl(vec3d *p0,float width1,vec3d *p1,float width2, int r, int g, int b, uint tmap_flags)
{
	width1 *= 0.5f;
	width2 *= 0.5f;
	vec3d uvec, fvec, rvec, center, reye, rfvec;

	vm_vec_sub( &fvec, p0, p1 );
	vm_vec_normalize_safe( &fvec );
	vm_vec_copy_scale(&rfvec, &fvec, -1.0f);

	vm_vec_avg( &center, p0, p1 ); //needed for the return value only
	vm_vec_sub(&reye, &Eye_position, &center);
	vm_vec_normalize(&reye);

	// code intended to prevent possible null vector normalize issue - start
	if (vm_test_parallel(&reye,&fvec)){
		fvec.xyz.x = -reye.xyz.z;
		fvec.xyz.y = 0.0f;
		fvec.xyz.z = -reye.xyz.x;
	}

	if (vm_test_parallel(&reye,&rfvec)){
		fvec.xyz.x = reye.xyz.z;
		fvec.xyz.y = 0.0f;
		fvec.xyz.z = reye.xyz.x;
	}
	// code intended to prevent possible null vector normalize issue - end

	vm_vec_crossprod(&uvec,&fvec,&reye);
	vm_vec_normalize(&uvec);
	vm_vec_crossprod(&fvec,&uvec,&reye);
	vm_vec_normalize(&fvec);
	 
	//now recompute right vector, in case it wasn't entirely perpendiclar
	vm_vec_crossprod(&rvec,&uvec,&fvec);

	// Now have uvec, which is up vector and rvec which is the normal
	// of the face.

	int i;
	vec3d start, end;
	vm_vec_scale_add(&start, p0, &fvec, -width1);
	vm_vec_scale_add(&end, p1, &fvec, width2);
	vec3d vecs[4];
	vertex pts[4];
	vertex *ptlist[8] = 
	{ &pts[3], &pts[2], &pts[1], &pts[0], 
	  &pts[0], &pts[1], &pts[2], &pts[3]};

	vm_vec_scale_add( &vecs[0], &start, &uvec, width1 );
	vm_vec_scale_add( &vecs[1], &end, &uvec, width2 );
	vm_vec_scale_add( &vecs[2], &end, &uvec, -width2 );
	vm_vec_scale_add( &vecs[3], &start, &uvec, -width1 );

	for (i=0; i<4; i++ )	{
		g3_transfer_vertex( &pts[i], &vecs[i] );
	}
	ptlist[0]->texture_position.u = 0.0f;
	ptlist[0]->texture_position.v = 0.0f;
	ptlist[1]->texture_position.u = 1.0f;
	ptlist[1]->texture_position.v = 0.0f;
	ptlist[2]->texture_position.u = 1.0f;
	ptlist[2]->texture_position.v = 1.0f;
	ptlist[3]->texture_position.u = 0.0f;
	ptlist[3]->texture_position.v = 1.0f;
	ptlist[0]->r = (ubyte)r;
	ptlist[0]->g = (ubyte)g;
	ptlist[0]->b = (ubyte)b;
	ptlist[0]->a = 255;
	ptlist[1]->r = (ubyte)r;
	ptlist[1]->g = (ubyte)g;
	ptlist[1]->b = (ubyte)b;
	ptlist[1]->a = 255;
	ptlist[2]->r = (ubyte)r;
	ptlist[2]->g = (ubyte)g;
	ptlist[2]->b = (ubyte)b;
	ptlist[2]->a = 255;
	ptlist[3]->r = (ubyte)r;
	ptlist[3]->g = (ubyte)g;
	ptlist[3]->b = (ubyte)b;
	ptlist[3]->a = 255;

	gr_tmapper(4, ptlist,tmap_flags | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT);
	
	return center.xyz.z;
}
void warpin_queue_render(draw_list *scene, object *obj, matrix *orient, vec3d *pos, int texture_bitmap_num, float radius, float life_percent, float max_radius, int warp_3d)
{
	vec3d center;
	vec3d vecs[5];
	vertex verts[5];

	vm_vec_scale_add( &center, pos, &orient->vec.fvec, -(max_radius/2.5f)/3.0f );


	if (Warp_glow_bitmap >= 0) {
		float r = radius;
		bool render_it = true;

#define OUT_PERCENT1 0.80f
#define OUT_PERCENT2 0.90f

#define IN_PERCENT1 0.10f
#define IN_PERCENT2 0.20f

		if (Cmdline_warp_flash)
		{
			if ( (life_percent >= IN_PERCENT1) && (life_percent < IN_PERCENT2) ) {
				r *= (life_percent - IN_PERCENT1) / (IN_PERCENT2 - IN_PERCENT1);
				//render_it = true;
			} else if ( (life_percent >= OUT_PERCENT1) && (life_percent < OUT_PERCENT2) ) {
				r *= (OUT_PERCENT2 - life_percent) / (OUT_PERCENT2 - OUT_PERCENT1);
				//render_it = true;
			}
		}

		if (render_it) {
			// Add in noise 
			int noise_frame = fl2i(Missiontime/15.0f) % NOISE_NUM_FRAMES;

			r *= (0.40f + Noise[noise_frame] * 0.30f);

			// Bobboau's warp thingie, toggled by cmdline
			if (Cmdline_warp_flash) {
				r += powf((2.0f * life_percent) - 1.0f, 24.0f) * max_radius * 1.5f;
			}

			vecs[4] = center;
			verts[4].texture_position.u = 0.5f; verts[4].texture_position.v = 0.5f; 

			g3_transfer_vertex( &verts[4], &vecs[4] );

			float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? (1.0f - neb2_get_fog_intensity(obj)) : 1.0f;

			batch_add_bitmap(Warp_glow_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_EMISSIVE , &verts[4], 0, r, alpha);
		}
	}

	if ( (Warp_model >= 0) && (warp_3d || Cmdline_3dwarp) ) {
		model_render_params render_info;

		float scale = radius / 25.0f;

		vec3d warp_scale;

		warp_scale.xyz.x = warp_scale.xyz.y = warp_scale.xyz.z = scale;

		float dist = vm_vec_dist_quick( pos, &Eye_position );

		render_info.set_warp_params(texture_bitmap_num, radius/max_radius, warp_scale);
		render_info.set_detail_level_lock((int)(dist / (radius * 10.0f)));
		render_info.set_flags(MR_NO_LIGHTING | MR_NORMAL | MR_NO_FOGGING | MR_NO_CULL | MR_NO_BATCH);

		model_render_queue( &render_info, scene, Warp_model, orient, pos);
	} else {
		float Grid_depth = radius/2.5f;

		// gr_set_bitmap( texture_bitmap_num, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f );	

		vm_vec_scale_add( &vecs[0], &center, &orient->vec.uvec, radius );
		vm_vec_scale_add2( &vecs[0], &orient->vec.rvec, -radius );
		vm_vec_scale_add2( &vecs[0], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[1], &center, &orient->vec.uvec, radius );
		vm_vec_scale_add2( &vecs[1], &orient->vec.rvec, radius );
		vm_vec_scale_add2( &vecs[1], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[2], &center, &orient->vec.uvec, -radius );
		vm_vec_scale_add2( &vecs[2], &orient->vec.rvec, radius );
		vm_vec_scale_add2( &vecs[2], &orient->vec.fvec, Grid_depth );

		vm_vec_scale_add( &vecs[3], &center, &orient->vec.uvec, -radius );
		vm_vec_scale_add2( &vecs[3], &orient->vec.rvec, -radius );
		vm_vec_scale_add2( &vecs[3], &orient->vec.fvec, Grid_depth );

		//	vm_vec_scale_add( &vecs[4], ¢er, &orient->vec.fvec, -Grid_depth );
		vecs[4] = center;

		verts[0].texture_position.u = 0.01f;
		verts[0].texture_position.v = 0.01f;

		verts[1].texture_position.u = 0.99f;
		verts[1].texture_position.v = 0.01f;

		verts[2].texture_position.u = 0.99f;
		verts[2].texture_position.v = 0.99f;

		verts[3].texture_position.u = 0.01f;
		verts[3].texture_position.v = 0.99f;

		verts[4].texture_position.u = 0.5f;
		verts[4].texture_position.v = 0.5f; 

		g3_transfer_vertex( &verts[0], &vecs[0] );
		g3_transfer_vertex( &verts[1], &vecs[1] );
		g3_transfer_vertex( &verts[2], &vecs[2] );
		g3_transfer_vertex( &verts[3], &vecs[3] );
		g3_transfer_vertex( &verts[4], &vecs[4] );

		warpin_batch_draw_face( texture_bitmap_num, &verts[0], &verts[4], &verts[1] );
		warpin_batch_draw_face( texture_bitmap_num, &verts[1], &verts[4], &verts[2] );
		warpin_batch_draw_face( texture_bitmap_num, &verts[4], &verts[3], &verts[2] );
		warpin_batch_draw_face( texture_bitmap_num, &verts[0], &verts[3], &verts[4] );
	}

	if (Warp_ball_bitmap > -1 && Cmdline_warp_flash == 1) {
		//flash_ball warp_ball(20, .1f,.25f, &vmd_z_vector, &vmd_zero_vector, 4.0f, 0.5f);
		flash_ball warp_ball(20, .1f,.25f, &orient->vec.fvec, pos, 4.0f, 0.5f);

		float adg = (2.0f * life_percent) - 1.0f;
		float pct = (powf(adg, 4.0f) - powf(adg, 128.0f)) * 4.0f;

		if (pct > 0.00001f) {
			warp_ball.render(Warp_ball_bitmap, max_radius * pct * 0.5f, adg * adg, adg * adg * 6.0f);
		}
	}
}
void trail_add_batch(trail * trailp)
{
	int sections[NUM_TRAIL_SECTIONS];
	int num_sections = 0;
	int i;
	vec3d topv, botv, *fvec, last_pos, tmp_fvec;
	vertex  top, bot, top_prev, bot_prev;
	float w;
	ubyte l;
	vec3d centerv;

	if (trailp->tail == trailp->head)
		return;

	// if this trail is on the player ship, and he's in any padlock view except rear view, don't draw	
	if ((Player_ship != NULL) && trail_is_on_ship(trailp, Player_ship) &&
		(Viewer_mode & (VM_PADLOCK_UP | VM_PADLOCK_LEFT | VM_PADLOCK_RIGHT)))
	{
		return;
	}

	trail_info *ti = &trailp->info;

	int n = trailp->tail;

	do	{
		n--;

		if (n < 0)
			n = NUM_TRAIL_SECTIONS - 1;

		if (trailp->val[n] > 1.0f)
			break;

		sections[num_sections++] = n;
	} while (n != trailp->head);

	if (num_sections <= 0)
		return;

	Assertion(ti->texture.bitmap_id != -1, "Weapon trail %s could not be loaded", ti->texture.filename); // We can leave this as an assert, but tell them how to fix it. --Chief

	memset(&top, 0, sizeof(vertex));
	memset(&bot, 0, sizeof(vertex));
	memset(&top_prev, 0, sizeof(vertex));
	memset(&bot_prev, 0, sizeof(vertex));

	float w_size = (ti->w_end - ti->w_start);
	float a_size = (ti->a_end - ti->a_start);
	int num_faded_sections = ti->n_fade_out_sections;

	for (i = 0; i < num_sections; i++) {
		n = sections[i];
		float init_fade_out = 1.0f;

		if ((num_faded_sections > 0) && (i < num_faded_sections)) {
			init_fade_out = ((float)i) / (float)num_faded_sections;
		}

		w = trailp->val[n] * w_size + ti->w_start;
		if (init_fade_out != 1.0f) {
			l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f * init_fade_out * init_fade_out);
		}
		else {
			l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f);
		}

		if (i == 0)	{
			if (num_sections > 1)	{
				vm_vec_sub(&tmp_fvec, &trailp->pos[n], &trailp->pos[sections[i + 1]]);
				vm_vec_normalize_safe(&tmp_fvec);
				fvec = &tmp_fvec;
			}
			else {
				fvec = &tmp_fvec;
				fvec->xyz.x = 0.0f;
				fvec->xyz.y = 0.0f;
				fvec->xyz.z = 1.0f;
			}
		}
		else {
			vm_vec_sub(&tmp_fvec, &last_pos, &trailp->pos[n]);
			vm_vec_normalize_safe(&tmp_fvec);
			fvec = &tmp_fvec;
		}

		trail_calc_facing_pts(&topv, &botv, fvec, &trailp->pos[n], w);

		if (!Cmdline_nohtl) {
			g3_transfer_vertex(&top, &topv);
			g3_transfer_vertex(&bot, &botv);
		}
		else {
			g3_rotate_vertex(&top, &topv);
			g3_rotate_vertex(&bot, &botv);
		}

		top.r = top.g = top.b = l;
		bot.r = bot.g = bot.b = l;
		top.a = bot.a = l;

		float U = i2fl(i);
		
		top.texture_position.u = U;
		top.texture_position.v = 1.0f;

		bot.texture_position.u = U;
		bot.texture_position.v = 0.0f;

		if (i > 0) {
			if (i == num_sections - 1) {
				// Last one...
				vm_vec_avg(&centerv, &topv, &botv);

				vertex center_vert = vertex();

				if (!Cmdline_nohtl)
					g3_transfer_vertex(&center_vert, &centerv);
				else
					g3_rotate_vertex(&center_vert, &centerv);

				center_vert.texture_position.u = U + 1.0f;
				center_vert.texture_position.v = 0.5f;
				center_vert.a = center_vert.r = center_vert.g = center_vert.b = l;

				vertex tri[3];

				tri[1] = top_prev;
				tri[2] = bot_prev;
				tri[0] = center_vert;

				batch_add_tri(
					ti->texture.bitmap_id,
					TMAP_FLAG_TEXTURED | TMAP_FLAG_ALPHA | TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_HTL_3D_UNLIT,
					tri,
					1.0f
				);
			} else {
				vertex quad[4];

				quad[0] = top_prev;
				quad[1] = bot_prev;
				quad[2] = bot;
				quad[3] = top;

				batch_add_quad(
					ti->texture.bitmap_id, 
					TMAP_FLAG_TEXTURED | TMAP_FLAG_ALPHA | TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_HTL_3D_UNLIT,
					quad,
					1.0f
				);
			}
		}

		last_pos = trailp->pos[n];
		top_prev = top;
		bot_prev = bot;
	}
}
void trail_render( trail * trailp )
{
	int sections[NUM_TRAIL_SECTIONS];
	int num_sections = 0;
	int i;
	vec3d topv, botv, *fvec, last_pos, tmp_fvec;
	vertex  top, bot;
	int nv = 0;
	float w;
	ubyte l;
	vec3d centerv;

	if (trailp->tail == trailp->head)
		return;

	// if this trail is on the player ship, and he's in any padlock view except rear view, don't draw	
	if ( (Player_ship != NULL) && trail_is_on_ship(trailp, Player_ship) &&
		(Viewer_mode & (VM_PADLOCK_UP | VM_PADLOCK_LEFT | VM_PADLOCK_RIGHT)) )
	{
		return;
	}

	trail_info *ti	= &trailp->info;

	int n = trailp->tail;

	do	{
		n--;

		if (n < 0)
			n = NUM_TRAIL_SECTIONS-1;

		if (trailp->val[n] > 1.0f)
			break;

		sections[num_sections++] = n;
	} while ( n != trailp->head );

	if (num_sections <= 0)
		return;

	Assertion(ti->texture.bitmap_id != -1, "Weapon trail %s could not be loaded", ti->texture.filename); // We can leave this as an assert, but tell them how to fix it. --Chief

	memset( &top, 0, sizeof(vertex) );
	memset( &bot, 0, sizeof(vertex) );

	// it's a tristrip, so allocate for 2+1
	allocate_trail_verts((num_sections * 2) + 1);

	float w_size = (ti->w_end - ti->w_start);
	float a_size = (ti->a_end - ti->a_start);
	int num_faded_sections = ti->n_fade_out_sections;

	for (i = 0; i < num_sections; i++) {
		n = sections[i];
		float init_fade_out = 1.0f;

		if ((num_faded_sections > 0) && (i < num_faded_sections)) {
			init_fade_out = ((float) i) / (float) num_faded_sections;
		}

		w = trailp->val[n] * w_size + ti->w_start;
		if (init_fade_out != 1.0f) {
			l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f * init_fade_out * init_fade_out);
		} else {
			l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f);
		}

		if ( i == 0 )	{
			if ( num_sections > 1 )	{
				vm_vec_sub(&tmp_fvec, &trailp->pos[n], &trailp->pos[sections[i+1]] );
				vm_vec_normalize_safe(&tmp_fvec);
				fvec = &tmp_fvec;
			} else {
				fvec = &tmp_fvec;
				fvec->xyz.x = 0.0f;
				fvec->xyz.y = 0.0f;
				fvec->xyz.z = 1.0f;
			}
		} else {
			vm_vec_sub(&tmp_fvec, &last_pos, &trailp->pos[n] );
			vm_vec_normalize_safe(&tmp_fvec);
			fvec = &tmp_fvec;
		}

		trail_calc_facing_pts( &topv, &botv, fvec, &trailp->pos[n], w );

		if ( !Cmdline_nohtl ) {
			g3_transfer_vertex( &top, &topv );
			g3_transfer_vertex( &bot, &botv );
		} else {
			g3_rotate_vertex( &top, &topv );
			g3_rotate_vertex( &bot, &botv );
		}

		top.a = bot.a = l;	

		if (i > 0) {
			float U = i2fl(i);

			if (i == num_sections-1) {
				// Last one...
				vm_vec_avg( &centerv, &topv, &botv );

				if ( !Cmdline_nohtl )
					g3_transfer_vertex( &Trail_v_list[nv+2], &centerv );
				else
					g3_rotate_vertex( &Trail_v_list[nv+2], &centerv );

				Trail_v_list[nv].a = l;	

				Trail_v_list[nv].texture_position.u = U;
				Trail_v_list[nv].texture_position.v = 1.0f; 
				Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
				nv++;

				Trail_v_list[nv].texture_position.u = U;
				Trail_v_list[nv].texture_position.v = 0.0f; 
				Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
				nv++;

				Trail_v_list[nv].texture_position.u = U + 1.0f;
				Trail_v_list[nv].texture_position.v = 0.5f;
				Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = 0;
				nv++;
			} else {
				Trail_v_list[nv].texture_position.u = U;
				Trail_v_list[nv].texture_position.v = 1.0f; 
				Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
				nv++;

				Trail_v_list[nv].texture_position.u = U;
				Trail_v_list[nv].texture_position.v = 0.0f; 
				Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
				nv++;
			}
		}

		last_pos = trailp->pos[n];
		Trail_v_list[nv] = top;
		Trail_v_list[nv+1] = bot;
	}

	if ( !nv )
		return;

	if (nv < 3)
		Error( LOCATION, "too few verts in trail render\n" );

	// there should always be three verts in the last section and 2 everyware else, therefore there should always be an odd number of verts
	if ( (nv % 2) != 1 )
		Warning( LOCATION, "even number of verts in trail render\n" );

	profile_begin("Trail Draw");
	gr_set_bitmap( ti->texture.bitmap_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f );
	gr_render(nv, Trail_v_list, TMAP_FLAG_TEXTURED | TMAP_FLAG_ALPHA | TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_HTL_3D_UNLIT | TMAP_FLAG_TRISTRIP);
	profile_end("Trail Draw");
}