Render tweaks part 1: refactored sprite struct.

Optimized sprite handling in render.
Fixed switch warning in control.
This commit is contained in:
Muzychenko Andrey 2022-09-21 16:43:04 +03:00
parent 1e43bdd5fa
commit 9f0ae0434e
23 changed files with 258 additions and 321 deletions

View File

@ -52,7 +52,7 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
auto zDepth = proj::z_distance(*visVec);
VisualZArray[index] = zDepth;
}
RenderSprite = render::create_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr);
RenderSprite = new render_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr);
PinballTable->CollisionCompOffset = Offset;
Position.Z = Offset;
}
@ -78,8 +78,7 @@ void TBall::Repaint()
}
auto bmp = ListBitmap->at(index);
render::ball_set(
RenderSprite,
RenderSprite->ball_set(
bmp,
zDepth,
pos2D.X - bmp->Width / 2,
@ -110,7 +109,7 @@ int TBall::Message(MessageCode code, float value)
{
if (code == MessageCode::Reset)
{
render::ball_set(RenderSprite, nullptr, 0.0, 0, 0);
RenderSprite->ball_set(nullptr, 0.0, 0, 0);
Position.X = 0.0;
CollisionComp = nullptr;
Position.Y = 0.0;
@ -145,5 +144,5 @@ vector2 TBall::get_coordinates()
void TBall::Disable()
{
ActiveFlag = false;
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
}

View File

@ -20,7 +20,7 @@ TBlocker::TBlocker(TPinballTable* table, int groupIndex) : TCollisionComponent(t
Timer = 0;
MessageField = 0;
ActiveFlag = 0;
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
}
int TBlocker::Message(MessageCode code, float value)
@ -38,14 +38,14 @@ int TBlocker::Message(MessageCode code, float value)
}
MessageField = 0;
ActiveFlag = 0;
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
if (code == MessageCode::TBlockerDisable)
loader::play_sound(SoundIndex3, this, "TBlocker1");
break;
case MessageCode::TBlockerEnable:
ActiveFlag = 1;
loader::play_sound(SoundIndex4, this, "TBlocker2");
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
RenderSprite->set_bitmap(ListBitmap->at(0));
if (Timer)
timer::kill(Timer);
Timer = timer::set(std::max(value, 0.0f), this, TimerExpired);

View File

@ -113,8 +113,7 @@ void TBumper::TimerExpired(int timerId, void* caller)
auto bmp = bump->ListBitmap->at(bump->BmpIndex * 2);
auto zMap = bump->ListZMap->at(bump->BmpIndex * 2);
bump->Timer = 0;
render::sprite_set(
bump->RenderSprite,
bump->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - bump->PinballTable->XOffset,
@ -127,8 +126,7 @@ void TBumper::Fire()
int bmpIndex = 2 * BmpIndex + 1;
auto bmp = ListBitmap->at(bmpIndex);
auto zMap = ListZMap->at(bmpIndex);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,

View File

@ -62,8 +62,7 @@ int TFlagSpinner::Message(MessageCode code, float value)
BmpIndex = 0;
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
@ -113,8 +112,7 @@ void TFlagSpinner::NextFrame()
auto bmp = ListBitmap->at(BmpIndex);
auto zMap = ListZMap->at(BmpIndex);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,

View File

@ -121,8 +121,7 @@ void TFlipper::UpdateSprite(float timeNow)
BmpIndex = newBmpIndex;
auto bmp = ListBitmap->at(BmpIndex);
auto zMap = ListZMap->at(BmpIndex);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,

View File

@ -14,7 +14,7 @@ TGate::TGate(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
SoundIndex4 = visual.SoundIndex4;
SoundIndex3 = visual.SoundIndex3;
ActiveFlag = 1;
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
RenderSprite->set_bitmap(ListBitmap->at(0));
control::handler(MessageCode::Reset, this);
}
@ -24,13 +24,13 @@ int TGate::Message(MessageCode code, float value)
{
case MessageCode::TGateDisable:
ActiveFlag = 0;
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
loader::play_sound(SoundIndex3, this, "TGate1");
break;
case MessageCode::Reset:
case MessageCode::TGateEnable:
ActiveFlag = 1;
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
RenderSprite->set_bitmap(ListBitmap->at(0));
if (code == MessageCode::TGateEnable)
loader::play_sound(SoundIndex4, this, "TGate2");
break;

View File

@ -25,7 +25,7 @@ int TKickback::Message(MessageCode code, float value)
{
timer::kill(Timer);
if (ListBitmap)
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
Timer = 0;
KickActiveFlag = 0;
Threshold = 1000000000.0;
@ -67,8 +67,7 @@ void TKickback::TimerExpired(int timerId, void* caller)
{
auto bmp = kick->ListBitmap->at(1);
auto zMap = kick->ListZMap->at(1);
render::sprite_set(
kick->RenderSprite,
kick->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - kick->PinballTable->XOffset,
@ -81,8 +80,7 @@ void TKickback::TimerExpired(int timerId, void* caller)
{
auto bmp = kick->ListBitmap->at(0);
auto zMap = kick->ListZMap->at(0);
render::sprite_set(
kick->RenderSprite,
kick->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - kick->PinballTable->XOffset,

View File

@ -225,7 +225,8 @@ int TLight::Message(MessageCode code, float value)
case MessageCode::TLightFtTmpOverrideOn:
case MessageCode::TLightFtTmpOverrideOff:
// FT codes in negative to avoid overlap with 3DPB TLightGroup codes
render::sprite_set_bitmap(RenderSprite, BmpArr[code == MessageCode::TLightFtTmpOverrideOn]);
if (ListBitmap)
RenderSprite->set_bitmap(BmpArr[code == MessageCode::TLightFtTmpOverrideOn]);
if (UndoOverrideTimer)
timer::kill(UndoOverrideTimer);
UndoOverrideTimer = 0;
@ -240,7 +241,8 @@ int TLight::Message(MessageCode code, float value)
timer::kill(UndoOverrideTimer);
UndoOverrideTimer = 0;
TemporaryOverrideFlag = false;
render::sprite_set_bitmap(RenderSprite, PreviousBitmap);
if (ListBitmap)
RenderSprite->set_bitmap(PreviousBitmap);
break;
default:
break;
@ -267,10 +269,12 @@ void TLight::Reset()
TemporaryOverrideFlag = false;
TurnOffAfterFlashingFg = false;
PreviousBitmap = nullptr;
render::sprite_set_bitmap(RenderSprite, nullptr);
BmpArr[0] = nullptr;
if (ListBitmap)
{
BmpArr[1] = ListBitmap->at(0);
RenderSprite->set_bitmap(nullptr);
}
MessageField = 0;
}
@ -326,8 +330,8 @@ void TLight::flasher_start(bool bmpIndex)
void TLight::SetSpriteBmp(gdrv_bitmap8* bmp)
{
PreviousBitmap = bmp;
if (!TemporaryOverrideFlag)
render::sprite_set_bitmap(RenderSprite, bmp);
if (!TemporaryOverrideFlag && RenderSprite)
RenderSprite->set_bitmap(bmp);
}
void TLight::flasher_callback(int timerId, void* caller)

View File

@ -14,7 +14,7 @@ TLightRollover::TLightRollover(TPinballTable* table, int groupIndex) : TRollover
RolloverFlag = 0;
Timer = 0;
if (ListBitmap != nullptr)
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
build_walls(groupIndex);
FloatArr = *loader::query_float_attribute(groupIndex, 0, 407);
}
@ -29,7 +29,7 @@ int TLightRollover::Message(MessageCode code, float value)
timer::kill(Timer);
Timer = 0;
if (ListBitmap)
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
}
return 0;
}
@ -57,7 +57,7 @@ void TLightRollover::Collision(TBall* ball, vector2* nextPosition, vector2* dire
control::handler(MessageCode::ControlCollision, this);
RolloverFlag = RolloverFlag == 0;
if (ListBitmap)
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
RenderSprite->set_bitmap(ListBitmap->at(0));
}
}
}
@ -65,6 +65,6 @@ void TLightRollover::Collision(TBall* ball, vector2* nextPosition, vector2* dire
void TLightRollover::delay_expired(int timerId, void* caller)
{
auto roll = static_cast<TLightRollover*>(caller);
render::sprite_set_bitmap(roll->RenderSprite, nullptr);
roll->RenderSprite->set_bitmap(nullptr);
roll->Timer = 0;
}

View File

@ -23,10 +23,12 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
Control = nullptr;
VisualPosNormX= -1.0f;
VisualPosNormY = -1.0f;
GroupIndex = groupIndex;
if (table)
table->ComponentList.push_back(this);
if (groupIndex >= 0)
GroupName = loader::query_name(groupIndex);
if (loadVisuals && groupIndex >= 0)
{
int visualCount = loader::query_visual_states(groupIndex);
@ -37,15 +39,13 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
{
if (!ListBitmap)
ListBitmap = new std::vector<gdrv_bitmap8*>();
if (ListBitmap)
ListBitmap->push_back(visual.Bitmap);
ListBitmap->push_back(visual.Bitmap);
}
if (visual.ZMap)
{
if (!ListZMap)
ListZMap = new std::vector<zmap_header_type*>();
if (ListZMap)
ListZMap->push_back(visual.ZMap);
ListZMap->push_back(visual.ZMap);
}
}
@ -68,8 +68,8 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
maths::enclosing_box(bmp1Rect, tmpRect, bmp1Rect);
}
RenderSprite = render::create_sprite(
visualCount > 0 ? VisualTypes::Sprite : VisualTypes::None,
RenderSprite = new render_sprite(
VisualTypes::Sprite,
rootBmp,
zMap,
rootBmp->XPosition - table->XOffset,
@ -85,7 +85,6 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
VisualPosNormY = posNorm.Y;
}
}
GroupIndex = groupIndex;
}

View File

@ -2,7 +2,7 @@
struct zmap_header_type;
struct gdrv_bitmap8;
struct render_sprite_type_struct;
struct render_sprite;
struct component_control;
struct vector2;
class TPinballTable;
@ -145,7 +145,7 @@ public:
char* GroupName;
component_control* Control;
int GroupIndex;
render_sprite_type_struct* RenderSprite;
render_sprite* RenderSprite;
TPinballTable* PinballTable;
std::vector<gdrv_bitmap8*>* ListBitmap;
std::vector<zmap_header_type*>* ListZMap;

View File

@ -133,8 +133,7 @@ int TPlunger::Message(MessageCode code, float value)
loader::play_sound(SoundIndexP2, this, "TPlunger3");
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
@ -156,8 +155,7 @@ int TPlunger::Message(MessageCode code, float value)
auto bmp = ListBitmap->at(0);
auto zMap = ListZMap->at(0);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,
@ -204,8 +202,7 @@ void TPlunger::PullbackTimer(int timerId, void* caller)
(plunger->Boost / plunger->MaxPullback)));
auto bmp = plunger->ListBitmap->at(index);
auto zMap = plunger->ListZMap->at(index);
render::sprite_set(
plunger->RenderSprite,
plunger->RenderSprite->set(
bmp,
zMap,
bmp->XPosition - plunger->PinballTable->XOffset,

View File

@ -20,7 +20,7 @@ int TPopupTarget::Message(MessageCode code, float value)
{
case MessageCode::TPopupTargetDisable:
ActiveFlag = 0;
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
break;
case MessageCode::TPopupTargetEnable:
Timer = timer::set(TimerTime, this, TimerExpired);
@ -79,7 +79,7 @@ void TPopupTarget::TimerExpired(int timerId, void* caller)
auto target = static_cast<TPopupTarget*>(caller);
target->Timer = 0;
target->ActiveFlag = 1;
render::sprite_set_bitmap(target->RenderSprite, target->ListBitmap->at(0));
target->RenderSprite->set_bitmap(target->ListBitmap->at(0));
if (timerId)
{
if (target->SoftHitSoundId)

View File

@ -20,7 +20,7 @@ TRollover::TRollover(TPinballTable* table, int groupIndex, bool createWall) : TC
TRollover::TRollover(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false)
{
if (ListBitmap)
render::sprite_set_bitmap(RenderSprite, ListBitmap->at(0));
RenderSprite->set_bitmap(ListBitmap->at(0));
build_walls(groupIndex);
}
@ -29,10 +29,10 @@ int TRollover::Message(MessageCode code, float value)
{
if (code == MessageCode::Reset)
{
this->ActiveFlag = 1;
this->RolloverFlag = 0;
if (this->ListBitmap)
render::sprite_set_bitmap(this->RenderSprite, this->ListBitmap->at(0));
ActiveFlag = 1;
RolloverFlag = 0;
if (ListBitmap)
RenderSprite->set_bitmap(ListBitmap->at(0));
}
return 0;
}
@ -62,7 +62,7 @@ void TRollover::Collision(TBall* ball, vector2* nextPosition, vector2* direction
{
if (!RolloverFlag)
bmp = ListBitmap->at(0);
render::sprite_set_bitmap(RenderSprite, bmp);
RenderSprite->set_bitmap(bmp);
}
}
}

View File

@ -42,8 +42,7 @@ int TSoloTarget::Message(MessageCode code, float value)
auto index = 1 - ActiveFlag;
auto bmp = ListBitmap->at(index);
auto zMap = ListZMap->at(index);
render::sprite_set(
RenderSprite,
RenderSprite->set(
bmp,
zMap,
bmp->XPosition - PinballTable->XOffset,

View File

@ -32,7 +32,7 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f
rect.YPosition = 0;
rect.Width = bmp->Width;
rect.Height = bmp->Height;
render::create_sprite(VisualTypes::None, bmp, visual.ZMap, 0, 0, &rect);
new render_sprite(VisualTypes::Background, bmp, visual.ZMap, 0, 0, &rect);
PinballTable->SoundIndex1 = visual.SoundIndex4;
PinballTable->SoundIndex2 = visual.SoundIndex3;

View File

@ -9,7 +9,7 @@
TWall::TWall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, true)
{
if (RenderSprite)
render::sprite_set_bitmap(RenderSprite, nullptr);
RenderSprite->set_bitmap(nullptr);
if (ListBitmap)
BmpPtr = ListBitmap->at(0);
}
@ -30,7 +30,7 @@ void TWall::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
{
if (BmpPtr)
{
render::sprite_set_bitmap(RenderSprite, BmpPtr);
RenderSprite->set_bitmap(BmpPtr);
Timer = timer::set(0.1f, this, TimerExpired);
}
control::handler(MessageCode::ControlCollision, this);
@ -40,7 +40,7 @@ void TWall::Collision(TBall* ball, vector2* nextPosition, vector2* direction, fl
void TWall::TimerExpired(int timerId, void* caller)
{
auto wall = static_cast<TWall*>(caller);
render::sprite_set_bitmap(wall->RenderSprite, nullptr);
wall->RenderSprite->set_bitmap(nullptr);
wall->Timer = 0;
wall->MessageField = 0;
}

View File

@ -2081,6 +2081,7 @@ void control::GravityWellKickoutControl(MessageCode code, TPinballComponent* cal
case MessageCode::Reset:
kickout1->ActiveFlag = 0;
break;
default: break;
}
}

View File

@ -42,7 +42,7 @@ TTextBox *pb::InfoTextBox, *pb::MissTextBox;
int pb::init()
{
float projMat[12], zMin = 0, zScaler = 0;
float projMat[12];
if (DatFileName.empty())
return 1;
@ -75,12 +75,12 @@ int pb::init()
auto projCenterX = resInfo->TableWidth * 0.5f;
auto projCenterY = resInfo->TableHeight * 0.5f;
auto projD = cameraInfo[0];
proj::init(projMat, projD, projCenterX, projCenterY);
zMin = cameraInfo[1];
zScaler = cameraInfo[2];
auto zMin = cameraInfo[1];
auto zScaler = cameraInfo[2];
proj::init(projMat, projD, projCenterX, projCenterY, zMin, zScaler);
}
render::init(nullptr, zMin, zScaler, resInfo->TableWidth, resInfo->TableHeight);
render::init(nullptr, resInfo->TableWidth, resInfo->TableHeight);
gdrv::copy_bitmap(
render::vscreen,
backgroundBmp->Width,

View File

@ -3,17 +3,10 @@
mat4_row_major proj::matrix;
float proj::d_, proj::centerx, proj::centery;
float proj::zscaler, proj::zmin, proj::zmax;
void proj::init(float* mat4x3, float d, float centerX, float centerY)
void proj::init(float* mat4x3, float d, float centerX, float centerY, float zMin, float zScaler)
{
/*for (auto colIndex = 0; colIndex < 4; ++colIndex)
{
// Todo: out of bounds read from mat4x3?
for (int rowIndex = colIndex, i = 4; i > 0; rowIndex += 4, --i)
{
((float*)&matrix)[rowIndex] = mat4x3[rowIndex];
}
}*/
memcpy(&matrix, mat4x3, sizeof(float) * 4 * 3);
matrix.Row3.X = 0.0;
@ -24,6 +17,10 @@ void proj::init(float* mat4x3, float d, float centerX, float centerY)
d_ = d;
centerx = centerX;
centery = centerY;
zscaler = zScaler;
zmin = zMin;
zmax = static_cast<float>(0xffFFffFF) / zScaler + zMin;
}
vector3 proj::matrix_vector_multiply(const mat4_row_major& mat, const vector3& vec)
@ -103,3 +100,17 @@ void proj::recenter(float centerX, float centerY)
centerx = centerX;
centery = centerY;
}
uint16_t proj::NormalizeDepth(float depth)
{
uint16_t result = 0;
if (depth >= zmin)
{
auto depthScaled = (depth - zmin) * zscaler;
if (depthScaled <= zmax)
result = static_cast<uint16_t>(depthScaled);
else
result = 0xffFF;
}
return result;
}

View File

@ -21,14 +21,16 @@ struct mat4_row_major
class proj
{
public:
static void init(float* mat4x3, float d, float centerX, float centerY);
static void init(float* mat4x3, float d, float centerX, float centerY, float zMin, float zScaler);
static vector3 matrix_vector_multiply(const mat4_row_major& mat, const vector3& vec);
static float z_distance(const vector3& vec);
static vector2i xform_to_2d(const vector3& vec);
static vector2i xform_to_2d(const vector2& vec);
static vector3 ReverseXForm(const vector2i& vec);
static void recenter(float centerX, float centerY);
static uint16_t NormalizeDepth(float depth);
private:
static mat4_row_major matrix;
static float d_, centerx, centery;
static float zscaler, zmin, zmax;
};

View File

@ -9,21 +9,95 @@
#include "TPinballTable.h"
#include "winmain.h"
#include "DebugOverlay.h"
#include "proj.h"
std::vector<render_sprite_type_struct*> render::dirty_list, render::sprite_list, render::ball_list;
std::vector<render_sprite*> render::sprite_list, render::ball_list;
zmap_header_type* render::background_zmap;
int render::zmap_offset, render::zmap_offsetY, render::offset_x, render::offset_y;
float render::zscaler, render::zmin, render::zmax;
int render::zmap_offsetX, render::zmap_offsetY, render::offset_x, render::offset_y;
rectangle_type render::vscreen_rect;
gdrv_bitmap8 *render::vscreen, *render::background_bitmap, *render::ball_bitmap[20];
zmap_header_type* render::zscreen;
SDL_Rect render::DestinationRect{};
void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height)
render_sprite::render_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap,
int xPosition, int yPosition, rectangle_type* boundingRect)
{
Bmp = bmp;
ZMap = zMap;
VisualType = visualType;
DeleteFlag = false;
OccludedSprites = nullptr;
DirtyRect = rectangle_type{};
DirtyFlag = visualType != VisualTypes::Ball;
ZMapOffestX = 0;
ZMapOffestY = 0;
Depth = 0xffFF;
if (boundingRect)
{
BoundingRect = *boundingRect;
}
else
{
BoundingRect.Width = -1;
BoundingRect.Height = -1;
BoundingRect.XPosition = 0;
BoundingRect.YPosition = 0;
}
BmpRect.YPosition = yPosition;
BmpRect.XPosition = xPosition;
if (bmp)
{
BmpRect.Width = bmp->Width;
BmpRect.Height = bmp->Height;
}
else
{
BmpRect.Width = 0;
BmpRect.Height = 0;
}
DirtyRectPrev = BmpRect;
render::AddSprite(*this);
}
render_sprite::~render_sprite()
{
render::RemoveSprite(*this);
delete OccludedSprites;
}
void render_sprite::set(gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos, int yPos)
{
if (Bmp == bmp && ZMap == zMap && BmpRect.XPosition == xPos && BmpRect.YPosition == yPos)
return;
Bmp = bmp;
ZMap = zMap;
DirtyFlag = VisualType != VisualTypes::Ball;
BmpRect.XPosition = xPos;
BmpRect.YPosition = yPos;
if (bmp)
{
BmpRect.Width = bmp->Width;
BmpRect.Height = bmp->Height;
}
}
void render_sprite::set_bitmap(gdrv_bitmap8* bmp)
{
set(bmp, ZMap, BmpRect.XPosition, BmpRect.YPosition);
}
void render_sprite::ball_set(gdrv_bitmap8* bmp, float depth, int xPos, int yPos)
{
set(bmp, ZMap,xPos, yPos);
Depth = proj::NormalizeDepth(depth);
}
void render::init(gdrv_bitmap8* bmp, int width, int height)
{
zscaler = zScaler;
zmin = zMin;
zmax = 4294967300.0f / zScaler + zMin;
vscreen = new gdrv_bitmap8(width, height, false);
zscreen = new zmap_header_type(width, height, width);
zdrv::fill(zscreen, zscreen->Width, zscreen->Height, 0, 0, 0xFFFF);
@ -49,15 +123,12 @@ void render::uninit()
{
delete vscreen;
delete zscreen;
for (auto sprite : sprite_list)
remove_sprite(sprite, false);
for (auto ball : ball_list)
remove_ball(ball, false);
for (auto& ballBmp : ball_bitmap)
delete ballBmp;
ball_list.clear();
dirty_list.clear();
sprite_list.clear();
// Sprite destructor removes it from the list.
while (!sprite_list.empty())
delete sprite_list[0];
while (!ball_list.empty())
delete ball_list[0];
DebugOverlay::UnInit();
}
@ -70,36 +141,39 @@ void render::update()
{
unpaint_balls();
// Clip dirty sprites with vScreen, clear clipping (dirty) rectangles
for (auto curSprite : dirty_list)
// Clip dirty sprites with vScreen, clear clipping (dirty) rectangles
for (const auto sprite : sprite_list)
{
if (!sprite->DirtyFlag)
continue;
bool clearSprite = false;
switch (curSprite->VisualType)
switch (sprite->VisualType)
{
case VisualTypes::Sprite:
if (curSprite->DirtyRectPrev.Width > 0)
maths::enclosing_box(curSprite->DirtyRectPrev, curSprite->BmpRect, curSprite->DirtyRect);
if (sprite->DirtyRectPrev.Width > 0)
maths::enclosing_box(sprite->DirtyRectPrev, sprite->BmpRect, sprite->DirtyRect);
if (maths::rectangle_clip(curSprite->DirtyRect, vscreen_rect, &curSprite->DirtyRect))
if (maths::rectangle_clip(sprite->DirtyRect, vscreen_rect, &sprite->DirtyRect))
clearSprite = true;
else
curSprite->DirtyRect.Width = -1;
sprite->DirtyRect.Width = -1;
break;
case VisualTypes::None:
if (maths::rectangle_clip(curSprite->BmpRect, vscreen_rect, &curSprite->DirtyRect))
clearSprite = !curSprite->Bmp;
case VisualTypes::Background:
if (maths::rectangle_clip(sprite->BmpRect, vscreen_rect, &sprite->DirtyRect))
clearSprite = !sprite->Bmp;
else
curSprite->DirtyRect.Width = -1;
sprite->DirtyRect.Width = -1;
break;
default: break;
}
if (clearSprite)
{
auto yPos = curSprite->DirtyRect.YPosition;
auto width = curSprite->DirtyRect.Width;
auto xPos = curSprite->DirtyRect.XPosition;
auto height = curSprite->DirtyRect.Height;
auto yPos = sprite->DirtyRect.YPosition;
auto width = sprite->DirtyRect.Width;
auto xPos = sprite->DirtyRect.XPosition;
auto height = sprite->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);
@ -109,191 +183,60 @@ void render::update()
}
// Paint dirty rectangles of dirty sprites
for (auto sprite : dirty_list)
for (auto sprite : sprite_list)
{
if (sprite->DirtyRect.Width > 0 && (sprite->VisualType == VisualTypes::None || sprite->VisualType ==
VisualTypes::Sprite))
repaint(sprite);
if (!sprite->DirtyFlag)
continue;
repaint(*sprite);
sprite->DirtyFlag = false;
sprite->DirtyRectPrev = sprite->DirtyRect;
if (sprite->DeleteFlag)
delete sprite;
}
paint_balls();
// In the original, this used to blit dirty sprites and balls
for (auto sprite : dirty_list)
{
sprite->DirtyRectPrev = sprite->DirtyRect;
if (sprite->UnknownFlag != 0)
remove_sprite(sprite, true);
}
dirty_list.clear();
}
void render::sprite_modified(render_sprite_type_struct* sprite)
void render::AddSprite(render_sprite& sprite)
{
if (sprite->VisualType != VisualTypes::Ball && dirty_list.size() < 999)
dirty_list.push_back(sprite);
if (!sprite.ZMap && sprite.VisualType != VisualTypes::Ball)
{
sprite.ZMap = background_zmap;
sprite.ZMapOffestY = sprite.BmpRect.XPosition - zmap_offsetX;
sprite.ZMapOffestX = sprite.BmpRect.YPosition - zmap_offsetY;
}
auto& list = sprite.VisualType == VisualTypes::Ball ? ball_list : sprite_list;
list.push_back(&sprite);
}
render_sprite_type_struct* render::create_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap,
int xPosition, int yPosition, rectangle_type* rect)
void render::RemoveSprite(render_sprite& sprite)
{
auto sprite = new render_sprite_type_struct();
if (!sprite)
return nullptr;
sprite->BmpRect.YPosition = yPosition;
sprite->BmpRect.XPosition = xPosition;
sprite->Bmp = bmp;
sprite->VisualType = visualType;
sprite->UnknownFlag = 0;
sprite->SpriteArray = nullptr;
sprite->DirtyRect = rectangle_type{};
if (rect)
{
sprite->BoundingRect = *rect;
}
else
{
sprite->BoundingRect.Width = -1;
sprite->BoundingRect.Height = -1;
sprite->BoundingRect.XPosition = 0;
sprite->BoundingRect.YPosition = 0;
}
if (bmp)
{
sprite->BmpRect.Width = bmp->Width;
sprite->BmpRect.Height = bmp->Height;
}
else
{
sprite->BmpRect.Width = 0;
sprite->BmpRect.Height = 0;
}
sprite->ZMap = zMap;
sprite->ZMapOffestX = 0;
sprite->ZMapOffestY = 0;
if (!zMap && visualType != VisualTypes::Ball)
{
sprite->ZMap = background_zmap;
sprite->ZMapOffestY = xPosition - zmap_offset;
sprite->ZMapOffestX = yPosition - zmap_offsetY;
}
sprite->DirtyRectPrev = sprite->BmpRect;
if (visualType == VisualTypes::Ball)
{
ball_list.push_back(sprite);
}
else
{
sprite_list.push_back(sprite);
sprite_modified(sprite);
}
return sprite;
auto& list = sprite.VisualType == VisualTypes::Ball ? ball_list : sprite_list;
auto it = std::find(list.begin(), list.end(), &sprite);
if (it != list.end())
list.erase(it);
}
void render::remove_sprite(render_sprite_type_struct* sprite, bool removeFromList)
{
if (removeFromList)
{
auto it = std::find(sprite_list.begin(), sprite_list.end(), sprite);
if (it != sprite_list.end())
sprite_list.erase(it);
}
delete sprite->SpriteArray;
delete sprite;
}
void render::remove_ball(render_sprite_type_struct* ball, bool removeFromList)
{
if (removeFromList)
{
auto it = std::find(ball_list.begin(), ball_list.end(), ball);
if (it != ball_list.end())
ball_list.erase(it);
}
delete ball->SpriteArray;
delete ball;
}
void render::sprite_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos,
int yPos)
{
if (sprite)
{
sprite->BmpRect.XPosition = xPos;
sprite->BmpRect.YPosition = yPos;
sprite->Bmp = bmp;
if (bmp)
{
sprite->BmpRect.Width = bmp->Width;
sprite->BmpRect.Height = bmp->Height;
}
sprite->ZMap = zMap;
sprite_modified(sprite);
}
}
void render::sprite_set_bitmap(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp)
{
if (sprite && sprite->Bmp != bmp)
{
sprite->Bmp = bmp;
if (bmp)
{
sprite->BmpRect.Width = bmp->Width;
sprite->BmpRect.Height = bmp->Height;
}
sprite_modified(sprite);
}
}
void render::set_background_zmap(struct zmap_header_type* zMap, int offsetX, int offsetY)
void render::set_background_zmap(zmap_header_type* zMap, int offsetX, int offsetY)
{
background_zmap = zMap;
zmap_offset = offsetX;
zmap_offsetX = offsetX;
zmap_offsetY = offsetY;
}
void render::ball_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, float depth, int xPos, int yPos)
{
if (sprite)
{
sprite->Bmp = bmp;
if (bmp)
{
sprite->BmpRect.XPosition = xPos;
sprite->BmpRect.YPosition = yPos;
sprite->BmpRect.Width = bmp->Width;
sprite->BmpRect.Height = bmp->Height;
}
if (depth >= zmin)
{
float depth2 = (depth - zmin) * zscaler;
if (depth2 <= zmax)
sprite->Depth = static_cast<short>(depth2);
else
sprite->Depth = -1;
}
else
{
sprite->Depth = 0;
}
}
}
void render::repaint(struct render_sprite_type_struct* sprite)
void render::repaint(const render_sprite& sprite)
{
rectangle_type clipRect{};
if (!sprite->SpriteArray)
if (!sprite.OccludedSprites || sprite.VisualType == VisualTypes::Ball || sprite.DirtyRect.Width <= 0)
return;
for (auto refSprite : *sprite->SpriteArray)
for (auto refSprite : *sprite.OccludedSprites)
{
if (!refSprite->UnknownFlag && refSprite->Bmp)
if (!refSprite->DeleteFlag && refSprite->Bmp)
{
if (maths::rectangle_clip(refSprite->BmpRect, sprite->DirtyRect, &clipRect))
if (maths::rectangle_clip(refSprite->BmpRect, sprite.DirtyRect, &clipRect))
zdrv::paint(
clipRect.Width,
clipRect.Height,
@ -316,20 +259,11 @@ void render::repaint(struct render_sprite_type_struct* sprite)
void render::paint_balls()
{
// Sort ball sprites by depth
for (auto i = 0u; i < ball_list.size(); i++)
// Sort ball sprites by ascending depth
std::sort(ball_list.begin(), ball_list.end(), [](const render_sprite* lhs, const render_sprite* rhs)
{
for (auto j = i; j < ball_list.size() / 2; ++j)
{
auto ballA = ball_list[j];
auto ballB = ball_list[i];
if (ballB->Depth > ballA->Depth)
{
ball_list[i] = ballA;
ball_list[j] = ballB;
}
}
}
return lhs->Depth < rhs->Depth;
});
// For balls that clip vScreen: save original vScreen contents and paint ball bitmap.
for (auto index = 0u; index < ball_list.size(); ++index)
@ -391,26 +325,25 @@ void render::shift(int offsetX, int offsetY)
void render::build_occlude_list()
{
std::vector<render_sprite_type_struct*>* spriteArr = nullptr;
std::vector<render_sprite*>* spriteArr = nullptr;
for (auto mainSprite : sprite_list)
{
if (mainSprite->SpriteArray)
if (mainSprite->OccludedSprites)
{
delete mainSprite->SpriteArray;
mainSprite->SpriteArray = nullptr;
delete mainSprite->OccludedSprites;
mainSprite->OccludedSprites = nullptr;
}
if (!mainSprite->UnknownFlag && mainSprite->BoundingRect.Width != -1)
if (!mainSprite->DeleteFlag && mainSprite->BoundingRect.Width != -1)
{
if (!spriteArr)
spriteArr = new std::vector<render_sprite_type_struct*>();
spriteArr = new std::vector<render_sprite*>();
for (auto refSprite : sprite_list)
{
if (!refSprite->UnknownFlag
if (!refSprite->DeleteFlag
&& refSprite->BoundingRect.Width != -1
&& maths::rectangle_clip(mainSprite->BoundingRect, refSprite->BoundingRect, nullptr)
&& spriteArr)
&& maths::rectangle_clip(mainSprite->BoundingRect, refSprite->BoundingRect, nullptr))
{
spriteArr->push_back(refSprite);
}
@ -420,7 +353,7 @@ void render::build_occlude_list()
spriteArr->clear();
if (!spriteArr->empty())
{
mainSprite->SpriteArray = spriteArr;
mainSprite->OccludedSprites = spriteArr;
spriteArr = nullptr;
}
}

View File

@ -5,25 +5,33 @@
enum class VisualTypes : char
{
None = 0,
Background = 0,
Sprite = 1,
Ball = 2
};
struct render_sprite_type_struct
struct render_sprite
{
rectangle_type BmpRect;
rectangle_type BmpRect{};
gdrv_bitmap8* Bmp;
zmap_header_type* ZMap;
char UnknownFlag;
bool DeleteFlag;
VisualTypes VisualType;
int16_t Depth;
rectangle_type DirtyRectPrev;
uint16_t Depth;
rectangle_type DirtyRectPrev{};
int ZMapOffestY;
int ZMapOffestX;
rectangle_type DirtyRect;
std::vector<render_sprite_type_struct*>* SpriteArray;
rectangle_type BoundingRect;
rectangle_type DirtyRect{};
std::vector<render_sprite*>* OccludedSprites;
rectangle_type BoundingRect{};
bool DirtyFlag{};
render_sprite(VisualTypes visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap,
int xPosition, int yPosition, rectangle_type* boundingRect);
~render_sprite();
void set(gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos, int yPos);
void set_bitmap(gdrv_bitmap8* bmp);
void ball_set(gdrv_bitmap8* bmp, float depth, int xPos, int yPos);
};
@ -33,35 +41,26 @@ public:
static gdrv_bitmap8 *vscreen, *background_bitmap;
static SDL_Rect DestinationRect;
static void init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height);
static void init(gdrv_bitmap8* bmp, int width, int height);
static void uninit();
static void recreate_screen_texture();
static void update();
static void sprite_modified(render_sprite_type_struct* sprite);
static render_sprite_type_struct* create_sprite(VisualTypes visualType, gdrv_bitmap8* bmp,
zmap_header_type* zMap,
int xPosition, int yPosition, rectangle_type* rect);
static void remove_sprite(render_sprite_type_struct* sprite, bool removeFromList);
static void remove_ball(render_sprite_type_struct* ball, bool removeFromList);
static void sprite_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, zmap_header_type* zMap, int xPos,
int yPos);
static void sprite_set_bitmap(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp);
static void set_background_zmap(struct zmap_header_type* zMap, int offsetX, int offsetY);
static void ball_set(render_sprite_type_struct* sprite, gdrv_bitmap8* bmp, float depth, int xPos, int yPos);
static void AddSprite(render_sprite& sprite);
static void RemoveSprite(render_sprite& sprite);
static void set_background_zmap(zmap_header_type* zMap, int offsetX, int offsetY);
static void shift(int offsetX, int offsetY);
static void build_occlude_list();
static void SpriteViewer(bool* show);
static void PresentVScreen();
private:
static std::vector<render_sprite_type_struct*> dirty_list, sprite_list, ball_list;
static std::vector<render_sprite*> sprite_list, ball_list;
static zmap_header_type* background_zmap;
static int zmap_offset, zmap_offsetY, offset_x, offset_y;
static float zscaler, zmin, zmax;
static int zmap_offsetX, zmap_offsetY, offset_x, offset_y;
static rectangle_type vscreen_rect;
static gdrv_bitmap8 *ball_bitmap[20];
static zmap_header_type* zscreen;
static void repaint(struct render_sprite_type_struct* sprite);
static void repaint(const render_sprite& sprite);
static void paint_balls();
static void unpaint_balls();
};