/** * 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); } }
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 }
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); }
//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); }
/** * 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; // } }
/** * 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; }
/** * 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; } }