diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fe96c4187..2cd674c1a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,9 @@ D1X-Rebirth Changelog +20100121 +-------- +main/newdemo.c: Removed the new Demo Interpolating code and added the improvements to the old one + 20100120 -------- arch/include/event.h, arch/sdl/event.c, arch/sdl/window.c, main/automap.c, main/game.c, main/gamecntl.c, main/kconfig.c, main/menu.c, main/net_ipx.c, main/net_udp.c, main/newmenu.c, main/state.c: Add EVENT_WINDOW_ACTIVATED, change EVENT_DRAW to EVENT_WINDOW_DRAW and EVENT_CLOSE to EVENT_WINDOW_CLOSE diff --git a/main/newdemo.c b/main/newdemo.c index 7660ecd48..176dcc73b 100644 --- a/main/newdemo.c +++ b/main/newdemo.c @@ -138,6 +138,8 @@ COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #define NORMAL_PLAYBACK 0 #define SKIP_PLAYBACK 1 +#define INTERPOLATE_PLAYBACK 2 +#define INTERPOL_FACTOR (F1_0 + (F1_0/5)) #define DEMO_VERSION_SHAREWARE 5 #define DEMO_VERSION 13 @@ -2726,90 +2728,129 @@ void newdemo_back_frames(int frames) } -void DoZicoInterpolate (fix recorded_time) +/* + * routine to interpolate the viewer position. the current position is + * stored in the Viewer object. Save this position, and read the next + * frame to get all objects read in. Calculate the delta playback and + * the delta recording frame times between the two frames, then intepolate + * the viewers position accordingly. nd_recorded_time is the time that it + * took the recording to render the frame that we are currently looking + * at. +*/ + +void interpolate_frame(fix d_play, fix d_recorded) { - static fix delay_time=0; - int i = 0, j = 0; - object SavObj[MAX_OBJECTS]; - int SavObjNum; + int i, j, num_cur_objs; + fix factor; + object *cur_objs; + static fix InterpolStep = fl2f(.01); - memset(&SavObj, 0, sizeof(object)*MAX_OBJECTS); - - if (NewdemoFrameCount < 2) + InterpolStep -= FrameTime; + // This interpolating looks just more crappy on high FPS, so let's not even waste performance on it. + if (InterpolStep >= 0) return; + InterpolStep = fl2f(.01); + + factor = fixdiv(d_play, d_recorded); + if (factor > F1_0) + factor = F1_0; + + num_cur_objs = Highest_object_index; + cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1)); + if (cur_objs == NULL) { + Int3(); + return; + } + for (i = 0; i <= num_cur_objs; i++) + memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object)); - // Read in the next Frame and store the objects from it. Newdemo_vcr_state = ND_STATE_PAUSED; - if (newdemo_read_frame_information(0) == -1) - { + if (newdemo_read_frame_information(0) == -1) { + d_free(cur_objs); newdemo_stop_playback(); return; } - Newdemo_vcr_state = ND_STATE_PLAYBACK; - for (i=0; i<=Highest_object_index; i++) - memcpy(&SavObj[i], &Objects[i], sizeof(object)); - SavObjNum=Highest_object_index; - newdemo_back_frames(1); - // Do necessary steps to repeat a Demo-Frame if necessary - if (recorded_time > FrameTime) - { - if (delay_time <= 0) - { - delay_time += recorded_time; - } - else if (delay_time > 0) - { - newdemo_back_frames(1); - delay_time -= FrameTime; - } - } - - if (!Newdemo_do_interpolate) - return; - - // Interpolate objects. - for (i = 0; i <= SavObjNum; i++) - { - for (j = 0; j <= Highest_object_index; j++) - { - if (SavObj[i].signature == Objects[j].signature) - { - vms_vector fvec1, fvec2, rvec1, rvec2; - fix mag1; + for (i = 0; i <= num_cur_objs; i++) { + for (j = 0; j <= Highest_object_index; j++) { + if (cur_objs[i].signature == Objects[j].signature) { + sbyte render_type = cur_objs[i].render_type; + fix delta_x, delta_y, delta_z; /* * HACK: Good 'ol Descent does not check for duplicate signatures when creating objects. * So in case our two objects have a huge distance, we assume they are different. * Interpolating would not make much sense in that case nevertheless. */ - if (vm_vec_dist(&SavObj[i].pos, &Objects[j].pos) > (Objects[j].size*2)) + if (vm_vec_dist(&cur_objs[i].pos, &Objects[j].pos) > (Objects[j].size*2)) continue; - - vm_vec_avg(&Objects[j].pos, &SavObj[i].pos, &Objects[j].pos); - if (!((SavObj[i].render_type == RT_LASER) || (SavObj[i].render_type == RT_FIREBALL) || (SavObj[i].render_type == RT_POWERUP))) - { - fvec1 = SavObj[i].orient.fvec; + // Extract the angles from the object orientation matrix. + // Some of this code taken from ai_turn_towards_vector + // Don't do the interpolation on certain render types which don't use an orientation matrix + + if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) { + + vms_vector fvec1, fvec2, rvec1, rvec2; + fix mag1; + + fvec1 = cur_objs[i].orient.fvec; + vm_vec_scale(&fvec1, F1_0-factor); fvec2 = Objects[j].orient.fvec; + vm_vec_scale(&fvec2, factor); vm_vec_add2(&fvec1, &fvec2); mag1 = vm_vec_normalize_quick(&fvec1); if (mag1 > F1_0/256) { - rvec1 = SavObj[i].orient.rvec; + rvec1 = cur_objs[i].orient.rvec; + vm_vec_scale(&rvec1, F1_0-factor); rvec2 = Objects[j].orient.rvec; + vm_vec_scale(&rvec2, factor); vm_vec_add2(&rvec1, &rvec2); - vm_vec_normalize_quick(&rvec1); - vm_vector_2_matrix(&Objects[j].orient, &fvec1, NULL, &rvec1); + vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1 + vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1); } } + + // Interpolate the object position. This is just straight linear + // interpolation. + + delta_x = Objects[j].pos.x - cur_objs[i].pos.x; + delta_y = Objects[j].pos.y - cur_objs[i].pos.y; + delta_z = Objects[j].pos.z - cur_objs[i].pos.z; + + delta_x = fixmul(delta_x, factor); + delta_y = fixmul(delta_y, factor); + delta_z = fixmul(delta_z, factor); + + cur_objs[i].pos.x += delta_x; + cur_objs[i].pos.y += delta_y; + cur_objs[i].pos.z += delta_z; } } } + + // get back to original position in the demo file. Reread the current + // frame information again to reset all of the object stuff not covered + // with Highest_object_index and the object array (previously rendered + // objects, etc....) + + newdemo_back_frames(1); + newdemo_back_frames(1); + if (newdemo_read_frame_information(0) == -1) + newdemo_stop_playback(); + Newdemo_vcr_state = ND_STATE_PLAYBACK; + + for (i = 0; i <= num_cur_objs; i++) + memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object)); + Highest_object_index = num_cur_objs; + d_free(cur_objs); } void newdemo_playback_one_frame() { int frames_back, i, level; + static fix base_interpol_time = 0; + static fix d_recorded = 0; for (i = 0; i < MAX_PLAYERS; i++) if (Players[i].flags & PLAYER_FLAGS_CLOAKED) @@ -2890,9 +2931,9 @@ void newdemo_playback_one_frame() else { // First, uptate the total playback time to date. Then we check to see - // if we need to change the playback style to skip frames. - // In the end, interpolate between current and next frame and repeat frames - // if FPS are higher than recorded FPS. + // if we need to change the playback style to interpolate frames or + // skip frames based on where the playback time is relative to the + // recorded time. if (NewdemoFrameCount <= 0) nd_playback_total = nd_recorded_total; // baseline total playback time @@ -2903,24 +2944,97 @@ void newdemo_playback_one_frame() { if (nd_playback_total > nd_recorded_total) playback_style = SKIP_PLAYBACK; + + //if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) // no matte rhow we look at it: this does not make ANY sense! + if (nd_recorded_total > 0 && nd_recorded_time > 0) + { + playback_style = INTERPOLATE_PLAYBACK; + nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time + base_interpol_time = nd_recorded_total; + d_recorded = nd_recorded_time; // baseline delta recorded + } } - if (newdemo_read_frame_information(0) == -1) { - newdemo_stop_playback(); + + if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) { + fix d_play = 0; + + if (nd_recorded_total - nd_playback_total < FrameTime) { + d_recorded = nd_recorded_total - nd_playback_total; + + while (nd_recorded_total - nd_playback_total < FrameTime) { + object *cur_objs; + int i, j, num_objs, level; + + num_objs = Highest_object_index; + cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1)); + if (cur_objs == NULL) { + Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs); + break; + } + for (i = 0; i <= num_objs; i++) + memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object)); + + level = Current_level_num; + if (newdemo_read_frame_information(0) == -1) { + d_free(cur_objs); + newdemo_stop_playback(); + return; + } + if (level != Current_level_num) { + d_free(cur_objs); + if (newdemo_read_frame_information(0) == -1) + newdemo_stop_playback(); + break; + } + + // for each new object in the frame just read in, determine if there is + // a corresponding object that we have been interpolating. If so, then + // copy that interpolated object to the new Objects array so that the + // interpolated position and orientation can be preserved. + + for (i = 0; i <= num_objs; i++) { + for (j = 0; j <= Highest_object_index; j++) { + if (cur_objs[i].signature == Objects[j].signature) { + /* + * HACK: Good 'ol Descent does not check for duplicate signatures when creating objects. + * So in case our two objects have a huge distance, we assume they are different. + * Interpolating would not make much sense in that case nevertheless. + */ + if (vm_vec_dist(&cur_objs[i].pos, &Objects[j].pos) > (Objects[j].size*2)) + continue; + + memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix)); + memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector)); + break; + } + } + } + d_free(cur_objs); + d_recorded += nd_recorded_time; + base_interpol_time = nd_playback_total - FrameTime; + } + } + + d_play = nd_playback_total - base_interpol_time; + interpolate_frame(d_play, d_recorded); return; } - if (playback_style == SKIP_PLAYBACK) { - while (nd_playback_total > nd_recorded_total) { - if (newdemo_read_frame_information(0) == -1) { - newdemo_stop_playback(); - return; + else { + if (newdemo_read_frame_information(0) == -1) { + newdemo_stop_playback(); + return; + } + if (playback_style == SKIP_PLAYBACK) { + while (nd_playback_total > nd_recorded_total) { + if (newdemo_read_frame_information(0) == -1) { + newdemo_stop_playback(); + return; + } } } } } - - if (Newdemo_vcr_state == ND_STATE_PLAYBACK) - DoZicoInterpolate(nd_recorded_time); } void newdemo_start_recording()