mirror of
https://github.com/k4zmu2a/SpaceCadetPinball.git
synced 2024-11-14 13:58:04 +01:00
Added new render mode with reduced tearing.
Available under new option Window->Alternative Rendering. Issue #29.
This commit is contained in:
parent
de76557325
commit
0d9610ddb6
9 changed files with 121 additions and 88 deletions
|
@ -98,6 +98,7 @@ BEGIN
|
|||
POPUP "&Window"
|
||||
BEGIN
|
||||
MENUITEM "&Uniform Scaling", Menu1_WindowUniformScale
|
||||
MENUITEM "&Alternative Rendering", Menu1_AlternativeRender
|
||||
END
|
||||
END
|
||||
POPUP "&Help"
|
||||
|
|
|
@ -108,6 +108,8 @@ void options::init(HMENU menuHandle)
|
|||
Options.RightTableBumpKey = get_int(nullptr, "Right Table Bump key", Options.RightTableBumpKey);
|
||||
Options.BottomTableBumpKey = get_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
|
||||
Options.UniformScaling = get_int(nullptr, "Uniform scaling", true);
|
||||
Options.AlternativeRender = get_int(nullptr, "Alternative Render", false);
|
||||
|
||||
menu_check(Menu1_Sounds, Options.Sounds);
|
||||
Sound::Enable(0, 7, Options.Sounds);
|
||||
menu_check(Menu1_Music, Options.Music);
|
||||
|
@ -117,6 +119,7 @@ void options::init(HMENU menuHandle)
|
|||
menu_check(Menu1_3Players, Options.Players == 3);
|
||||
menu_check(Menu1_4Players, Options.Players == 4);
|
||||
menu_check(Menu1_WindowUniformScale, Options.UniformScaling);
|
||||
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
|
||||
auto tmpBuf = memory::allocate(0x1F4u);
|
||||
if (tmpBuf)
|
||||
{
|
||||
|
@ -149,6 +152,7 @@ void options::uninit()
|
|||
set_int(nullptr, "Bottom Table Bump key", Options.BottomTableBumpKey);
|
||||
set_int(nullptr, "Screen Resolution", Options.Resolution);
|
||||
set_int(nullptr, "Uniform scaling", Options.UniformScaling);
|
||||
set_int(nullptr, "Alternative Render", Options.AlternativeRender);
|
||||
}
|
||||
|
||||
void options::path_init(LPCSTR regPath)
|
||||
|
@ -343,6 +347,12 @@ void options::toggle(UINT uIDCheckItem)
|
|||
fullscrn::window_size_changed();
|
||||
fullscrn::paint();
|
||||
break;
|
||||
case Menu1_AlternativeRender:
|
||||
Options.AlternativeRender ^= true;
|
||||
menu_check(Menu1_AlternativeRender, Options.AlternativeRender);
|
||||
fullscrn::window_size_changed();
|
||||
fullscrn::paint();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ struct optionsStruct
|
|||
int BottomTableBumpKeyDft;
|
||||
int Resolution;
|
||||
bool UniformScaling;
|
||||
bool AlternativeRender;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
TPinballTable* pb::MainTable = nullptr;
|
||||
datFileStruct* pb::record_table = nullptr;
|
||||
int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::mode_countdown_, pb::state;
|
||||
int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::mode_countdown_, pb::state, pb::frameCounter = 0;
|
||||
float pb::time_now, pb::time_next, pb::ball_speed_limit;
|
||||
high_score_struct pb::highscore_table[5];
|
||||
bool pb::FullTiltMode = false, pb::cheat_mode = false;
|
||||
|
@ -129,9 +129,7 @@ void pb::reset_table()
|
|||
|
||||
void pb::firsttime_setup()
|
||||
{
|
||||
render::blit = 0;
|
||||
render::update();
|
||||
render::blit = 1;
|
||||
render::update(false);
|
||||
}
|
||||
|
||||
void pb::paint()
|
||||
|
@ -223,6 +221,8 @@ void pb::ballset(int x, int y)
|
|||
|
||||
int pb::frame(int time)
|
||||
{
|
||||
static int frameTime = 0;
|
||||
|
||||
if (time > 100)
|
||||
time = 100;
|
||||
float timeMul = time * 0.001f;
|
||||
|
@ -244,7 +244,29 @@ int pb::frame(int time)
|
|||
nudge::nudge_count = nudgeDec;
|
||||
}
|
||||
timer::check();
|
||||
render::update();
|
||||
|
||||
if (!options::Options.AlternativeRender)
|
||||
{
|
||||
render::update(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Screen update at UPS > screen refresh rate cause tearing.
|
||||
// Especially noticeable on fast moving ball in scaled up window.
|
||||
// Retained render prevents frame skip. The next best thing - complete refresh at fixed rate.
|
||||
render::update(false);
|
||||
|
||||
// Frame time at 60 FPS = 16.(6) ms = (16 + 17 + 17) / 3
|
||||
auto targetTime = frameCounter % 3 == 0 ? 16 : 17;
|
||||
frameTime += time;
|
||||
if (frameTime >= targetTime)
|
||||
{
|
||||
frameTime = min(frameTime - targetTime, 100);
|
||||
render::shift(0, 0, 0, 0, MainTable->Width, MainTable->Height);
|
||||
frameCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
score::update(MainTable->CurScoreStruct);
|
||||
if (!MainTable->TiltLockFlag)
|
||||
{
|
||||
|
@ -288,8 +310,8 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
|
|||
ball->Acceleration.Y = ball->Speed * ball->Acceleration.Y;
|
||||
maths::vector_add(&ball->Acceleration, &vec2);
|
||||
ball->Speed = maths::normalize_2d(&ball->Acceleration);
|
||||
ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.X;
|
||||
ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.Y;
|
||||
ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.X;
|
||||
ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.Y;
|
||||
}
|
||||
|
||||
auto timeDelta2 = timeDelta;
|
||||
|
|
|
@ -10,7 +10,7 @@ class pb
|
|||
public:
|
||||
static int time_ticks;
|
||||
static float ball_speed_limit, time_now, time_next;
|
||||
static int game_mode;
|
||||
static int game_mode, frameCounter;
|
||||
static bool cheat_mode;
|
||||
static datFileStruct* record_table;
|
||||
static TPinballTable* MainTable;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "render.h"
|
||||
#include "memory.h"
|
||||
|
||||
int render::blit = 0;
|
||||
int render::many_dirty, render::many_sprites, render::many_balls;
|
||||
render_sprite_type_struct **render::dirty_list, **render::sprite_list, **render::ball_list;
|
||||
zmap_header_type* render::background_zmap;
|
||||
|
@ -60,46 +59,35 @@ void render::uninit()
|
|||
many_balls = 0;
|
||||
}
|
||||
|
||||
void render::update()
|
||||
void render::update(bool blit)
|
||||
{
|
||||
rectangle_type overlapRect{};
|
||||
|
||||
auto dirtyPtr = dirty_list;
|
||||
for (int index = 0; index < many_dirty; ++dirtyPtr, ++index)
|
||||
for (int index = 0; index < many_dirty; ++index)
|
||||
{
|
||||
auto curSprite = *dirtyPtr;
|
||||
if ((*dirtyPtr)->VisualType != VisualType::None)
|
||||
{
|
||||
if ((*dirtyPtr)->VisualType == VisualType::Sprite)
|
||||
auto curSprite = dirty_list[index];
|
||||
bool clearSprite = false;
|
||||
switch (curSprite->VisualType)
|
||||
{
|
||||
case VisualType::Sprite:
|
||||
if (curSprite->BmpRectCopy.Width > 0)
|
||||
maths::enclosing_box(&curSprite->BmpRectCopy, &curSprite->BmpRect, &curSprite->DirtyRect);
|
||||
|
||||
if (!maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect))
|
||||
{
|
||||
if (maths::rectangle_clip(&curSprite->DirtyRect, &vscreen_rect, &curSprite->DirtyRect))
|
||||
clearSprite = true;
|
||||
else
|
||||
curSprite->DirtyRect.Width = -1;
|
||||
continue;
|
||||
break;
|
||||
case VisualType::None:
|
||||
if (maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect))
|
||||
clearSprite = !curSprite->Bmp;
|
||||
else
|
||||
curSprite->DirtyRect.Width = -1;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
auto yPos = curSprite->DirtyRect.YPosition;
|
||||
auto width = curSprite->DirtyRect.Width;
|
||||
auto xPos = curSprite->DirtyRect.XPosition;
|
||||
auto height = curSprite->DirtyRect.Height;
|
||||
zdrv::fill(&zscreen, width, height, xPos, yPos, 0xFFFF);
|
||||
if (background_bitmap)
|
||||
gdrv::copy_bitmap(&vscreen, width, height, xPos, yPos, background_bitmap, xPos, yPos);
|
||||
else
|
||||
gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!maths::rectangle_clip(&curSprite->BmpRect, &vscreen_rect, &curSprite->DirtyRect))
|
||||
{
|
||||
curSprite->DirtyRect.Width = -1;
|
||||
continue;
|
||||
}
|
||||
if (!curSprite->Bmp)
|
||||
if (clearSprite)
|
||||
{
|
||||
auto yPos = curSprite->DirtyRect.YPosition;
|
||||
auto width = curSprite->DirtyRect.Width;
|
||||
|
@ -112,31 +100,28 @@ void render::update()
|
|||
gdrv::fill_bitmap(&vscreen, width, height, xPos, yPos, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirtyPtr = dirty_list;
|
||||
for (int index = 0; index < many_dirty; ++index)
|
||||
{
|
||||
auto sprite = *dirtyPtr;
|
||||
if ((*dirtyPtr)->DirtyRect.Width > 0 && (sprite->VisualType == VisualType::None || sprite->VisualType ==
|
||||
auto sprite = dirty_list[index];
|
||||
if (sprite->DirtyRect.Width > 0 && (sprite->VisualType == VisualType::None || sprite->VisualType ==
|
||||
VisualType::Sprite))
|
||||
repaint(*dirtyPtr);
|
||||
++dirtyPtr;
|
||||
repaint(sprite);
|
||||
}
|
||||
|
||||
paint_balls();
|
||||
if (blit)
|
||||
{
|
||||
paint_balls();
|
||||
gdrv::start_blit_sequence();
|
||||
|
||||
auto xPos = vscreen.XPosition + offset_x;
|
||||
auto yPos = vscreen.YPosition + offset_y;
|
||||
dirtyPtr = dirty_list;
|
||||
for (int index = 0; index < many_dirty; ++dirtyPtr, ++index)
|
||||
|
||||
for (int index = 0; index < many_dirty; ++index)
|
||||
{
|
||||
auto sprite = *dirtyPtr;
|
||||
auto dirtyRect = &(*dirtyPtr)->DirtyRect;
|
||||
auto width2 = (*dirtyPtr)->DirtyRect.Width;
|
||||
auto sprite = dirty_list[index];
|
||||
auto dirtyRect = &sprite->DirtyRect;
|
||||
auto width2 = sprite->DirtyRect.Width;
|
||||
if (width2 > 0)
|
||||
gdrv::blit_sequence(
|
||||
&vscreen,
|
||||
|
@ -147,21 +132,16 @@ void render::update()
|
|||
width2,
|
||||
dirtyRect->Height);
|
||||
|
||||
auto rect = &sprite->BmpRectCopy;
|
||||
rect->XPosition = dirtyRect->XPosition;
|
||||
rect->YPosition = dirtyRect->YPosition;
|
||||
rect->Width = dirtyRect->Width;
|
||||
rect->Height = dirtyRect->Height;
|
||||
|
||||
sprite->BmpRectCopy = *dirtyRect;
|
||||
if (sprite->UnknownFlag != 0)
|
||||
remove_sprite(sprite);
|
||||
}
|
||||
|
||||
dirtyPtr = ball_list;
|
||||
for (int index = 0; index < many_balls; ++dirtyPtr, ++index)
|
||||
for (int index = 0; index < many_balls; ++index)
|
||||
{
|
||||
auto rectCopy = &(*dirtyPtr)->BmpRectCopy;
|
||||
auto dirtyRect = &(*dirtyPtr)->DirtyRect;
|
||||
auto sprite = ball_list[index];
|
||||
auto rectCopy = &sprite->BmpRectCopy;
|
||||
auto dirtyRect = &sprite->DirtyRect;
|
||||
if (maths::overlapping_box(dirtyRect, rectCopy, &overlapRect) && dirtyRect->Width > 0)
|
||||
{
|
||||
if (overlapRect.Width > 0)
|
||||
|
@ -198,13 +178,22 @@ void render::update()
|
|||
}
|
||||
|
||||
gdrv::end_blit_sequence();
|
||||
unpaint_balls();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < many_dirty; ++index)
|
||||
{
|
||||
auto sprite = dirty_list[index];
|
||||
sprite->BmpRectCopy = sprite->DirtyRect;
|
||||
if (sprite->UnknownFlag != 0)
|
||||
remove_sprite(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
many_dirty = 0;
|
||||
unpaint_balls();
|
||||
}
|
||||
|
||||
|
||||
void render::paint()
|
||||
{
|
||||
paint_balls();
|
||||
|
|
|
@ -31,7 +31,6 @@ struct render_sprite_type_struct
|
|||
class render
|
||||
{
|
||||
public:
|
||||
static int blit;
|
||||
static int many_dirty, many_sprites, many_balls;
|
||||
static render_sprite_type_struct **dirty_list, **sprite_list, **ball_list;
|
||||
static zmap_header_type* background_zmap;
|
||||
|
@ -43,7 +42,7 @@ public:
|
|||
|
||||
static void init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height);
|
||||
static void uninit();
|
||||
static void update();
|
||||
static void update(bool blit);
|
||||
static void paint();
|
||||
static void sprite_modified(render_sprite_type_struct* sprite);
|
||||
static render_sprite_type_struct* create_sprite(VisualType visualType, gdrv_bitmap8* bmp,
|
||||
|
|
|
@ -240,6 +240,7 @@
|
|||
#define DLG_HIGHSCORES_EditName3 603
|
||||
#define DLG_HIGHSCORES_EditName4 604
|
||||
#define DLG_HIGHSCORES_EditName5 605
|
||||
#define Menu1_AlternativeRender 601
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
|
|
|
@ -220,7 +220,16 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
|
|||
if (prevTime)
|
||||
{
|
||||
char buf[60];
|
||||
sprintf_s(buf, "Frames/sec = %02.02f", 300.0f / (static_cast<float>(curTime - prevTime) * 0.001f));
|
||||
auto dt = static_cast<float>(curTime - prevTime) * 0.001f;
|
||||
if (!options::Options.AlternativeRender)
|
||||
sprintf_s(buf, "Frames/sec = %02.02f", 300.0f / dt);
|
||||
else
|
||||
{
|
||||
sprintf_s(buf, "Updates/sec = %02.02f Frames/sec = %02.02f",
|
||||
300.0f / dt, pb::frameCounter / dt);
|
||||
pb::frameCounter = 0;
|
||||
}
|
||||
|
||||
SetWindowTextA(hwnd_frame, buf);
|
||||
|
||||
if (DispGRhistory)
|
||||
|
@ -610,6 +619,7 @@ LRESULT CALLBACK winmain::message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LP
|
|||
case Menu1_800x600:
|
||||
case Menu1_1024x768:
|
||||
case Menu1_WindowUniformScale:
|
||||
case Menu1_AlternativeRender:
|
||||
options::toggle(wParamI);
|
||||
break;
|
||||
case Menu1_Help_Topics:
|
||||
|
|
Loading…
Reference in a new issue