void draw_player( object * obj ) { vms_vector arrow_pos, head_pos; g3s_point sphere_point, arrow_point, head_point; // Draw Console player -- shaped like a ellipse with an arrow. g3_rotate_point(&sphere_point,&obj->pos); g3_draw_sphere(&sphere_point,obj->size); // Draw shaft of arrow vm_vec_scale_add( &arrow_pos, &obj->pos, &obj->orient.fvec, obj->size*3 ); g3_rotate_point(&arrow_point,&arrow_pos); automap_draw_line(&sphere_point, &arrow_point); // Draw right head of arrow vm_vec_scale_add( &head_pos, &obj->pos, &obj->orient.fvec, obj->size*2 ); vm_vec_scale_add2( &head_pos, &obj->orient.rvec, obj->size*1 ); g3_rotate_point(&head_point,&head_pos); automap_draw_line(&arrow_point, &head_point); // Draw left head of arrow vm_vec_scale_add( &head_pos, &obj->pos, &obj->orient.fvec, obj->size*2 ); vm_vec_scale_add2( &head_pos, &obj->orient.rvec, obj->size*(-1) ); g3_rotate_point(&head_point,&head_pos); automap_draw_line(&arrow_point, &head_point); // Draw player's up vector vm_vec_scale_add( &arrow_pos, &obj->pos, &obj->orient.uvec, obj->size*2 ); g3_rotate_point(&arrow_point,&arrow_pos); automap_draw_line(&sphere_point, &arrow_point); }
void supernova_get_eye(vec3d *eye_pos, matrix *eye_orient) { // supernova camera pos vec3d Supernova_camera_pos; static matrix Supernova_camera_orient; vec3d at; vec3d sun_temp, sun_vec; vec3d view; // set the controls for the heart of the sun stars_get_sun_pos(0, &sun_temp); vm_vec_add2(&sun_temp, &Player_obj->pos); vm_vec_sub(&sun_vec, &sun_temp, &Player_obj->pos); vm_vec_normalize(&sun_vec); // always set the camera pos vec3d move; matrix whee; vm_vector_2_matrix(&whee, &move, NULL, NULL); vm_vec_scale_add(&Supernova_camera_pos, &Player_obj->pos, &whee.vec.rvec, sn_cam_distance); vm_vec_scale_add2(&Supernova_camera_pos, &whee.vec.uvec, 30.0f); //cam->set_position(&Supernova_camera_pos); *eye_pos = Supernova_camera_pos; // if we're no longer moving the camera if(Supernova_time < (SUPERNOVA_CUT_TIME - SUPERNOVA_CAMERA_MOVE_TIME)) { // *eye_pos = Supernova_camera_pos; //cam->set_rotation(&Supernova_camera_orient); *eye_orient = Supernova_camera_orient; } // otherwise move it else { // get a vector somewhere between the supernova shockwave and the player ship at = Player_obj->pos; vm_vec_scale_add2(&at, &sun_vec, sn_distance); vm_vec_sub(&move, &Player_obj->pos, &at); vm_vec_normalize(&move); // linearly move towards the player pos float pct = ((SUPERNOVA_CUT_TIME - Supernova_time) / SUPERNOVA_CAMERA_MOVE_TIME); vm_vec_scale_add2(&at, &move, sn_distance * pct); vm_vec_sub(&view, &at, &Supernova_camera_pos); vm_vec_normalize(&view); vm_vector_2_matrix(&Supernova_camera_orient, &view, NULL, NULL); //cam->set_rotation(&Supernova_camera_orient); *eye_orient = Supernova_camera_orient; } //return supernova_camera; }
bool BeamPiercingEffect::processSource(const ParticleSource* source) { particle_info info; memset(&info, 0, sizeof(info)); source->getOrigin()->applyToParticleInfo(info); if (m_effectBitmap >= 0) { info.type = PARTICLE_BITMAP_PERSISTENT; info.optional_data = m_effectBitmap; } else { info.type = PARTICLE_SMOKE; } info.rad = m_radius * frand_range(0.5f, 2.0f); vec3d fvec = source->getOrientation()->getDirectionVector(); float base_v, back_v; vec3d rnd_vec; vm_vec_rand_vec_quick(&rnd_vec); if (m_velocity != 0.0f) { base_v = m_velocity; } else { base_v = m_radius; } if (m_backVelocity != 0.0f) { back_v = m_backVelocity; } else { back_v = base_v * (-0.2f); } vm_vec_copy_scale(&info.vel, &fvec, base_v * frand_range(1.0f, 2.0f)); vm_vec_scale_add2(&info.vel, &rnd_vec, base_v * m_variance); // Create the primary piercing particle create(&info); vm_vec_copy_scale(&info.vel, &fvec, back_v * frand_range(1.0f, 2.0f)); vm_vec_scale_add2(&info.vel, &rnd_vec, back_v * m_variance); // Create the splash particle create(&info); return false; }
void warp_camera::set_velocity( vec3d *in_vel, bool instantaneous ) { c_desired_vel.xyz.x = 0.0f; c_desired_vel.xyz.y = 0.0f; c_desired_vel.xyz.z = 0.0f; vm_vec_scale_add2( &c_desired_vel, &c_ori.vec.rvec, in_vel->xyz.x ); vm_vec_scale_add2( &c_desired_vel, &c_ori.vec.uvec, in_vel->xyz.y ); vm_vec_scale_add2( &c_desired_vel, &c_ori.vec.fvec, in_vel->xyz.z ); if ( instantaneous ) { c_vel = c_desired_vel; } }
//Used to set a countermeasure velocity after being launched from a ship as a countermeasure //ie not as a primary or secondary. void cmeasure_set_ship_launch_vel(object *objp, object *parent_objp, int arand) { vec3d vel, rand_vec; //Get cmeasure rear velocity in world vm_vec_scale_add(&vel, &parent_objp->phys_info.vel, &parent_objp->orient.vec.fvec, -25.0f); //Get random velocity vector static_randvec(arand+1, &rand_vec); //Add it to the rear velocity vm_vec_scale_add2(&vel, &rand_vec, 2.0f); objp->phys_info.vel = vel; //Zero out this stuff so it isn't moving vm_vec_zero(&objp->phys_info.rotvel); vm_vec_zero(&objp->phys_info.max_vel); vm_vec_zero(&objp->phys_info.max_rotvel); // blow out his reverse thrusters. Or drag, same thing. objp->phys_info.rotdamp = 10000.0f; objp->phys_info.side_slip_time_const = 10000.0f; objp->phys_info.max_vel.xyz.z = -25.0f; vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.vec.fvec, objp->phys_info.max_vel.xyz.z ); }
// Return true if object A is expected to collide with object B within time duration // For purposes of this check, the first object moves from current location to predicted // location. The second object is assumed to be where it will be at time duration, NOT // where it currently is. // radius_scale is used to control the precision of the check. // If 0.0, then use polygon models to perform check, slow and accurate // If !0.0, then use as a scale on the radius of the objects. 1.0 is Descent style // collisions. Larger values can be used to be sloppy about the collisions which // is useful if a moving object wants to prevent a collision. int objects_will_collide(object *A, object *B, float duration, float radius_scale) { object A_future; vec3d hitpos; A_future = *A; vm_vec_scale_add2(&A_future.pos, &A->phys_info.vel, duration); if (radius_scale == 0.0f) { return ship_check_collision_fast(B, &A_future, &hitpos ); } else { float size_A, size_B, dist, r; vec3d nearest_point; size_A = A->radius * radius_scale; size_B = B->radius * radius_scale; // If A is moving, check along vector. if (A->phys_info.speed != 0.0f) { r = find_nearest_point_on_line(&nearest_point, &A->pos, &A_future.pos, &B->pos); if (r < 0) { nearest_point = A->pos; } else if (r > 1) { nearest_point = A_future.pos; } dist = vm_vec_dist_quick(&B->pos, &nearest_point); return (dist < size_A + size_B); } else { return vm_vec_dist_quick(&B->pos, &A->pos) < size_A + size_B; } } }
void create_low_detail_poly(int global_index, vec3d *tcp, vec3d *rightv, vec3d *upv) { float scale; gshield_tri *trip; trip = &Global_tris[global_index]; scale = vm_vec_mag(tcp) * 2.0f; vm_vec_scale_add(&trip->verts[0].pos, tcp, rightv, -scale/2.0f); vm_vec_scale_add2(&trip->verts[0].pos, upv, scale/2.0f); vm_vec_scale_add(&trip->verts[1].pos, &trip->verts[0].pos, rightv, scale); vm_vec_scale_add(&trip->verts[2].pos, &trip->verts[1].pos, upv, -scale); vm_vec_scale_add(&trip->verts[3].pos, &trip->verts[2].pos, rightv, -scale); // Set u, v coordinates. // Note, this need only be done once, as it's common for all explosions. trip->verts[0].u = 0.0f; trip->verts[0].v = 0.0f; trip->verts[1].u = 1.0f; trip->verts[1].v = 0.0f; trip->verts[2].u = 1.0f; trip->verts[2].v = 1.0f; trip->verts[3].u = 0.0f; trip->verts[3].v = 1.0f; }
warp_camera::warp_camera(object *objp) { this->reset(); vec3d object_pos = objp->pos; matrix tmp; ship_get_eye(&object_pos, &tmp, objp); vm_vec_scale_add2( &object_pos, &Player_obj->orient.vec.rvec, 0.0f ); vm_vec_scale_add2( &object_pos, &Player_obj->orient.vec.uvec, 0.952f ); vm_vec_scale_add2( &object_pos, &Player_obj->orient.vec.fvec, -1.782f ); vec3d tmp_vel = { { { 0.0f, 5.1919f, 14.7f } } }; this->set_position( &object_pos ); this->set_rotation( &objp->orient ); this->set_velocity( &tmp_vel, true); }
void initial_status::undock(object *objp1, object *objp2) { vec3d v; int ship_num, other_ship_num; if (objp1 == NULL || objp2 == NULL) return; vm_vec_sub(&v, &objp2->pos, &objp1->pos); vm_vec_normalize(&v); ship_num = get_ship_from_obj(OBJ_INDEX(objp1)); other_ship_num = get_ship_from_obj(OBJ_INDEX(objp2)); if (ship_class_compare(Ships[ship_num].ship_info_index, Ships[other_ship_num].ship_info_index) <= 0) vm_vec_scale_add2(&objp2->pos, &v, objp2->radius * 2.0f); else vm_vec_scale_add2(&objp1->pos, &v, objp1->radius * -2.0f); ai_do_objects_undocked_stuff(objp1, objp2); // check to see if one of these ships has an arrival cue of false. If so, then // reset it back to default value of true. be sure to correctly update before // and after setting data. // Goober5000 - but don't reset it if it's part of a wing! Ship_editor_dialog.update_data(1); if ( Ships[ship_num].arrival_cue == Locked_sexp_false && Ships[ship_num].wingnum < 0 ) { Ships[ship_num].arrival_cue = Locked_sexp_true; } else if ( Ships[other_ship_num].arrival_cue == Locked_sexp_false && Ships[other_ship_num].wingnum < 0 ) { Ships[other_ship_num].arrival_cue = Locked_sexp_true; } // if this ship is no longer docked, ensure its dock leader flag is clear if (!object_is_docked(&Objects[Ships[ship_num].objnum])) Ships[ship_num].flags.remove(Ship::Ship_Flags::Dock_leader); // same for the other ship if (!object_is_docked(&Objects[Ships[other_ship_num].objnum])) Ships[other_ship_num].flags.remove(Ship::Ship_Flags::Dock_leader); Ship_editor_dialog.initialize_data(1); }
//Applies an instantaneous force on an object, resulting in an instantaneous //change in velocity. void phys_apply_force(object *obj,vms_vector *force_vec) { if (obj->movement_type != MT_PHYSICS) return; //Add in acceleration due to force vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass)); }
render_external_scene(fix eye_offset) { Viewer_eye = Viewer->pos; if (eye_offset) vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset); g3_set_view_matrix(&Viewer->pos,&Viewer->orient,Render_zoom); //g3_draw_horizon(BM_XRGB(0,0,0),BM_XRGB(16,16,16)); //,-1); gr_clear_canvas(BM_XRGB(0,0,0)); g3_start_instance_matrix(&vmd_zero_vector,&surface_orient); draw_stars(); g3_done_instance(); { //draw satellite vms_vector delta; g3s_point p,top_pnt; g3_rotate_point(&p,&satellite_pos); g3_rotate_delta_vec(&delta,&satellite_upvec); g3_add_delta_vec(&top_pnt,&p,&delta); if (! (p.p3_codes & CC_BEHIND)) { int save_im = Interpolation_method; //p.p3_flags &= ~PF_PROJECTED; //g3_project_point(&p); if (! (p.p3_flags & PF_OVERFLOW)) { Interpolation_method = 0; //gr_bitmapm(f2i(p.p3_sx)-32,f2i(p.p3_sy)-32,satellite_bitmap); g3_draw_rod_tmap(satellite_bitmap,&p,SATELLITE_WIDTH,&top_pnt,SATELLITE_WIDTH,f1_0); Interpolation_method = save_im; } } } #ifdef STATION_ENABLED draw_polygon_model(&station_pos,&vmd_identity_matrix,NULL,station_modelnum,0,f1_0,NULL,NULL); #endif render_terrain(&mine_ground_exit_point,exit_point_bmx,exit_point_bmy); draw_exit_model(); if (ext_expl_playing) draw_fireball(&external_explosion); Lighting_on=0; render_object(ConsoleObject); Lighting_on=1; }
draw_exit_model() { vms_vector model_pos; int f=15,u=0; //21; vm_vec_scale_add(&model_pos,&mine_exit_point,&mine_exit_orient.fvec,i2f(f)); vm_vec_scale_add2(&model_pos,&mine_exit_orient.uvec,i2f(u)); draw_polygon_model(&model_pos,&mine_exit_orient,NULL,(mine_destroyed)?destroyed_exit_modelnum:exit_modelnum,0,f1_0,NULL,NULL); }
endlevel_render_mine(fix eye_offset) { int start_seg_num; Viewer_eye = Viewer->pos; if (Viewer->type == OBJ_PLAYER ) vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4); if (eye_offset) vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset); #ifdef EDITOR if (Function_mode==FMODE_EDITOR) Viewer_eye = Viewer->pos; #endif if (Endlevel_sequence >= EL_OUTSIDE) { start_seg_num = exit_segnum; } else { start_seg_num = find_point_seg(&Viewer_eye,Viewer->segnum); if (start_seg_num==-1) start_seg_num = Viewer->segnum; } if (Endlevel_sequence == EL_LOOKBACK) { vms_matrix headm,viewm; vms_angvec angles = {0,0,0x7fff}; vm_angles_2_matrix(&headm,&angles); vm_matrix_x_matrix(&viewm,&Viewer->orient,&headm); g3_set_view_matrix(&Viewer_eye,&viewm,Render_zoom); } else g3_set_view_matrix(&Viewer_eye,&Viewer->orient,Render_zoom); render_mine(start_seg_num,eye_offset); }
// On joining edges fvi tends to get inaccurate as hell. Approach is to check if the object interects with the wall and if so, move away from it. void fix_illegal_wall_intersection(object *obj, vms_vector *origin) { int hseg = -1, hside = -1, hface = -1; if (!(obj->type == OBJ_PLAYER || obj->type == OBJ_ROBOT || obj->type == OBJ_CAMERA)) // jinx 02-01-13 spec return; if ( object_intersects_wall_d(obj,&hseg,&hside,&hface) ) { vm_vec_scale_add2(&obj->pos,&Segments[hseg].sides[hside].normals[0],FrameTime*10); update_object_seg(obj); } }
// On joining edges fvi tends to get inaccurate as hell. Approach is to check if the object interects with the wall and if so, move away from it. static void fix_illegal_wall_intersection(dxxobject *obj, vms_vector *) { int hside = -1, hface = -1; if (!(obj->type == OBJ_PLAYER || obj->type == OBJ_ROBOT)) return; segnum_t hseg = segment_none; if ( object_intersects_wall_d(obj,&hseg,&hside,&hface) ) { vm_vec_scale_add2(&obj->pos,&Segments[hseg].sides[hside].normals[0],FrameTime*10); update_object_seg(obj); } }
// ----------------------------------------------------------------------------------------------------------- // Simulate a physics object for this frame void physics_sim(vec3d* position, matrix* orient, physics_info* pi, float sim_time) { // check flag which tells us whether or not to do velocity translation if (pi->flags & PF_CONST_VEL) { vm_vec_scale_add2(position, &pi->vel, sim_time); } else { physics_sim_vel(position, pi, sim_time, orient); physics_sim_rot(orient, pi, sim_time); pi->speed = vm_vec_mag(&pi->vel); // Note, cannot use quick version, causes cumulative error, increasing speed. pi->fspeed = vm_vec_dot(&orient->vec.fvec, &pi->vel); // instead of vector magnitude -- use only forward vector since we are only interested in forward velocity } }
//Applies an instantaneous force on an object, resulting in an instantaneous //change in velocity. void phys_apply_force(dxxobject *obj,vms_vector *force_vec) { // Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass, // in collision with crazy reactor robot thing on d2levf-s. if (obj->mtype.phys_info.mass == 0) return; if (obj->movement_type != MT_PHYSICS) return; //Add in acceleration due to force vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass)); }
void physics_apply_whack(vec3d *impulse, vec3d *pos, physics_info *pi, matrix *orient, float mass) { vec3d local_torque, torque; // vec3d npos; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; // first do the rotational velocity // calculate the torque on the body based on the point on the // object that was hit and the momentum being applied to the object vm_vec_crossprod(&torque, pos, impulse); vm_vec_rotate ( &local_torque, &torque, orient ); vec3d delta_rotvel; vm_vec_rotate( &delta_rotvel, &local_torque, &pi->I_body_inv ); vm_vec_scale ( &delta_rotvel, (float) ROTVEL_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &delta_rotvel ); //mprintf(("Whack: %7.3f %7.3f %7.3f\n", pi->rotvel.xyz.x, pi->rotvel.xyz.y, pi->rotvel.xyz.z)); // instant whack on the velocity // reduce damping on all axes pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } // Goober5000 - pi->mass should probably be just mass, as specified in the header vm_vec_scale_add2( &pi->vel, impulse, 1.0f / mass ); if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_apply_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision }
//------------------------------------------------------------ // Trigger (enable) the materialization center in segment segnum void trigger_matcen(int segnum) { // -- segment *segp = &Segments[segnum]; segment2 *seg2p = &Segment2s[segnum]; vms_vector pos, delta; FuelCenter *robotcen; int objnum; mprintf((0, "Trigger matcen, segment %i\n", segnum)); Assert(seg2p->special == SEGMENT_IS_ROBOTMAKER); Assert(seg2p->matcen_num < Num_fuelcenters); Assert((seg2p->matcen_num >= 0) && (seg2p->matcen_num <= Highest_segment_index)); robotcen = &Station[RobotCenters[seg2p->matcen_num].fuelcen_num]; if (robotcen->Enabled == 1) return; if (!robotcen->Lives) return; // MK: 11/18/95, At insane, matcens work forever! if (Difficulty_level+1 < NDL) robotcen->Lives--; robotcen->Timer = F1_0*1000; // Make sure the first robot gets emitted right away. robotcen->Enabled = 1; // Say this center is enabled, it can create robots. robotcen->Capacity = i2f(Difficulty_level + 3); robotcen->Disable_time = MATCEN_LIFE; // Create a bright object in the segment. pos = robotcen->Center; vm_vec_sub(&delta, &Vertices[Segments[segnum].verts[0]], &robotcen->Center); vm_vec_scale_add2(&pos, &delta, F1_0/2); objnum = obj_create( OBJ_LIGHT, 0, segnum, &pos, NULL, 0, CT_LIGHT, MT_NONE, RT_NONE ); if (objnum != -1) { Objects[objnum].lifeleft = MATCEN_LIFE; Objects[objnum].ctype.light_info.intensity = i2f(8); // Light cast by a fuelcen. } else { mprintf((1, "Can't create invisible flare for matcen.\n")); Int3(); } // mprintf((0, "Created invisibile flare, object=%i, segment=%i, pos=%7.3f %7.3f%7.3f\n", objnum, segnum, f2fl(pos.x), f2fl(pos.y), f2fl(pos.z))); }
void update_points(polymodel *pm,int submodel_num,morph_data *md) { ushort nverts; vms_vector *vp; ushort *data,type; int i; //printf("updating %d ",submodel_num); data = (ushort *) &pm->model_data[pm->submodel_ptrs[submodel_num]]; type = *data++; Assert(type == 7 || type == 1); nverts = *data++; if (type==7) { i = *data++; //get start point number data++; //skip pad } else i = 0; //start at zero vp = (vms_vector *) data; while (nverts--) { if (md->morph_times[i]) //not done yet { if ((md->morph_times[i] -= FrameTime) <= 0) { md->morph_vecs[i] = *vp; md->morph_times[i] = 0; md->n_morphing_points[submodel_num]--; } else vm_vec_scale_add2(&md->morph_vecs[i],&md->morph_deltas[i],FrameTime); } vp++; i++; } //printf("npoints = %d\n",n_morphing_points[submodel_num]); }
//Applies an instantaneous force on an object, resulting in an instantaneous //change in velocity. void phys_apply_force(object *obj,vms_vector *force_vec) { // Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass, // in collision with crazy reactor robot thing on d2levf-s. if (obj->mtype.phys_info.mass == 0) return; if (obj->movement_type != MT_PHYSICS) return; #ifdef TACTILE if (TactileStick && obj==&Objects[Players[Player_num].objnum]) Tactile_apply_force (force_vec,&obj->orient); #endif //Add in acceleration due to force vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass)); }
//------------------------------------------------------------ // Trigger (enable) the materialization center in segment segnum void trigger_matcen(const vsegptridx_t segnum) { const auto &segp = segnum; FuelCenter *robotcen; Assert(segp->special == SEGMENT_IS_ROBOTMAKER); Assert(segp->matcen_num < Num_fuelcenters); Assert((segp->matcen_num >= 0) && (segp->matcen_num <= Highest_segment_index)); robotcen = &Station[RobotCenters[segp->matcen_num].fuelcen_num]; if (robotcen->Enabled == 1) return; if (!robotcen->Lives) return; #if defined(DXX_BUILD_DESCENT_II) // MK: 11/18/95, At insane, matcens work forever! if (Difficulty_level+1 < NDL) #endif robotcen->Lives--; robotcen->Timer = F1_0*1000; // Make sure the first robot gets emitted right away. robotcen->Enabled = 1; // Say this center is enabled, it can create robots. robotcen->Capacity = i2f(Difficulty_level + 3); robotcen->Disable_time = MATCEN_LIFE; // Create a bright object in the segment. auto pos = compute_segment_center(segp); const auto delta = vm_vec_sub(Vertices[segnum->verts[0]], pos); vm_vec_scale_add2(pos, delta, F1_0/2); auto objnum = obj_create( OBJ_LIGHT, 0, segnum, pos, NULL, 0, CT_LIGHT, MT_NONE, RT_NONE ); if (objnum != object_none) { objnum->lifeleft = MATCEN_LIFE; objnum->ctype.light_info.intensity = i2f(8); // Light cast by a fuelcen. } else { Int3(); } }
// Return true if object A is expected to collide with object B within time duration // For purposes of this check, the first object moves from current location to predicted // location. The second object is assumed to be where it will be at time duration, NOT // where it currently is. // radius_scale is used to control the precision of the check. // If 0.0, then use polygon models to perform check, slow and accurate // If !0.0, then use as a scale on the radius of the objects. 1.0 is Descent style // collisions. Larger values can be used to be sloppy about the collisions which // is useful if a moving object wants to prevent a collision. int objects_will_collide(object *A, object *B, float duration, float radius_scale) { vec3d prev_pos; vec3d hitpos; int ret; prev_pos = A->pos; vm_vec_scale_add2(&A->pos, &A->phys_info.vel, duration); if (radius_scale == 0.0f) { ret = ship_check_collision_fast(B, A, &hitpos); } else { float size_A, size_B, dist, r; vec3d nearest_point; size_A = A->radius * radius_scale; size_B = B->radius * radius_scale; // If A is moving, check along vector. if (A->phys_info.speed != 0.0f) { r = find_nearest_point_on_line(&nearest_point, &prev_pos, &A->pos, &B->pos); if (r < 0) { nearest_point = prev_pos; } else if (r > 1) { nearest_point = A->pos; } dist = vm_vec_dist_quick(&B->pos, &nearest_point); ret = (dist < size_A + size_B); } else { ret = vm_vec_dist_quick(&B->pos, &prev_pos) < size_A + size_B; } } // Reset the position to the previous value A->pos = prev_pos; return ret; }
void physics_collide_whack( vec3d *impulse, vec3d *world_delta_rotvel, physics_info *pi, matrix *orient, bool is_landing ) { vec3d body_delta_rotvel; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; vm_vec_rotate( &body_delta_rotvel, world_delta_rotvel, orient ); // vm_vec_scale( &body_delta_rotvel, (float) ROTVEL_COLLIDE_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &body_delta_rotvel ); update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end if (!is_landing) { int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } } pi->flags |= PF_REDUCED_DAMP; vm_vec_scale_add2( &pi->vel, impulse, 1.0f / pi->mass ); // check that collision does not give ship too much speed // reset if too high if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_collide_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision // rotate previous ramp velocity (in model coord) to be same as vel (in world coords) }
void grid_render_elevation_line(vector *pos, grid* gridp) { vector gpos; // Location of point on grid. vector tpos; float dxz; plane tplane; vector *gv; tplane.A = gridp->gmatrix.v.uvec.xyz.x; tplane.B = gridp->gmatrix.v.uvec.xyz.y; tplane.C = gridp->gmatrix.v.uvec.xyz.z; tplane.D = gridp->planeD; compute_point_on_plane(&gpos, &tplane, pos); dxz = vm_vec_dist(pos, &gpos)/8.0f; gv = &gridp->gmatrix.v.uvec; if (gv->xyz.x * pos->xyz.x + gv->xyz.y * pos->xyz.y + gv->xyz.z * pos->xyz.z < -gridp->planeD) gr_set_color(127, 127, 127); else gr_set_color(255, 255, 255); // white rpd_line(&gpos, pos); // Line from grid to object center. tpos = gpos; vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, -dxz/2); vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.fvec, -dxz/2); vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, dxz/2); vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.fvec, dxz/2); rpd_line(&gpos, &tpos); vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, dxz); vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, -dxz); rpd_line(&gpos, &tpos); }
// ----------------------------------------------------------------------------- //do whatever this thing does in a frame void do_controlcen_frame(object *obj) { int best_gun_num; // If a boss level, then Control_center_present will be 0. if (!Control_center_present) return; #ifndef NDEBUG if (!Robot_firing_enabled || (Game_suspended & SUSP_ROBOTS)) return; #else if (!Robot_firing_enabled) return; #endif if (!(Control_center_been_hit || Control_center_player_been_seen)) { if (!(FrameCount % 8)) { // Do every so often... vms_vector vec_to_player; fix dist_to_player; int i; segment *segp = &Segments[obj->segnum]; // This is a hack. Since the control center is not processed by // ai_do_frame, it doesn't know to deal with cloaked dudes. It // seems to work in single-player mode because it is actually using // the value of Believed_player_position that was set by the last // person to go through ai_do_frame. But since a no-robots game // never goes through ai_do_frame, I'm making it so the control // center can spot cloaked dudes. if (Game_mode & GM_MULTI) Believed_player_pos = Objects[Players[Player_num].objnum].pos; // Hack for special control centers which are isolated and not reachable because the // real control center is inside the boss. for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) if (segp->children[i] != -1) break; if (i == MAX_SIDES_PER_SEGMENT) return; vm_vec_sub(&vec_to_player, &ConsoleObject->pos, &obj->pos); dist_to_player = vm_vec_normalize_quick(&vec_to_player); if (dist_to_player < F1_0*200) { Control_center_player_been_seen = player_is_visible_from_object(obj, &obj->pos, 0, &vec_to_player, 0); Control_center_next_fire_time = 0; } } return; } if ((Control_center_next_fire_time < 0) && !(Player_is_dead && (GameTime > Player_time_of_death+F1_0*2))) { if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) best_gun_num = calc_best_gun(N_controlcen_guns, Gun_pos, Gun_dir, &Believed_player_pos); else best_gun_num = calc_best_gun(N_controlcen_guns, Gun_pos, Gun_dir, &ConsoleObject->pos); if (best_gun_num != -1) { vms_vector vec_to_goal; fix dist_to_player; fix delta_fire_time; if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) { vm_vec_sub(&vec_to_goal, &Believed_player_pos, &Gun_pos[best_gun_num]); dist_to_player = vm_vec_normalize_quick(&vec_to_goal); } else { vm_vec_sub(&vec_to_goal, &ConsoleObject->pos, &Gun_pos[best_gun_num]); dist_to_player = vm_vec_normalize_quick(&vec_to_goal); } if (dist_to_player > F1_0*300) { Control_center_been_hit = 0; Control_center_player_been_seen = 0; return; } #ifdef NETWORK if (Game_mode & GM_MULTI) multi_send_controlcen_fire(&vec_to_goal, best_gun_num, obj-Objects); #endif Laser_create_new_easy( &vec_to_goal, &Gun_pos[best_gun_num], obj-Objects, CONTROLCEN_WEAPON_NUM, 1); // 1/4 of time, fire another thing, not directly at player, so it might hit him if he's constantly moving. if (rand() < 32767/4) { vms_vector randvec; make_random_vector(&randvec); vm_vec_scale_add2(&vec_to_goal, &randvec, F1_0/4); vm_vec_normalize_quick(&vec_to_goal); #ifdef NETWORK if (Game_mode & GM_MULTI) multi_send_controlcen_fire(&vec_to_goal, best_gun_num, obj-Objects); #endif Laser_create_new_easy( &vec_to_goal, &Gun_pos[best_gun_num], obj-Objects, CONTROLCEN_WEAPON_NUM, 1); } delta_fire_time = (NDL - Difficulty_level) * F1_0/4; if (Game_mode & GM_MULTI) // slow down rate of fire in multi player delta_fire_time *= 2; Control_center_next_fire_time = delta_fire_time; } } else Control_center_next_fire_time -= FrameTime; }
do_endlevel_frame() { static fix timer; vms_vector save_last_pos; static fix explosion_wait1=0; static fix explosion_wait2=0; static fix bank_rate; static fix ext_expl_halflife; save_last_pos = ConsoleObject->last_pos; //don't let move code change this object_move_all(); ConsoleObject->last_pos = save_last_pos; if (ext_expl_playing) { external_explosion.lifeleft -= FrameTime; do_explosion_sequence(&external_explosion); if (external_explosion.lifeleft < ext_expl_halflife) mine_destroyed = 1; if (external_explosion.flags & OF_SHOULD_BE_DEAD) ext_expl_playing = 0; } if (cur_fly_speed != desired_fly_speed) { fix delta = desired_fly_speed - cur_fly_speed; fix frame_accel = fixmul(FrameTime,FLY_ACCEL); if (abs(delta) < frame_accel) cur_fly_speed = desired_fly_speed; else if (delta > 0) cur_fly_speed += frame_accel; else cur_fly_speed -= frame_accel; } //do big explosions if (!outside_mine) { if (Endlevel_sequence==EL_OUTSIDE) { vms_vector tvec; vm_vec_sub(&tvec,&ConsoleObject->pos,&mine_side_exit_point); if (vm_vec_dot(&tvec,&mine_exit_orient.fvec) > 0) { object *tobj; outside_mine = 1; tobj = object_create_explosion(exit_segnum,&mine_side_exit_point,i2f(50),VCLIP_BIG_PLAYER_EXPLOSION); if (tobj) { external_explosion = *tobj; tobj->flags |= OF_SHOULD_BE_DEAD; flash_scale = 0; //kill lights in mine ext_expl_halflife = tobj->lifeleft; ext_expl_playing = 1; } digi_link_sound_to_pos( SOUND_BIG_ENDLEVEL_EXPLOSION, exit_segnum, 0, &mine_side_exit_point, 0, i2f(3)/4 ); } } //do explosions chasing player if ((explosion_wait1-=FrameTime) < 0) { vms_vector tpnt; int segnum; object *expl; static int sound_count; vm_vec_scale_add(&tpnt,&ConsoleObject->pos,&ConsoleObject->orient.fvec,-ConsoleObject->size*5); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*15); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*15); segnum = find_point_seg(&tpnt,ConsoleObject->segnum); if (segnum != -1) { expl = object_create_explosion(segnum,&tpnt,i2f(20),VCLIP_BIG_PLAYER_EXPLOSION); if (rand()<10000 || ++sound_count==7) { //pseudo-random digi_link_sound_to_pos( SOUND_TUNNEL_EXPLOSION, segnum, 0, &tpnt, 0, F1_0 ); sound_count=0; } } explosion_wait1 = 0x2000 + rand()/4; } } //do little explosions on walls if (Endlevel_sequence >= EL_FLYTHROUGH && Endlevel_sequence < EL_OUTSIDE) if ((explosion_wait2-=FrameTime) < 0) { vms_vector tpnt; fvi_query fq; fvi_info hit_data; //create little explosion on wall vm_vec_copy_scale(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*100); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*100); vm_vec_add2(&tpnt,&ConsoleObject->pos); if (Endlevel_sequence == EL_FLYTHROUGH) vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*200); else vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*60); //find hit point on wall fq.p0 = &ConsoleObject->pos; fq.p1 = &tpnt; fq.startseg = ConsoleObject->segnum; fq.rad = 0; fq.thisobjnum = 0; fq.ignore_obj_list = NULL; fq.flags = 0; find_vector_intersection(&fq,&hit_data); if (hit_data.hit_type==HIT_WALL && hit_data.hit_seg!=-1) object_create_explosion(hit_data.hit_seg,&hit_data.hit_pnt,i2f(3)+rand()*6,VCLIP_SMALL_EXPLOSION); explosion_wait2 = (0xa00 + rand()/8)/2; } switch (Endlevel_sequence) { case EL_OFF: return; case EL_FLYTHROUGH: { do_endlevel_flythrough(0); if (ConsoleObject->segnum == transition_segnum) { int objnum; Endlevel_sequence = EL_LOOKBACK; objnum = obj_create(OBJ_CAMERA, 0, ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0, CT_NONE,MT_NONE,RT_NONE); if (objnum == -1) { //can't get object, so abort mprintf((1, "Can't get object for endlevel sequence. Aborting endlevel sequence.\n")); stop_endlevel_sequence(); return; } Viewer = endlevel_camera = &Objects[objnum]; select_cockpit(CM_LETTERBOX); fly_objects[1] = fly_objects[0]; fly_objects[1].obj = endlevel_camera; fly_objects[1].speed = (5*cur_fly_speed)/4; fly_objects[1].offset_frac = 0x4000; vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,i2f(7)); timer=0x20000; } break; } case EL_LOOKBACK: { do_endlevel_flythrough(0); do_endlevel_flythrough(1); if (timer>0) { timer -= FrameTime; if (timer < 0) //reduce speed fly_objects[1].speed = fly_objects[0].speed; } if (endlevel_camera->segnum == exit_segnum) { vms_angvec cam_angles,exit_seg_angles; Endlevel_sequence = EL_OUTSIDE; timer = i2f(2); vm_vec_negate(&endlevel_camera->orient.fvec); vm_vec_negate(&endlevel_camera->orient.rvec); vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient); vm_extract_angles_matrix(&exit_seg_angles,&mine_exit_orient); bank_rate = (-exit_seg_angles.b - cam_angles.b)/2; ConsoleObject->control_type = endlevel_camera->control_type = CT_NONE; //_MARK_("Starting outside");//Commented out by KRB #ifdef SLEW_ON slew_obj = endlevel_camera; #endif } break; } case EL_OUTSIDE: { #ifndef SLEW_ON vms_angvec cam_angles; #endif vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifndef SLEW_ON vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,-2*cur_fly_speed)); vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.uvec,fixmul(FrameTime,-cur_fly_speed/10)); vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient); cam_angles.b += fixmul(bank_rate,FrameTime); vm_angles_2_matrix(&endlevel_camera->orient,&cam_angles); #endif timer -= FrameTime; if (timer < 0) { Endlevel_sequence = EL_STOPPED; vm_extract_angles_matrix(&player_angles,&ConsoleObject->orient); timer = i2f(3); } break; } case EL_STOPPED: { get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); timer -= FrameTime; if (timer < 0) { #ifdef SLEW_ON slew_obj = endlevel_camera; _do_slew_movement(endlevel_camera,1,1); timer += FrameTime; //make time stop break; #else #ifdef SHORT_SEQUENCE stop_endlevel_sequence(); #else Endlevel_sequence = EL_PANNING; vm_extract_angles_matrix(&camera_cur_angles,&endlevel_camera->orient); timer = i2f(3); if (Game_mode & GM_MULTI) { // try to skip part of the seq if multiplayer stop_endlevel_sequence(); return; } //mprintf((0,"Switching to pan...\n")); #endif //SHORT_SEQUENCE #endif //SLEW_ON } break; } #ifndef SHORT_SEQUENCE case EL_PANNING: { #ifndef SLEW_ON int mask; #endif get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifdef SLEW_ON _do_slew_movement(endlevel_camera,1,1); #else get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos); mask = chase_angles(&camera_cur_angles,&camera_desired_angles); vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles); if ((mask&5) == 5) { vms_vector tvec; Endlevel_sequence = EL_CHASING; //_MARK_("Done outside");//Commented out -KRB vm_vec_normalized_dir_quick(&tvec,&station_pos,&ConsoleObject->pos); vm_vector_2_matrix(&ConsoleObject->orient,&tvec,&surface_orient.uvec,NULL); desired_fly_speed *= 2; //mprintf((0,"Switching to chase...\n")); } #endif break; } case EL_CHASING: { fix d,speed_scale; #ifdef SLEW_ON _do_slew_movement(endlevel_camera,1,1); #endif get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos); chase_angles(&camera_cur_angles,&camera_desired_angles); #ifndef SLEW_ON vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles); #endif d = vm_vec_dist_quick(&ConsoleObject->pos,&endlevel_camera->pos); speed_scale = fixdiv(d,i2f(0x20)); if (d<f1_0) d=f1_0; get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifndef SLEW_ON vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,fixmul(speed_scale,cur_fly_speed))); if (vm_vec_dist(&ConsoleObject->pos,&station_pos) < i2f(10)) stop_endlevel_sequence(); #endif break; } #endif //ifdef SHORT_SEQUENCE } }
do_endlevel_flythrough(int n) { object *obj; segment *pseg; int old_player_seg; flydata = &fly_objects[n]; obj = flydata->obj; old_player_seg = obj->segnum; //move the player for this frame if (!flydata->first_time) { vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime); angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime); vm_angles_2_matrix(&obj->orient,&flydata->angles); } //check new player seg update_object_seg(obj); pseg = &Segments[obj->segnum]; if (flydata->first_time || obj->segnum != old_player_seg) { //moved into new seg vms_vector curcenter,nextcenter; fix step_size,seg_time; short entry_side,exit_side; //what sides we entry and leave through vms_vector dest_point; //where we are heading (center of exit_side) vms_angvec dest_angles; //where we want to be pointing vms_matrix dest_orient; int up_side; //find new exit side if (!flydata->first_time) { entry_side = matt_find_connect_side(obj->segnum,old_player_seg); exit_side = Side_opposite[entry_side]; } if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1) exit_side = find_exit_side(obj); { //find closest side to align to fix d,largest_d=-f1_0; int i; for (i=0;i<6;i++) { #ifdef COMPACT_SEGS vms_vector v1; get_side_normal(pseg, i, 0, &v1 ); d = vm_vec_dot(&v1,&flydata->obj->orient.uvec); #else d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec); #endif if (d > largest_d) {largest_d = d; up_side=i;} } } //update target point & angles compute_center_point_on_side(&dest_point,pseg,exit_side); //update target point and movement points //offset object sideways if (flydata->offset_frac) { int s0=-1,s1,i; vms_vector s0p,s1p; fix dist; for (i=0;i<6;i++) if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side]) if (s0==-1) s0 = i; else s1 = i; compute_center_point_on_side(&s0p,pseg,s0); compute_center_point_on_side(&s1p,pseg,s1); dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac); if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT) dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT; flydata->offset_dist = dist; vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist); } vm_vec_sub(&flydata->step,&dest_point,&obj->pos); step_size = vm_vec_normalize_quick(&flydata->step); vm_vec_scale(&flydata->step,flydata->speed); compute_segment_center(&curcenter,pseg); compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]); vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter); #ifdef COMPACT_SEGS { vms_vector _v1; get_side_normal(pseg, up_side, 0, &_v1 ); vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL); } #else vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL); #endif vm_extract_angles_matrix(&dest_angles,&dest_orient); if (flydata->first_time) vm_extract_angles_matrix(&flydata->angles,&obj->orient); seg_time = fixdiv(step_size,flydata->speed); //how long through seg if (seg_time) { flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time))); flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time))); flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time))); } else { flydata->angles = dest_angles; flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0; } } flydata->first_time=0; }
// function looks at the flying controls and the current velocity to determine a goal velocity // function determines velocity in object's reference frame and goal velocity in object's reference frame void physics_read_flying_controls( matrix * orient, physics_info * pi, control_info * ci, float sim_time, vec3d *wash_rot) { vec3d goal_vel; // goal velocity in local coords, *not* accounting for ramping of velcity float ramp_time_const; // time constant for velocity ramping // apply throttle, unless reverse thrusters are held down if (ci->forward != -1.0f) ci->forward += (ci->forward_cruise_percent / 100.0f); // give control input to cause rotation in engine wash extern int Wash_on; if ( wash_rot && Wash_on ) { ci->pitch += wash_rot->xyz.x; ci->bank += wash_rot->xyz.z; ci->heading += wash_rot->xyz.y; } if (ci->pitch > 1.0f ) ci->pitch = 1.0f; else if (ci->pitch < -1.0f ) ci->pitch = -1.0f; if (ci->vertical > 1.0f ) ci->vertical = 1.0f; else if (ci->vertical < -1.0f ) ci->vertical = -1.0f; if (ci->heading > 1.0f ) ci->heading = 1.0f; else if (ci->heading < -1.0f ) ci->heading = -1.0f; if (ci->sideways > 1.0f ) ci->sideways = 1.0f; else if (ci->sideways < -1.0f ) ci->sideways = -1.0f; if (ci->bank > 1.0f ) ci->bank = 1.0f; else if (ci->bank < -1.0f ) ci->bank = -1.0f; if ( pi->flags & PF_AFTERBURNER_ON ){ //SparK: modifield to accept reverse burners if (!(pi->afterburner_max_reverse_vel > 0.0f)){ ci->forward = 1.0f; } } if (ci->forward > 1.0f ) ci->forward = 1.0f; else if (ci->forward < -1.0f ) ci->forward = -1.0f; if (!Flight_controls_follow_eyepoint_orientation || (Player_obj == NULL) || (Player_obj->type != OBJ_SHIP)) { // Default behavior; eyepoint orientation has no effect on controls pi->desired_rotvel.xyz.x = ci->pitch * pi->max_rotvel.xyz.x; pi->desired_rotvel.xyz.y = ci->heading * pi->max_rotvel.xyz.y; } else { // Optional behavior; pitch and yaw are always relative to the eyepoint // orientation (excluding slew) vec3d tmp_vec, new_rotvel; matrix tmp_mat, eyemat, rotvelmat; ship_get_eye(&tmp_vec, &eyemat, Player_obj, false); vm_copy_transpose_matrix(&tmp_mat, &Player_obj->orient); vm_matrix_x_matrix(&rotvelmat, &tmp_mat, &eyemat); vm_vec_rotate(&new_rotvel, &pi->max_rotvel, &rotvelmat); vm_vec_unrotate(&tmp_vec, &pi->max_rotvel, &rotvelmat); new_rotvel.xyz.x = tmp_vec.xyz.x; new_rotvel.xyz.x = ci->pitch * new_rotvel.xyz.x; new_rotvel.xyz.y = ci->heading * new_rotvel.xyz.y; vm_vec_unrotate(&tmp_vec, &new_rotvel, &rotvelmat); pi->desired_rotvel = tmp_vec; } float delta_bank; #ifdef BANK_WHEN_TURN // To change direction of bank, negate the whole expression. // To increase magnitude of banking, decrease denominator. // Adam: The following statement is all the math for banking while turning. delta_bank = - (ci->heading * pi->max_rotvel.xyz.y) * pi->delta_bank_const; #else delta_bank = 0.0f; #endif pi->desired_rotvel.xyz.z = ci->bank * pi->max_rotvel.xyz.z + delta_bank; pi->forward_thrust = ci->forward; pi->vert_thrust = ci->vertical; //added these two in order to get side and forward thrusters pi->side_thrust = ci->sideways; //to glow brighter when the ship is moving in the right direction -Bobboau if ( pi->flags & PF_AFTERBURNER_ON ) { goal_vel.xyz.x = ci->sideways*pi->afterburner_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->afterburner_max_vel.xyz.y; if(ci->forward < 0.0f) goal_vel.xyz.z = ci->forward* pi->afterburner_max_reverse_vel; else goal_vel.xyz.z = ci->forward* pi->afterburner_max_vel.xyz.z; } else if ( pi->flags & PF_BOOSTER_ON ) { goal_vel.xyz.x = ci->sideways*pi->booster_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->booster_max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->booster_max_vel.xyz.z; } else { goal_vel.xyz.x = ci->sideways*pi->max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->max_vel.xyz.z; } if ( goal_vel.xyz.z < -pi->max_rear_vel && !(pi->flags & PF_AFTERBURNER_ON) ) goal_vel.xyz.z = -pi->max_rear_vel; if ( pi->flags & PF_ACCELERATES ) { // // Determine *resultant* DESIRED VELOCITY (desired_vel) accounting for RAMPING of velocity // Use LOCAL coordinates // if slide_enabled, ramp velocity for x and y, otherwise set goal (0) // always ramp velocity for z // // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast. // Scale according to reduced_damp_time_expansion. float reduced_damp_ramp_time_expansion; if ( pi->flags & PF_REDUCED_DAMP && !timestamp_elapsed(pi->reduced_damp_decay) ) { float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME; reduced_damp_ramp_time_expansion = 1.0f + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left; } else { reduced_damp_ramp_time_expansion = 1.0f; } if (pi->flags & PF_SLIDE_ENABLED) { // determine the local velocity // deterimine whether accelerating or decleration toward goal for x if ( goal_vel.xyz.x > 0.0f ) { if ( goal_vel.xyz.x >= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.x < 0.0f ) { if ( goal_vel.xyz.x <= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.x = velocity_ramp(pi->prev_ramp_vel.xyz.x, goal_vel.xyz.x, ramp_time_const, sim_time); // deterimine whether accelerating or decleration toward goal for y if ( goal_vel.xyz.y > 0.0f ) { if ( goal_vel.xyz.y >= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.y < 0.0f ) { if ( goal_vel.xyz.y <= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.y = velocity_ramp( pi->prev_ramp_vel.xyz.y, goal_vel.xyz.y, ramp_time_const, sim_time); } else { // slide not enabled pi->prev_ramp_vel.xyz.x = 0.0f; pi->prev_ramp_vel.xyz.y = 0.0f; } // deterimine whether accelerating or decleration toward goal for z if ( goal_vel.xyz.z > 0.0f ) { if ( goal_vel.xyz.z >= pi->prev_ramp_vel.xyz.z ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_forward_accel_time_const; else if (pi->flags & PF_BOOSTER_ON) ramp_time_const = pi->booster_forward_accel_time_const; else ramp_time_const = pi->forward_accel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } } else if ( goal_vel.xyz.z < 0.0f ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_reverse_accel; else ramp_time_const = pi->forward_decel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.z = velocity_ramp(pi->prev_ramp_vel.xyz.z, goal_vel.xyz.z, ramp_time_const, sim_time); //Deternine the current dynamic glide cap, and ramp to it //This is outside the normal "glide" block since we want the cap to adjust whether or not the ship is in glide mode float dynamic_glide_cap_goal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) { dynamic_glide_cap_goal = ( goal_vel.xyz.z >= 0.0f ) ? pi->afterburner_max_vel.xyz.z : pi->afterburner_max_reverse_vel; } else { //Use the maximum value in X, Y, and Z (including overclocking) dynamic_glide_cap_goal = MAX(MAX(pi->max_vel.xyz.x,pi->max_vel.xyz.y), pi->max_vel.xyz.z); } pi->cur_glide_cap = velocity_ramp(pi->cur_glide_cap, dynamic_glide_cap_goal, ramp_time_const, sim_time); if ( (pi->flags & PF_GLIDING) || (pi->flags & PF_FORCE_GLIDE ) ) { pi->desired_vel = pi->vel; //SUSHI: A (hopefully better) approach to dealing with accelerations in glide mode //Get *actual* current velocities along each axis and use those instead of ramped velocities vec3d local_vel; vm_vec_rotate(&local_vel, &pi->vel, orient); //Having pi->glide_cap == 0 means we're using a dynamic glide cap float curGlideCap = 0.0f; if (pi->glide_cap == 0.0f) curGlideCap = pi->cur_glide_cap; else curGlideCap = pi->glide_cap; //If we're near the (positive) glide cap, decay velocity where we aren't thrusting //This is a hack, but makes the flight feel a lot smoother //Don't do this if we aren't applying any thrust, we have no glide cap, or the accel multiplier is 0 (no thrust while gliding) float cap_decay_threshold = 0.95f; float cap_decay_amount = 0.2f; if (curGlideCap >= 0.0f && vm_vec_mag(&pi->desired_vel) >= cap_decay_threshold * curGlideCap && vm_vec_mag(&goal_vel) > 0.0f && pi->glide_accel_mult != 0.0f) { if (goal_vel.xyz.x == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, -cap_decay_amount * local_vel.xyz.x); if (goal_vel.xyz.y == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, -cap_decay_amount * local_vel.xyz.y); if (goal_vel.xyz.z == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, -cap_decay_amount * local_vel.xyz.z); } //The glide_ramp function uses (basically) the same math as the velocity ramp so that thruster power is consistent //Only ramp if the glide cap is positive float xVal = glide_ramp(local_vel.xyz.x, goal_vel.xyz.x, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float yVal = glide_ramp(local_vel.xyz.y, goal_vel.xyz.y, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float zVal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->afterburner_forward_accel_time_const, pi->glide_accel_mult, sim_time); else { if (goal_vel.xyz.z >= 0.0f) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_accel_time_const, pi->glide_accel_mult, sim_time); else zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_decel_time_const, pi->glide_accel_mult, sim_time); } //Compensate for effect of dampening: normal flight cheats here, so /we make up for it this way so glide acts the same way xVal *= pi->side_slip_time_const / sim_time; yVal *= pi->side_slip_time_const / sim_time; if (pi->use_newtonian_damp) zVal *= pi->side_slip_time_const / sim_time; vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, zVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, xVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, yVal); // Only do the glide cap if we have one and are actively thrusting in some direction. if ( curGlideCap >= 0.0f && (ci->forward != 0.0f || ci->sideways != 0.0f || ci->vertical != 0.0f) ) { float currentmag = vm_vec_mag(&pi->desired_vel); if ( currentmag > curGlideCap ) { vm_vec_scale( &pi->desired_vel, curGlideCap / currentmag ); } } } else { // this translates local desired velocities to world velocities vm_vec_zero(&pi->desired_vel); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.rvec, pi->prev_ramp_vel.xyz.x ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.uvec, pi->prev_ramp_vel.xyz.y ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.fvec, pi->prev_ramp_vel.xyz.z ); } } else // object does not accelerate (PF_ACCELERATES not set) pi->desired_vel = pi->vel; }
void physics_apply_shock(vec3d *direction_vec, float pressure, physics_info *pi, matrix *orient, vec3d *min, vec3d *max, float radius) { vec3d normal; vec3d local_torque, temp_torque, torque; vec3d impact_vec; vec3d area; vec3d sin; if (radius > MAX_RADIUS) { return; } vm_vec_normalize_safe ( direction_vec ); area.xyz.x = (max->xyz.y - min->xyz.z) * (max->xyz.z - min->xyz.z); area.xyz.y = (max->xyz.x - min->xyz.x) * (max->xyz.z - min->xyz.z); area.xyz.z = (max->xyz.x - min->xyz.x) * (max->xyz.y - min->xyz.y); normal.xyz.x = vm_vec_dotprod( direction_vec, &orient->vec.rvec ); normal.xyz.y = vm_vec_dotprod( direction_vec, &orient->vec.uvec ); normal.xyz.z = vm_vec_dotprod( direction_vec, &orient->vec.fvec ); sin.xyz.x = fl_sqrt( fl_abs(1.0f - normal.xyz.x*normal.xyz.x) ); sin.xyz.y = fl_sqrt( fl_abs(1.0f - normal.xyz.y*normal.xyz.y) ); sin.xyz.z = fl_sqrt( fl_abs(1.0f - normal.xyz.z*normal.xyz.z) ); vm_vec_make( &torque, 0.0f, 0.0f, 0.0f ); // find the torque exerted due to the shockwave hitting each face // model the effect of the shockwave as if the shockwave were a plane of projectiles, // all moving in the direction direction_vec. then find the torque as the cross prod // of the force (pressure * area * normal * sin * scale * mass) // normal takes account the fraction of the surface exposed to the shockwave // the sin term is not technically needed but "feels" better // scale factors out the increase in area with larger objects // more massive objects get less rotation // find torque due to forces on the right/left face if ( normal.xyz.x < 0.0f ) // normal < 0, hits the right face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, max->xyz.x * pressure * area.xyz.x * normal.xyz.x * sin.xyz.x / pi->mass ); else // normal > 0, hits the left face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, min->xyz.x * pressure * area.xyz.x * -normal.xyz.x * sin.xyz.x / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the up/down face if ( normal.xyz.y < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, max->xyz.y * pressure * area.xyz.y * normal.xyz.y * sin.xyz.y / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, min->xyz.y * pressure * area.xyz.y * -normal.xyz.y * sin.xyz.y / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the forward/backward face if ( normal.xyz.z < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, max->xyz.z * pressure * area.xyz.z * normal.xyz.z * sin.xyz.z / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, min->xyz.z * pressure * area.xyz.z * -normal.xyz.z * sin.xyz.z / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // compute delta rotvel, scale according to blast and radius float scale; if (radius < MIN_RADIUS) { scale = 1.0f; } else { scale = (MAX_RADIUS - radius)/(MAX_RADIUS-MIN_RADIUS); } // set shockwave shake amplitude, duration, flag pi->shockwave_shake_amp = (float)(MAX_SHAKE*(pressure/STD_PRESSURE)*scale); pi->shockwave_decay = timestamp( SW_BLAST_DURATION ); pi->flags |= PF_IN_SHOCKWAVE; // safety dance if (!(IS_VEC_NULL_SQ_SAFE(&torque))) { vec3d delta_rotvel; vm_vec_rotate( &local_torque, &torque, orient ); vm_vec_copy_normalize(&delta_rotvel, &local_torque); vm_vec_scale(&delta_rotvel, (float)(MAX_ROTVEL*(pressure/STD_PRESSURE)*scale)); // nprintf(("Physics", "rotvel scale %f\n", (MAX_ROTVEL*(pressure/STD_PRESSURE)*scale))); vm_vec_add2(&pi->rotvel, &delta_rotvel); } // set reduced translational damping, set flags float velocity_scale = (float)MAX_VEL*scale; pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, velocity_scale*pi->mass ); vm_vec_scale_add2( &pi->vel, direction_vec, velocity_scale ); vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient); // set so velocity will ramp starting from current speed // check that kick from shockwave is not too large if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_apply_shock [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } }