Code from FT: simplified TFlipper sprite update.

TFlipperEdge moving geometry stored in object.
This commit is contained in:
Muzychenko Andrey 2022-08-23 08:14:28 +03:00
parent 7feba1e947
commit acd1ad34b2
9 changed files with 113 additions and 175 deletions

View File

@ -296,11 +296,10 @@ void DebugOverlay::DrawEdge(TEdgeSegment* edge)
if (flip)
{
flip->set_control_points(pb::time_now);
flip->build_edges_in_motion();
DrawLineType(TFlipperEdge::lineA);
DrawLineType(TFlipperEdge::lineB);
DrawCicleType(TFlipperEdge::circlebase);
DrawCicleType(TFlipperEdge::circleT1);
DrawLineType(flip->lineA);
DrawLineType(flip->lineB);
DrawCicleType(flip->circlebase);
DrawCicleType(flip->circleT1);
}
}

View File

@ -18,19 +18,11 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
HardHitSoundId = visual.SoundIndex4;
SoftHitSoundId = visual.SoundIndex3;
Elasticity = visual.Elasticity;
Timer = 0;
Smoothness = visual.Smoothness;
auto collMult = *loader::query_float_attribute(groupIndex, 0, 803);
auto retractTime = *loader::query_float_attribute(groupIndex, 0, 805);
auto extendTime = *loader::query_float_attribute(groupIndex, 0, 804);
/*Full tilt hack: different flipper speed*/
if (pb::FullTiltMode)
{
retractTime = 0.08f;
extendTime = 0.04f;
}
auto vecT2 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 802));
auto vecT1 = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 801));
auto origin = reinterpret_cast<vector3*>(loader::query_float_attribute(groupIndex, 0, 800));
@ -49,75 +41,53 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
Smoothness);
FlipperEdge = flipperEdge;
if (flipperEdge)
{
ExtendAnimationFrameTime = flipperEdge->ExtendTime / static_cast<float>(ListBitmap->size() - 1);
RetractAnimationFrameTime = flipperEdge->RetractTime / static_cast<float>(ListBitmap->size() - 1);
}
BmpIndex = 0;
InputTime = 0.0;
if (table)
table->FlipperList.push_back(this);
}
TFlipper::~TFlipper()
{
delete FlipperEdge;
if (PinballTable)
{
auto& flippers = PinballTable->FlipperList;
auto position = std::find(flippers.begin(), flippers.end(), this);
if (position != flippers.end())
flippers.erase(position);
}
}
int TFlipper::Message(int code, float value)
{
if (code == 1 || code == 2 || (code > 1008 && code <= 1011) || code == 1022)
{
float timerTime;
int command = code;
if (code == 1)
{
control::handler(1, this);
TimerTime = ExtendAnimationFrameTime;
loader::play_sound(HardHitSoundId, this, "TFlipper1");
}
else if (code == 2)
{
TimerTime = RetractAnimationFrameTime;
loader::play_sound(SoftHitSoundId, this, "TFlipper2");
}
else
{
// Retract for all non-input messages
command = 2;
TimerTime = RetractAnimationFrameTime;
code = 2;
}
if (MessageField)
{
// Message arrived before animation is finished
auto inputDt = value - FlipperEdge->InputTime;
timerTime = inputDt - floor(inputDt / TimerTime) * TimerTime;
if (timerTime < 0.0f)
timerTime = 0.0;
}
else
{
timerTime = TimerTime;
}
MessageField = command;
InputTime = value;
if (Timer)
timer::kill(Timer);
Timer = timer::set(timerTime, this, TimerExpired);
FlipperEdge->SetMotion(command, value);
MessageField = FlipperEdge->SetMotion(code, value);
return 0;
}
if (code == 1020 || code == 1024)
{
if (MessageField)
{
if (Timer)
timer::kill(Timer);
BmpIndex = -1;
MessageField = 2;
TimerExpired(Timer, this);
FlipperEdge->SetMotion(code, value);
MessageField = 0;
FlipperEdge->SetMotion(1024, value);
UpdateSprite(0);
}
}
return 0;
@ -132,53 +102,22 @@ void TFlipper::Collision(TBall* ball, vector2* nextPosition, vector2* direction,
{
}
void TFlipper::TimerExpired(int timerId, void* caller)
void TFlipper::UpdateSprite(float timeNow)
{
auto flip = static_cast<TFlipper*>(caller);
int bmpCountSub1 = flip->ListBitmap->size() - 1;
int bmpCountSub1 = ListBitmap->size() - 1;
auto newBmpIndex = static_cast<int>(floor(flip->FlipperEdge->flipper_angle(pb::time_now) / flip->FlipperEdge->AngleMax * bmpCountSub1 + 0.5));
if (newBmpIndex > bmpCountSub1)
newBmpIndex = bmpCountSub1;
if (newBmpIndex < 0)
newBmpIndex = 0;
auto newBmpIndex = static_cast<int>(floor(FlipperEdge->flipper_angle(timeNow) / FlipperEdge->AngleMax * bmpCountSub1 + 0.5f));
newBmpIndex = Clamp(newBmpIndex, 0, bmpCountSub1);
if (BmpIndex == newBmpIndex)
return;
bool bmpIndexOutOfBounds = false;
if (flip->MessageField == 1)
{
flip->BmpIndex = newBmpIndex;
if (flip->BmpIndex >= bmpCountSub1)
{
flip->BmpIndex = bmpCountSub1;
bmpIndexOutOfBounds = true;
}
}
if (flip->MessageField == 2)
{
flip->BmpIndex = newBmpIndex;
if (flip->BmpIndex <= 0)
{
flip->BmpIndex = 0;
bmpIndexOutOfBounds = true;
}
}
if (bmpIndexOutOfBounds)
{
flip->MessageField = 0;
flip->Timer = 0;
}
else
{
flip->Timer = timer::set(flip->TimerTime, flip, TimerExpired);
}
auto bmp = flip->ListBitmap->at(flip->BmpIndex);
auto zMap = flip->ListZMap->at(flip->BmpIndex);
BmpIndex = newBmpIndex;
auto bmp = ListBitmap->at(BmpIndex);
auto zMap = ListZMap->at(BmpIndex);
render::sprite_set(
flip->RenderSprite,
RenderSprite,
bmp,
zMap,
bmp->XPosition - flip->PinballTable->XOffset,
bmp->YPosition - flip->PinballTable->YOffset);
bmp->XPosition - PinballTable->XOffset,
bmp->YPosition - PinballTable->YOffset);
}

View File

@ -13,14 +13,8 @@ public:
void port_draw() override;
void Collision(TBall* ball, vector2* nextPosition, vector2* direction, float distance,
TEdgeSegment* edge) override;
static void TimerExpired(int timerId, void* caller);
void UpdateSprite(float timeNow);
int BmpIndex;
TFlipperEdge* FlipperEdge;
int Timer;
float ExtendAnimationFrameTime{};
float RetractAnimationFrameTime{};
float TimerTime{};
float InputTime;
};

View File

@ -2,14 +2,11 @@
#include "TFlipperEdge.h"
#include "pb.h"
#include "TLine.h"
#include "TPinballTable.h"
#include "TTableLayer.h"
float TFlipperEdge::flipper_sin_angle, TFlipperEdge::flipper_cos_angle;
vector2 TFlipperEdge::A1, TFlipperEdge::A2, TFlipperEdge::B1, TFlipperEdge::B2, TFlipperEdge::T1;
line_type TFlipperEdge::lineA, TFlipperEdge::lineB;
circle_type TFlipperEdge::circlebase, TFlipperEdge::circleT1;
TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, TPinballTable* table,
vector3* origin, vector3* vecT1, vector3* vecT2, float extendTime, float retractTime,
@ -19,8 +16,6 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
Elasticity = elasticity;
Smoothness = smoothness;
ExtendTime = extendTime;
RetractTime = retractTime;
CollisionMult = collMult;
T1Src = static_cast<vector2>(*vecT1);
@ -51,7 +46,19 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
if (crossProd.Z < 0.0f)
AngleMax = -AngleMax;
FlipperFlag = 0;
Angle1 = 0.0;
AngleDst = 0.0;
// 3DPB and FT have different formats for flipper speed:
// 3DPB: Time it takes for flipper to go from source to destination, in sec.
// FT: Flipper movement speed, in radians per sec.
if (pb::FullTiltMode)
{
auto angleMax = std::abs(AngleMax);
retractTime = angleMax / retractTime;
extendTime = angleMax / extendTime;
}
ExtendTime = extendTime;
RetractTime = retractTime;
auto dirX1 = vecDir1.X;
auto dirY1 = -vecDir1.Y;
@ -87,13 +94,12 @@ TFlipperEdge::TFlipperEdge(TCollisionComponent* collComp, char* activeFlag, unsi
InputTime = 0.0;
CollisionFlag1 = 0;
AngleStopTime = 0.0;
AngleMult = 0.0;
AngleAdvanceTime = 0.0;
}
void TFlipperEdge::port_draw()
{
set_control_points(InputTime);
build_edges_in_motion();
}
float TFlipperEdge::FindCollisionDistance(ray_type* ray)
@ -112,7 +118,6 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
CollisionFlag1 = 0;
CollisionFlag2 = 0;
set_control_points(ogRay->TimeNow);
build_edges_in_motion();
auto ballInside = is_ball_inside(ogRay->Origin.X, ogRay->Origin.Y);
srcRay.MinDistance = ogRay->MinDistance;
if (ballInside == 0)
@ -120,7 +125,7 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
srcRay.Direction = ogRay->Direction;
srcRay.MaxDistance = ogRay->MaxDistance;
srcRay.Origin = ogRay->Origin;
auto distance = maths::distance_to_flipper(srcRay, dstRay);
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
if (distance == 0.0f)
{
NextBallPosition = dstRay.Origin;
@ -166,14 +171,14 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
if (maths::distance_to_flipper(srcRay, dstRay) >= 1e+09f)
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
srcRay.Direction.X = RotOrigin.X - ogRay->Origin.X;
srcRay.Direction.Y = RotOrigin.Y - ogRay->Origin.Y;
maths::normalize_2d(srcRay.Direction);
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
if (maths::distance_to_flipper(srcRay, dstRay) >= 1e+09f)
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
return 1e+09;
}
@ -196,7 +201,6 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
while (timeNow < stopTime)
{
set_control_points(timeNow);
build_edges_in_motion();
auto ballInside = is_ball_inside(posX, posY);
if (ballInside != 0)
{
@ -220,7 +224,7 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
srcRay.Origin.X = posX - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = posY - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
if (maths::distance_to_flipper(srcRay, dstRay) >= 1e+09f)
if (maths::distance_to_flipper(this, srcRay, dstRay) >= 1e+09f)
{
NextBallPosition.X = posX;
NextBallPosition.Y = posY;
@ -249,7 +253,7 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
srcRay.Origin.X = ogRay->Origin.X - srcRay.Direction.X * 5.0f;
srcRay.Origin.Y = ogRay->Origin.Y - srcRay.Direction.Y * 5.0f;
srcRay.MaxDistance = ogRay->MaxDistance + 10.0f;
auto distance = maths::distance_to_flipper(srcRay, dstRay);
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
CollisionDirection = dstRay.Direction;
if (distance >= 1e+09f)
{
@ -265,7 +269,7 @@ float TFlipperEdge::FindCollisionDistance(ray_type* ray)
srcRay.MinDistance = ogRay->MinDistance;
srcRay.Origin = ogRay->Origin;
srcRay.MaxDistance = rayMaxDistance;
auto distance = maths::distance_to_flipper(srcRay, dstRay);
auto distance = maths::distance_to_flipper(this, srcRay, dstRay);
if (distance < 1e+09f)
{
NextBallPosition = dstRay.Origin;
@ -312,7 +316,7 @@ void TFlipperEdge::EdgeCollision(TBall* ball, float distance)
if (circlebase.RadiusSq * 1.01f < distanceSq)
{
float v11;
float v20 = sqrt(distanceSq / DistanceDivSq) * (fabs(AngleMax) / AngleMult);
float v20 = sqrt(distanceSq / DistanceDivSq) * (fabs(AngleMax) / AngleAdvanceTime);
float dot1 = maths::DotProduct(CollisionLinePerp, CollisionDirection);
if (dot1 >= 0.0f)
v11 = dot1 * v20;
@ -389,46 +393,39 @@ void TFlipperEdge::place_in_grid()
void TFlipperEdge::set_control_points(float timeNow)
{
maths::SinCos(flipper_angle(timeNow), flipper_sin_angle, flipper_cos_angle);
float sin, cos;
maths::SinCos(flipper_angle(timeNow), sin, cos);
A1 = A1Src;
A2 = A2Src;
B1 = B1Src;
B2 = B2Src;
T1 = T1Src;
maths::RotatePt(A1, flipper_sin_angle, flipper_cos_angle, RotOrigin);
maths::RotatePt(A2, flipper_sin_angle, flipper_cos_angle, RotOrigin);
maths::RotatePt(T1, flipper_sin_angle, flipper_cos_angle, RotOrigin);
maths::RotatePt(B1, flipper_sin_angle, flipper_cos_angle, RotOrigin);
maths::RotatePt(B2, flipper_sin_angle, flipper_cos_angle, RotOrigin);
}
void TFlipperEdge::build_edges_in_motion()
{
maths::RotatePt(A1, sin, cos, RotOrigin);
maths::RotatePt(A2, sin, cos, RotOrigin);
maths::RotatePt(T1, sin, cos, RotOrigin);
maths::RotatePt(B1, sin, cos, RotOrigin);
maths::RotatePt(B2, sin, cos, RotOrigin);
maths::line_init(lineA, A1.X, A1.Y, A2.X, A2.Y);
maths::line_init(lineB, B1.X, B1.Y, B2.X, B2.Y);
circlebase.RadiusSq = CirclebaseRadiusSq;
circlebase.Center.X = RotOrigin.X;
circlebase.Center.Y = RotOrigin.Y;
circleT1.RadiusSq = CircleT1RadiusSq;
circleT1.Center.X = T1.X;
circleT1.Center.Y = T1.Y;
circlebase = {RotOrigin, CirclebaseRadiusSq};
circleT1 = {T1, CircleT1RadiusSq};
}
float TFlipperEdge::flipper_angle(float timeNow)
{
// When not moving, flipper is at destination angle.
if (!FlipperFlag)
return Angle1;
return AngleDst;
float currentAngleDuration = fabsf((Angle1 - Angle2) / AngleMax * AngleMult);
float currentAngleRatio;
// How much time it takes to go from source to destination angle, in sec.
auto arcDuration = std::abs((AngleDst - AngleSrc) / AngleMax * AngleAdvanceTime);
if (currentAngleDuration >= 0.0000001f)
currentAngleRatio = (timeNow - InputTime) / currentAngleDuration;
else
currentAngleRatio = 1.0;
// How close the flipper is to destination, in [0, 1] range.
auto t = arcDuration >= 0.0000001f ? (timeNow - InputTime) / arcDuration : 1.0f;
t = Clamp(t, 0.0f, 1.0f);
currentAngleRatio = std::min(1.0f, std::max(currentAngleRatio, 0.0f));
return currentAngleRatio * (Angle1 - Angle2) + Angle2;
// Result = linear interpolation between source and destination angle.
return AngleSrc + t * (AngleDst - AngleSrc);
}
int TFlipperEdge::is_ball_inside(float x, float y)
@ -459,28 +456,32 @@ int TFlipperEdge::is_ball_inside(float x, float y)
return 0;
}
void TFlipperEdge::SetMotion(int code, float value)
int TFlipperEdge::SetMotion(int code, float value)
{
switch (code)
{
case 1:
Angle2 = flipper_angle(value);
Angle1 = AngleMax;
AngleMult = ExtendTime;
AngleSrc = flipper_angle(value);
AngleDst = AngleMax;
AngleAdvanceTime = ExtendTime;
break;
case 2:
Angle2 = flipper_angle(value);
Angle1 = 0.0;
AngleMult = RetractTime;
AngleSrc = flipper_angle(value);
AngleDst = 0.0f;
AngleAdvanceTime = RetractTime;
break;
case 1024:
FlipperFlag = 0;
Angle1 = 0.0;
return;
AngleSrc = 0.0f;
AngleDst = 0.0f;
break;
default: break;
}
if (AngleSrc == AngleDst)
code = 0;
InputTime = value;
FlipperFlag = code;
AngleStopTime = AngleMult + InputTime;
AngleStopTime = AngleAdvanceTime + InputTime;
return code;
}

View File

@ -15,10 +15,9 @@ public:
void EdgeCollision(TBall* ball, float distance) override;
void place_in_grid() override;
void set_control_points(float timeNow);
void build_edges_in_motion();
float flipper_angle(float timeNow);
int is_ball_inside(float x, float y);
void SetMotion(int code, float value);
int SetMotion(int code, float value);
int FlipperFlag;
float Elasticity;
@ -31,8 +30,8 @@ public:
float CirclebaseRadiusMSq;
float CircleT1RadiusMSq;
float AngleMax;
float Angle2{};
float Angle1;
float AngleSrc{};
float AngleDst;
int CollisionFlag1;
int CollisionFlag2{};
vector2 CollisionLinePerp{};
@ -49,13 +48,11 @@ public:
int EdgeCollisionFlag;
float InputTime;
float AngleStopTime;
float AngleMult;
float AngleAdvanceTime;
float ExtendTime;
float RetractTime;
vector2 NextBallPosition{};
static float flipper_sin_angle, flipper_cos_angle;
static vector2 A1, A2, B1, B2, T1;
static line_type lineA, lineB;
static circle_type circlebase, circleT1;
vector2 A1, A2, B1, B2, T1;
line_type lineA, lineB;
circle_type circlebase, circleT1;
};

View File

@ -67,6 +67,7 @@ public:
int Height{};
std::vector<TPinballComponent*> ComponentList;
std::vector<TBall*> BallList;
std::vector<TFlipper*> FlipperList;
TLightGroup* LightGroup;
float GravityDirVectMult{};
float GravityAngleX{};

View File

@ -300,29 +300,29 @@ void maths::RotatePt(vector2& point, float sin, float cos, const vector2& origin
// Return the distance from ray1 origin to the intersection point with the closest flipper feature.
// Sets ray2 origin to intersection point, direction to collision direction
float maths::distance_to_flipper(const ray_type& ray1, ray_type& ray2)
float maths::distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ray_type& ray2)
{
auto distance = 1000000000.0f;
auto distanceType = FlipperIntersect::none;
auto newDistance = ray_intersect_line(ray1, TFlipperEdge::lineA);
auto newDistance = ray_intersect_line(ray1, flipper->lineA);
if (newDistance < distance)
{
distance = newDistance;
distanceType = FlipperIntersect::lineA;
}
newDistance = ray_intersect_circle(ray1, TFlipperEdge::circlebase);
newDistance = ray_intersect_circle(ray1, flipper->circlebase);
if (newDistance < distance)
{
distance = newDistance;
distanceType = FlipperIntersect::circlebase;
}
newDistance = ray_intersect_circle(ray1, TFlipperEdge::circleT1);
newDistance = ray_intersect_circle(ray1, flipper->circleT1);
if (newDistance < distance)
{
distance = newDistance;
distanceType = FlipperIntersect::circleT1;
}
newDistance = ray_intersect_line(ray1, TFlipperEdge::lineB);
newDistance = ray_intersect_line(ray1, flipper->lineB);
if (newDistance < distance)
{
distance = newDistance;
@ -332,19 +332,19 @@ float maths::distance_to_flipper(const ray_type& ray1, ray_type& ray2)
switch (distanceType)
{
case FlipperIntersect::lineA:
ray2.Direction = TFlipperEdge::lineA.PerpendicularC;
ray2.Origin = TFlipperEdge::lineA.RayIntersect;
ray2.Direction = flipper->lineA.PerpendicularC;
ray2.Origin = flipper->lineA.RayIntersect;
break;
case FlipperIntersect::lineB:
ray2.Direction = TFlipperEdge::lineB.PerpendicularC;
ray2.Origin = TFlipperEdge::lineB.RayIntersect;
ray2.Direction = flipper->lineB.PerpendicularC;
ray2.Origin = flipper->lineB.RayIntersect;
break;
case FlipperIntersect::circlebase:
case FlipperIntersect::circleT1:
ray2.Origin.X = distance * ray1.Direction.X + ray1.Origin.X;
ray2.Origin.Y = distance * ray1.Direction.Y + ray1.Origin.Y;
ray2.Direction = vector_sub(ray2.Origin, distanceType == FlipperIntersect::circlebase ?
TFlipperEdge::circlebase.Center : TFlipperEdge::circleT1.Center);
flipper->circlebase.Center : flipper->circleT1.Center);
normalize_2d(ray2.Direction);
break;
case FlipperIntersect::none:

View File

@ -1,6 +1,7 @@
#pragma once
class TBall;
class TFlipperEdge;
struct vector2
{
@ -118,7 +119,7 @@ public:
static float Distance(const vector2& vec1, const vector2& vec2);
static void SinCos(float angle, float& sinOut, float& cosOut);
static void RotatePt(vector2& point, float sin, float cos, const vector2& origin);
static float distance_to_flipper(const ray_type& ray1, ray_type& ray2);
static float distance_to_flipper(TFlipperEdge* flipper, const ray_type& ray1, ray_type& ray2);
static void RotateVector(vector2& vec, float angle);
static void find_closest_edge(ramp_plane_type* plane, int planeCount, wall_point_type* wall, vector2& lineEnd,
vector2& lineStart);

View File

@ -24,6 +24,7 @@
#include "GroupData.h"
#include "partman.h"
#include "score.h"
#include "TFlipper.h"
#include "TPinballTable.h"
#include "TTextBox.h"
@ -329,6 +330,11 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
}
}
for (auto flipper : MainTable->FlipperList)
{
flipper->UpdateSprite(timeNow);
}
if (drawBalls)
{
for (auto ball : MainTable->BallList)