From a4c64cc4c8878148b98f7ed243e14be299018c54 Mon Sep 17 00:00:00 2001 From: oz Date: Sun, 15 Nov 2020 17:39:00 +0300 Subject: [PATCH] render v2, ready --- Doc/FuncStats.xlsx | Bin 31496 -> 31540 bytes SpaceCadetPinball/SpaceCadetPinball.cpp | 2 +- SpaceCadetPinball/TPinballComponent.cpp | 2 +- SpaceCadetPinball/TPinballTable.cpp | 6 +- SpaceCadetPinball/maths.cpp | 142 +++++-- SpaceCadetPinball/maths.h | 8 +- SpaceCadetPinball/pinball.cpp | 1 - SpaceCadetPinball/pinball.h | 1 - SpaceCadetPinball/render.cpp | 513 ++++++++++++++++++++++-- SpaceCadetPinball/render.h | 51 ++- SpaceCadetPinball/score.cpp | 4 +- SpaceCadetPinball/score.h | 5 +- SpaceCadetPinball/zdrv.cpp | 10 +- SpaceCadetPinball/zdrv.h | 2 +- 14 files changed, 630 insertions(+), 117 deletions(-) diff --git a/Doc/FuncStats.xlsx b/Doc/FuncStats.xlsx index ea9f0bddaffbc65a351780da72ea5c7ea22f1e59..2337dfbe43bfa814b44c8e353af1eec7c713c013 100644 GIT binary patch delta 22840 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 uhAB?aXFq4ku1qzv{fplJmq-sy| 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 nzLuyFti|Y?7!<6Ngy#MqRWt|z diff --git a/SpaceCadetPinball/SpaceCadetPinball.cpp b/SpaceCadetPinball/SpaceCadetPinball.cpp index e3107f1..a31a780 100644 --- a/SpaceCadetPinball/SpaceCadetPinball.cpp +++ b/SpaceCadetPinball/SpaceCadetPinball.cpp @@ -61,7 +61,7 @@ int main() visualKickerStruct kicker1{}; loader::kicker(509, &kicker1); - auto score1 = score::create("score1", 117); + auto score1 = score::create("score1", nullptr); auto pinballTable = new TPinballTable(); //pinballTable->find_component(1); diff --git a/SpaceCadetPinball/TPinballComponent.cpp b/SpaceCadetPinball/TPinballComponent.cpp index cc777b2..26447d9 100644 --- a/SpaceCadetPinball/TPinballComponent.cpp +++ b/SpaceCadetPinball/TPinballComponent.cpp @@ -47,7 +47,7 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool zMap = static_cast(ListZMap->Get(0)); if (ListBitmap) { - visual_rect bmp1Rect{}, tmpRect{}; + rectangle_type bmp1Rect{}, tmpRect{}; auto rootBmp = static_cast(ListBitmap->Get(0)); bmp1Rect.XPosition = rootBmp->XPosition - table->XOffset; bmp1Rect.YPosition = rootBmp->YPosition - table->YOffset; diff --git a/SpaceCadetPinball/TPinballTable.cpp b/SpaceCadetPinball/TPinballTable.cpp index 733844b..8a87c05 100644 --- a/SpaceCadetPinball/TPinballTable.cpp +++ b/SpaceCadetPinball/TPinballTable.cpp @@ -58,7 +58,7 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false) TLightGroup* lightGroupObj = new TLightGroup(this, 0); this->LightGroup = lightGroupObj; - auto score1 = score::create("score1", pinball::render_background_bitmap); + auto score1 = score::create("score1", render::background_bitmap); this->Score1 = score1; this->Score2 = score1; int scoreIndex = 1; @@ -71,8 +71,8 @@ TPinballTable::TPinballTable(): TPinballComponent(nullptr, -1, false) while (scoreIndex < 4); this->UnknownP45 = 0; this->UnknownP73 = 3; - this->ScoreBallcount = (int*)score::create("ballcount1", pinball::render_background_bitmap); - this->ScorePlayerNumber1 = (int*)score::create("player_number1", pinball::render_background_bitmap); + this->ScoreBallcount = (int*)score::create("ballcount1", render::background_bitmap); + this->ScorePlayerNumber1 = (int*)score::create("player_number1", render::background_bitmap); int groupIndexObjects = loader::query_handle("table_objects"); short* shortArr = loader::query_iattribute(groupIndexObjects, 1025, &shortArrLength); diff --git a/SpaceCadetPinball/maths.cpp b/SpaceCadetPinball/maths.cpp index 7171477..8541f1b 100644 --- a/SpaceCadetPinball/maths.cpp +++ b/SpaceCadetPinball/maths.cpp @@ -2,35 +2,117 @@ #include "maths.h" -void maths::enclosing_box(visual_rect* rect1, visual_rect* rect2, visual_rect* dstRect) +void maths::enclosing_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect) { int xPos1 = rect1->XPosition; - int yPos1 = rect1->YPosition; - int width1 = rect1->Width; - int height1 = rect1->Height; - int xPos2 = rect2->XPosition; - bool rect2XPosLessRect1 = rect2->XPosition < rect1->XPosition; - int yPos2 = rect2->YPosition; - int width2 = rect2->Width; - int height2 = rect2->Height; - int xPos2_2 = rect2->XPosition; - if (rect2XPosLessRect1) - { - width1 += xPos1 - xPos2; - xPos1 = xPos2; - } - if (yPos2 < yPos1) - { - height1 += yPos1 - yPos2; - yPos1 = yPos2; - } - if (width2 + xPos2 > xPos1 + width1) - width1 = xPos2_2 + width2 - xPos1; - int height1_2 = height1; - if (height2 + yPos2 > height1 + yPos1) - height1_2 = yPos2 + height2 - yPos1; - dstRect->YPosition = yPos1; - dstRect->Height = height1_2; - dstRect->XPosition = xPos1; - dstRect->Width = width1; -} \ No newline at end of file + int yPos1 = rect1->YPosition; + int width1 = rect1->Width; + int height1 = rect1->Height; + int xPos2 = rect2->XPosition; + bool rect2XPosLessRect1 = rect2->XPosition < rect1->XPosition; + int yPos2 = rect2->YPosition; + int width2 = rect2->Width; + int height2 = rect2->Height; + int xPos2_2 = rect2->XPosition; + if (rect2XPosLessRect1) + { + width1 += xPos1 - xPos2; + xPos1 = xPos2; + } + if (yPos2 < yPos1) + { + height1 += yPos1 - yPos2; + yPos1 = yPos2; + } + if (width2 + xPos2 > xPos1 + width1) + width1 = xPos2_2 + width2 - xPos1; + int height1_2 = height1; + if (height2 + yPos2 > height1 + yPos1) + height1_2 = yPos2 + height2 - yPos1; + dstRect->YPosition = yPos1; + dstRect->Height = height1_2; + dstRect->XPosition = xPos1; + dstRect->Width = width1; +} + + +int maths::rectangle_clip(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect) +{ + int xPos1 = rect1->XPosition; + int yPos1 = rect1->YPosition; + int height1 = rect1->Height; + int xRight2 = rect2->XPosition + rect2->Width; + int width1 = rect1->Width; + int yRight2 = rect2->YPosition + rect2->Height; + if (xPos1 + width1 < rect2->XPosition) + return 0; + if (xPos1 >= xRight2) + return 0; + int yPos2 = yPos1; + if (yPos1 + height1 < rect2->YPosition || yPos1 >= yRight2) + return 0; + if (xPos1 < rect2->XPosition) + { + width1 += xPos1 - rect2->XPosition; + xPos1 = rect2->XPosition; + } + if (xPos1 + width1 > xRight2) + width1 = xRight2 - xPos1; + int height2 = height1; + if (yPos1 < rect2->YPosition) + { + height2 = yPos1 - rect2->YPosition + height1; + yPos2 = rect2->YPosition; + } + if (height2 + yPos2 > yRight2) + height2 = yRight2 - yPos2; + if (!width1 || !height2) + return 0; + if (dstRect) + { + dstRect->XPosition = xPos1; + dstRect->YPosition = yPos2; + dstRect->Width = width1; + dstRect->Height = height2; + } + return 1; +} + + +int maths::overlapping_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect) +{ + int v3; // esi + int v4; // edi + int v5; // esi + int v6; // esi + int v7; // edi + + if (rect1->XPosition >= rect2->XPosition) + { + dstRect->XPosition = rect2->XPosition; + v3 = rect1->Width - rect2->XPosition; + v4 = rect1->XPosition; + } + else + { + dstRect->XPosition = rect1->XPosition; + v3 = rect2->Width - rect1->XPosition; + v4 = rect2->XPosition; + } + dstRect->Width = v3 + v4 + 1; + v5 = rect1->YPosition; + if (v5 >= rect2->YPosition) + { + dstRect->YPosition = rect2->YPosition; + v6 = rect1->Height - rect2->YPosition; + v7 = rect1->YPosition; + } + else + { + dstRect->YPosition = v5; + v6 = rect2->Height - rect1->YPosition; + v7 = rect2->YPosition; + } + dstRect->Height = v6 + v7 + 1; + return dstRect->Width <= rect2->Width + rect1->Width && dstRect->Height <= rect2->Height + rect1->Height; +} diff --git a/SpaceCadetPinball/maths.h b/SpaceCadetPinball/maths.h index 18e6c26..c3433e9 100644 --- a/SpaceCadetPinball/maths.h +++ b/SpaceCadetPinball/maths.h @@ -1,7 +1,7 @@ #pragma once -struct __declspec(align(4)) visual_rect +struct __declspec(align(4)) rectangle_type { int XPosition; int YPosition; @@ -12,7 +12,7 @@ struct __declspec(align(4)) visual_rect class maths { public: - static void enclosing_box(visual_rect* rect1, visual_rect* rect2, visual_rect* dstRect); - + static void enclosing_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect); + static int rectangle_clip(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect); + static int overlapping_box(rectangle_type* rect1, rectangle_type* rect2, rectangle_type* dstRect); }; - diff --git a/SpaceCadetPinball/pinball.cpp b/SpaceCadetPinball/pinball.cpp index edd7b39..d3a35e7 100644 --- a/SpaceCadetPinball/pinball.cpp +++ b/SpaceCadetPinball/pinball.cpp @@ -4,7 +4,6 @@ int pinball::quickFlag = 0; -int pinball::render_background_bitmap = 0; TTextBox* pinball::InfoTextBox; TTextBox* pinball::MissTextBox; char pinball::getRcBuffer[6 * 256]; diff --git a/SpaceCadetPinball/pinball.h b/SpaceCadetPinball/pinball.h index f9571c2..d7cf97a 100644 --- a/SpaceCadetPinball/pinball.h +++ b/SpaceCadetPinball/pinball.h @@ -5,7 +5,6 @@ class pinball { public: static int quickFlag; - static int render_background_bitmap; static TTextBox* InfoTextBox; static TTextBox* MissTextBox; static HINSTANCE hinst; diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index 68548d2..7884145 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -1,75 +1,256 @@ #include "pch.h" #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 = new render_sprite_type_struct*[1000], **render::sprite_list = new - render_sprite_type_struct* [1000], **render::ball_list = new render_sprite_type_struct* [ - 1000]; +render_sprite_type_struct **render::dirty_list, **render::sprite_list, **render::ball_list; zmap_header_type* render::background_zmap; -int render::zmap_offset, render::zmap_offsetY; +int render::zmap_offset, render::zmap_offsetY, render::offset_x, render::offset_y; +float render::zscaler, render::zmin, render::zmax; +rectangle_type render::vscreen_rect; +gdrv_bitmap8 render::vscreen, *render::background_bitmap, render::ball_bitmap[20]; +zmap_header_type render::zscreen; + +void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height) +{ + ++memory::critical_allocation; + zscaler = zScaler; + zmin = zMin; + zmax = 4294967300.0f / zScaler + zMin; + sprite_list = (render_sprite_type_struct**)memory::allocate(0xFA0u); + dirty_list = (render_sprite_type_struct**)memory::allocate(0xFA0u); + ball_list = (render_sprite_type_struct**)memory::allocate(0x50u); + gdrv::create_bitmap(&vscreen, width, height); + zdrv::create_zmap(&zscreen, width, height); + zdrv::fill(&zscreen, zscreen.Width, zscreen.Height, 0, 0, 0xFFFF); + vscreen_rect.YPosition = 0; + vscreen_rect.XPosition = 0; + vscreen_rect.Width = width; + vscreen_rect.Height = height; + vscreen.YPosition = 0; + vscreen.XPosition = 0; + gdrv_bitmap8* ballBmp = ball_bitmap; + while (ballBmp <= &ball_bitmap[20]) + { + gdrv::create_raw_bitmap(ballBmp, 64, 64, 1); + ++ballBmp; + } + background_bitmap = bmp; + if (bmp) + gdrv::copy_bitmap(&vscreen, width, height, 0, 0, bmp, 0, 0); + else + gdrv::fill_bitmap(&vscreen, vscreen.Width, vscreen.Height, 0, 0, 0); + --memory::critical_allocation; +} + +void render::uninit() +{ + gdrv::destroy_bitmap(&vscreen); + zdrv::destroy_zmap(&zscreen); + for (int i = 0; i < many_sprites; ++i) + remove_sprite(sprite_list[i]); + for (int j = 0; j < many_balls; ++j) + remove_ball(ball_list[j]); + memory::free(ball_list); + memory::free(dirty_list); + memory::free(sprite_list); + many_sprites = 0; + many_dirty = 0; + many_balls = 0; +} void render::update() { + rectangle_type overlapRect{}; + + auto dirtyPtr = dirty_list; + for (int index = 0; index < many_dirty; ++dirtyPtr, ++index) + { + auto curSprite = *dirtyPtr; + if ((*dirtyPtr)->VisualType != VisualType::None) + { + if ((*dirtyPtr)->VisualType == 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)) + { + curSprite->DirtyRect.Width = -1; + continue; + } + + 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) + { + 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); + } + } + } + + 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 == + VisualType::Sprite)) + repaint(*dirtyPtr); + ++dirtyPtr; + } + + paint_balls(); + if (blit) + { + 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) + { + auto sprite = *dirtyPtr; + auto dirtyRect = &(*dirtyPtr)->DirtyRect; + auto width2 = (*dirtyPtr)->DirtyRect.Width; + if (width2 > 0) + gdrv::blit_sequence( + &vscreen, + dirtyRect->XPosition, + dirtyRect->YPosition, + dirtyRect->XPosition + xPos, + dirtyRect->YPosition + yPos, + width2, + dirtyRect->Height); + + auto rect = &sprite->BmpRectCopy; + rect->XPosition = dirtyRect->XPosition; + rect->YPosition = dirtyRect->YPosition; + rect->Width = dirtyRect->Width; + rect->Height = dirtyRect->Height; + + if (sprite->Unknown6_0 != 0) + remove_sprite(sprite); + } + + dirtyPtr = ball_list; + for (int index = 0; index < many_balls; ++dirtyPtr, ++index) + { + auto rectCopy = &(*dirtyPtr)->BmpRectCopy; + auto dirtyRect = &(*dirtyPtr)->DirtyRect; + if (maths::overlapping_box(dirtyRect, rectCopy, &overlapRect) && dirtyRect->Width > 0) + { + if (overlapRect.Width > 0) + gdrv::blit_sequence( + &vscreen, + overlapRect.XPosition, + overlapRect.YPosition, + overlapRect.XPosition + xPos, + overlapRect.YPosition + yPos, + overlapRect.Width, + overlapRect.Height); + } + else + { + if (dirtyRect->Width > 0) + gdrv::blit_sequence( + &vscreen, + dirtyRect->XPosition, + dirtyRect->YPosition, + dirtyRect->XPosition + xPos, + dirtyRect->YPosition + yPos, + dirtyRect->Width, + dirtyRect->Height); + if (rectCopy->Width > 0) + gdrv::blit_sequence( + &vscreen, + rectCopy->XPosition, + rectCopy->YPosition, + rectCopy->XPosition + xPos, + rectCopy->YPosition + yPos, + rectCopy->Width, + rectCopy->Height); + } + } + + gdrv::end_blit_sequence(); + } + + many_dirty = 0; + unpaint_balls(); } void render::paint() { - /*render_paint_balls(); - gdrv_blat((int)&vscreen, xDest, yDest); - render_unpaint_balls();*/ + paint_balls(); + gdrv::blat(&vscreen, vscreen.XPosition, vscreen.YPosition); + unpaint_balls(); } - -int render::sprite_modified(render_sprite_type_struct* sprite) +void render::sprite_modified(render_sprite_type_struct* sprite) { - int result = 0; // eax - - if (sprite->VisualType == VisualType::Ball) - return result; - result = many_dirty; - if (many_dirty < 999) + if (sprite->VisualType != VisualType::Ball && many_dirty < 999) dirty_list[many_dirty++] = sprite; - return result; } -render_sprite_type_struct* render::create_sprite(VisualType visualType, gdrv_bitmap8* rootBmp8, zmap_header_type* zMap, - int xPosition, int yPosition, visual_rect* rect) +render_sprite_type_struct* render::create_sprite(VisualType visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap, + int xPosition, int yPosition, rectangle_type* rect) { - render_sprite_type_struct* sprite = (render_sprite_type_struct*)memory::allocate(0x5Cu); - render_sprite_type_struct* result = nullptr; + auto sprite = (render_sprite_type_struct*)memory::allocate(sizeof(render_sprite_type_struct)); if (!sprite) - return result; - sprite->YPosition = yPosition; - sprite->RootBmp8 = rootBmp8; - sprite->XPosition = xPosition; + return nullptr; + sprite->BmpRect.YPosition = yPosition; + sprite->BmpRect.XPosition = xPosition; + sprite->Bmp = bmp; sprite->VisualType = visualType; sprite->Unknown6_0 = 0; - sprite->Unknown17 = 0; - sprite->Unknown18 = 0; + sprite->SpriteArray = nullptr; + sprite->SpriteCount = 0; if (rect) { - sprite->Rect = *rect; + sprite->BoundingRect = *rect; } else { - sprite->Rect.Width = -1; - sprite->Rect.Height = -1; - sprite->Rect.XPosition = 0; - sprite->Rect.YPosition = 0; + sprite->BoundingRect.Width = -1; + sprite->BoundingRect.Height = -1; + sprite->BoundingRect.XPosition = 0; + sprite->BoundingRect.YPosition = 0; } - if (rootBmp8) + if (bmp) { - sprite->Bmp8Width = rootBmp8->Width; - sprite->Bmp8Height = rootBmp8->Height; + sprite->BmpRect.Width = bmp->Width; + sprite->BmpRect.Height = bmp->Height; } else { - sprite->Bmp8Width = 0; - sprite->Bmp8Height = 0; + sprite->BmpRect.Width = 0; + sprite->BmpRect.Height = 0; } sprite->ZMap = zMap; sprite->ZMapOffestX = 0; @@ -80,10 +261,7 @@ render_sprite_type_struct* render::create_sprite(VisualType visualType, gdrv_bit sprite->ZMapOffestY = xPosition - zmap_offset; sprite->ZMapOffestX = yPosition - zmap_offsetY; } - sprite->XPosition2 = sprite->XPosition; - sprite->YPosition2 = sprite->YPosition; - sprite->Bmp8Width2 = sprite->Bmp8Width; - sprite->Bmp8Height2 = sprite->Bmp8Height; + sprite->BmpRectCopy = sprite->BmpRect; if (visualType == VisualType::Ball) { ball_list[many_balls++] = sprite; @@ -93,6 +271,255 @@ render_sprite_type_struct* render::create_sprite(VisualType visualType, gdrv_bit sprite_list[many_sprites++] = sprite; sprite_modified(sprite); } - result = sprite; - return result; + return sprite; +} + + +void render::remove_sprite(render_sprite_type_struct* sprite) +{ + int spriteCount = many_sprites; + int index = 0; + if (many_sprites > 0) + { + while (sprite_list[index] != sprite) + { + if (++index >= many_sprites) + return; + } + while (index < spriteCount) + { + sprite_list[index] = sprite_list[index + 1]; + spriteCount = many_sprites; + ++index; + } + many_sprites = spriteCount - 1; + if (sprite->SpriteArray) + memory::free(sprite->SpriteArray); + memory::free(sprite); + } +} + +void render::remove_ball(struct render_sprite_type_struct* ball) +{ + int ballCount = many_balls; + int index = 0; + if (many_balls > 0) + { + while (ball_list[index] != ball) + { + if (++index >= many_balls) + return; + } + while (index < ballCount) + { + ball_list[index] = ball_list[index + 1]; + ballCount = many_balls; + ++index; + } + many_balls = ballCount - 1; + memory::free(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) +{ + background_zmap = zMap; + zmap_offset = 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(depth2); + else + sprite->Depth = -1; + } + else + { + sprite->Depth = 0; + } + } +} + +void render::repaint(struct render_sprite_type_struct* sprite) +{ + rectangle_type clipRect{}; + if (!sprite->SpriteArray) + return; + for (int index = 0; index < sprite->SpriteCount; index++) + { + render_sprite_type_struct* curSprite = sprite->SpriteArray[index]; + if (!curSprite->Unknown6_0 && curSprite->Bmp) + { + if (maths::rectangle_clip(&curSprite->BmpRect, &sprite->DirtyRect, &clipRect)) + zdrv::paint( + clipRect.Width, + clipRect.Height, + &vscreen, + clipRect.XPosition, + clipRect.YPosition, + &zscreen, + clipRect.XPosition, + clipRect.YPosition, + curSprite->Bmp, + clipRect.XPosition - curSprite->BmpRect.XPosition, + clipRect.YPosition - curSprite->BmpRect.YPosition, + curSprite->ZMap, + clipRect.XPosition + curSprite->ZMapOffestY - curSprite->BmpRect.XPosition, + clipRect.YPosition + curSprite->ZMapOffestX - curSprite->BmpRect.YPosition); + } + } +} + + +void render::paint_balls() +{ + int ballCount = many_balls; + if (many_balls > 1) + { + for (int index = 0; index < ballCount; index++) + { + for (int i = index; i < ballCount / 2; ++i) + { + auto curBall = ball_list[i]; + auto firstBallPtr = &ball_list[index]; + if ((*firstBallPtr)->Depth > curBall->Depth) + { + auto firstBall = *firstBallPtr; + *firstBallPtr = curBall; + ball_list[i] = firstBall; + } + } + } + } + + auto ballPtr = ball_list; + auto ballBmpPtr = ball_bitmap; + for (int index2 = 0; index2 < many_balls; ++index2) + { + struct render_sprite_type_struct* sprite = *ballPtr; + rectangle_type* rect2 = &(*ballPtr)->DirtyRect; + if ((*ballPtr)->Bmp && maths::rectangle_clip(&sprite->BmpRect, &vscreen_rect, &(*ballPtr)->DirtyRect)) + { + int xPos = rect2->XPosition; + int yPos = rect2->YPosition; + gdrv::copy_bitmap(ballBmpPtr, rect2->Width, rect2->Height, 0, 0, &vscreen, xPos, yPos); + zdrv::paint_flat( + rect2->Width, + rect2->Height, + &vscreen, + xPos, + yPos, + &zscreen, + xPos, + yPos, + sprite->Bmp, + xPos - sprite->BmpRect.XPosition, + yPos - sprite->BmpRect.YPosition, + sprite->Depth); + } + else + { + rect2->Width = -1; + } + + ++ballBmpPtr; + ++ballPtr; + } +} + +void render::unpaint_balls() +{ + auto ballPtr = &ball_list[many_balls - 1]; + if (many_balls - 1 >= 0) + { + gdrv_bitmap8* bitmapPtr = &ball_bitmap[many_balls - 1]; + for (int index = many_balls; index > 0; index--) + { + struct render_sprite_type_struct* curBall = *ballPtr; + rectangle_type* rect2 = &(*ballPtr)->DirtyRect; + int width = (*ballPtr)->DirtyRect.Width; + if (width > 0) + gdrv::copy_bitmap( + &vscreen, + width, + (*ballPtr)->DirtyRect.Height, + (*ballPtr)->DirtyRect.XPosition, + (*ballPtr)->DirtyRect.YPosition, + bitmapPtr, + 0, + 0); + + rectangle_type* rectCopy = &curBall->BmpRectCopy; + rectCopy->XPosition = rect2->XPosition; + rectCopy->YPosition = rect2->YPosition; + rectCopy->Width = rect2->Width; + rectCopy->Height = rect2->Height; + + --ballPtr; + --bitmapPtr; + } + } +} + +void render::shift(int offsetX, int offsetY, int xSrc, int ySrc, int DestWidth, int DestHeight) +{ + offset_x += offsetX; + offset_y += offsetY; + paint_balls(); + gdrv::blit( + &vscreen, + xSrc, + ySrc, + xSrc + offset_x + vscreen.XPosition, + ySrc + offset_y + vscreen.YPosition, + DestWidth, + DestHeight); + unpaint_balls(); } diff --git a/SpaceCadetPinball/render.h b/SpaceCadetPinball/render.h index f56fcc1..b6a4fd3 100644 --- a/SpaceCadetPinball/render.h +++ b/SpaceCadetPinball/render.h @@ -12,30 +12,22 @@ enum class VisualType : char struct __declspec(align(4)) render_sprite_type_struct { - int XPosition; - int YPosition; - int Bmp8Width; - int Bmp8Height; - gdrv_bitmap8* RootBmp8; + rectangle_type BmpRect; + gdrv_bitmap8* Bmp; zmap_header_type* ZMap; char Unknown6_0; VisualType VisualType; - short Depth; - int XPosition2; - int YPosition2; - int Bmp8Width2; - int Bmp8Height2; + __int16 Depth; + rectangle_type BmpRectCopy; int ZMapOffestY; int ZMapOffestX; - int Unknown13; - int Unknown14; - int Unknown15; - int Unknown16; - int Unknown17; - int Unknown18; - visual_rect Rect; + rectangle_type DirtyRect; + render_sprite_type_struct** SpriteArray; + int SpriteCount; + rectangle_type BoundingRect; }; + static_assert(sizeof(render_sprite_type_struct) == 0x5c, "Wrong size render_sprite_type_struct"); class render @@ -45,12 +37,29 @@ public: 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; - static int zmap_offset, zmap_offsetY; + static int zmap_offset, zmap_offsetY, offset_x, offset_y; + static float zscaler, zmin, zmax; + static rectangle_type vscreen_rect; + static gdrv_bitmap8 vscreen, *background_bitmap, ball_bitmap[20]; + static zmap_header_type zscreen; + static void init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int height); + static void uninit(); static void update(); static void paint(); - static int sprite_modified(render_sprite_type_struct* sprite); - static render_sprite_type_struct* create_sprite(VisualType visualType, gdrv_bitmap8* rootBmp8, + static void sprite_modified(render_sprite_type_struct* sprite); + static render_sprite_type_struct* create_sprite(VisualType visualType, gdrv_bitmap8* bmp, zmap_header_type* zMap, - int xPosition, int yPosition, visual_rect* rect); + int xPosition, int yPosition, rectangle_type* rect); + static void remove_sprite(render_sprite_type_struct* sprite); + static void remove_ball(struct render_sprite_type_struct* ball); + 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 repaint(struct render_sprite_type_struct* sprite); + static void paint_balls(); + static void unpaint_balls(); + static void shift(int offsetX, int offsetY, int xSrc, int ySrc, int DestWidth, int DestHeight); }; diff --git a/SpaceCadetPinball/score.cpp b/SpaceCadetPinball/score.cpp index 101ad74..a987dc8 100644 --- a/SpaceCadetPinball/score.cpp +++ b/SpaceCadetPinball/score.cpp @@ -10,13 +10,13 @@ int score::init() return 1; } -scoreStruct* score::create(LPCSTR fieldName, int renderBgBmp) +scoreStruct* score::create(LPCSTR fieldName, gdrv_bitmap8* renderBgBmp) { scoreStruct* score = (scoreStruct*)memory::allocate(sizeof(scoreStruct)); if (!score) return nullptr; score->Unknown1 = -9999; - score->RenderBgBmp = renderBgBmp; + score->BackgroundBmp = renderBgBmp; __int16* shortArr = (__int16*)partman::field_labeled(loader::loader_table, fieldName, datFieldTypes::ShortArray); if (!shortArr) { diff --git a/SpaceCadetPinball/score.h b/SpaceCadetPinball/score.h index 0bbc71c..215e37b 100644 --- a/SpaceCadetPinball/score.h +++ b/SpaceCadetPinball/score.h @@ -1,10 +1,11 @@ #pragma once +#include "gdrv.h" struct scoreStruct { int Unknown1; int Unknown2; - int RenderBgBmp; + gdrv_bitmap8* BackgroundBmp; int Short1; int Short2; int Short3; @@ -25,6 +26,6 @@ class score { public: static int init(); - static scoreStruct* create(LPCSTR fieldName, int renderBgBmp); + static scoreStruct* create(LPCSTR fieldName, gdrv_bitmap8* renderBgBmp); static scoreStruct* dup(scoreStruct* score, int scoreIndex); }; diff --git a/SpaceCadetPinball/zdrv.cpp b/SpaceCadetPinball/zdrv.cpp index 8abcb7c..5463949 100644 --- a/SpaceCadetPinball/zdrv.cpp +++ b/SpaceCadetPinball/zdrv.cpp @@ -31,15 +31,11 @@ int zdrv::destroy_zmap(zmap_header_type* zmap) return -1; if (zmap->ZPtr1) memory::free(zmap->ZPtr1); - zmap->Width = 0; - zmap->Height = 0; - zmap->Stride = 0; - zmap->ZPtr1 = nullptr; - zmap->ZPtr2 = nullptr; + memset(zmap, 0, sizeof(zmap_header_type)); return 0; } -void zdrv::fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, __int16 fillChar) +void zdrv::fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, unsigned __int16 fillChar) { int fillCharInt = fillChar | (fillChar << 16); auto zmapPtr = &zmap->ZPtr1[2 * (xOff + zmap->Stride * (zmap->Height - height - yOff))]; @@ -50,7 +46,7 @@ void zdrv::fill(zmap_header_type* zmap, int width, int height, int xOff, int yOf unsigned int widthDiv2 = static_cast(width) >> 1; memset32(zmapPtr, fillCharInt, widthDiv2); - __int16* lastShort = (__int16*)&zmapPtr[2 * widthDiv2]; + auto lastShort = &zmapPtr[2 * widthDiv2]; for (int i = widthMod2; i; --i) *lastShort++ = fillChar; diff --git a/SpaceCadetPinball/zdrv.h b/SpaceCadetPinball/zdrv.h index 92d23ce..744b4eb 100644 --- a/SpaceCadetPinball/zdrv.h +++ b/SpaceCadetPinball/zdrv.h @@ -22,7 +22,7 @@ public: static int pad(int width); static int create_zmap(zmap_header_type* zmap, int width, int height); static int destroy_zmap(zmap_header_type* zmap); - static void fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, __int16 fillChar); + static void fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, unsigned __int16 fillChar); static void paint(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOff, int dstBmpYOff, zmap_header_type* dstZMap, int dstZMapXOff, int dstZMapYOff, gdrv_bitmap8* srcBmp, int srcBmpXOff, int srcBmpYOff, zmap_header_type* srcZMap, int srcZMapXOff, int srcZMapYOff);