From d73d89762d0205a3a63fcc89eb8285e2c07179b9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 08:32:25 +0200 Subject: [PATCH 01/58] Don't let DEarthquake depend on r_viewpoint. The ticFrac value should be passed as a parameter, especially since this gets called from code that sets up r_viewpoint. --- src/g_shared/a_quake.cpp | 14 +++++++------- src/g_shared/a_sharedglobal.h | 4 ++-- src/r_utility.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 39ffd3c79..c81e1ff28 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -177,9 +177,9 @@ void DEarthquake::Tick () // //========================================================================== -double DEarthquake::GetModWave(double waveMultiplier) const +double DEarthquake::GetModWave(double ticFrac, double waveMultiplier) const { - double time = m_Countdown - r_viewpoint.TicFrac; + double time = m_Countdown - ticFrac; return g_sin(waveMultiplier * time * (M_PI * 2 / TICRATE)); } @@ -292,7 +292,7 @@ double DEarthquake::GetFalloff(double dist) const // //========================================================================== -int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jiggers) +int DEarthquake::StaticGetQuakeIntensities(double ticFrac, AActor *victim, FQuakeJiggers &jiggers) { if (victim->player != NULL && (victim->player->cheats & CF_NOCLIP)) { @@ -339,12 +339,12 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger } else { - jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave) * falloff * strength; + jiggers.RollWave = r * quake->GetModWave(ticFrac, quake->m_RollWave) * falloff * strength; - intensity.X *= quake->GetModWave(quake->m_WaveSpeed.X); - intensity.Y *= quake->GetModWave(quake->m_WaveSpeed.Y); - intensity.Z *= quake->GetModWave(quake->m_WaveSpeed.Z); + intensity.X *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.X); + intensity.Y *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.Y); + intensity.Z *= quake->GetModWave(ticFrac, quake->m_WaveSpeed.Z); intensity *= strength * falloff; // [RH] This only gives effect to the last sine quake. I would diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index ce2376cec..ca6c60144 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -145,10 +145,10 @@ public: double m_RollIntensity, m_RollWave; double GetModIntensity(double intensity, bool fake = false) const; - double GetModWave(double waveMultiplier) const; + double GetModWave(double ticFrac, double waveMultiplier) const; double GetFalloff(double dist) const; - static int StaticGetQuakeIntensities(AActor *viewer, FQuakeJiggers &jiggers); + static int StaticGetQuakeIntensities(double ticFrac, AActor *viewer, FQuakeJiggers &jiggers); private: DEarthquake (); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 05dafee9b..127ac4597 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -872,7 +872,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor FQuakeJiggers jiggers; memset(&jiggers, 0, sizeof(jiggers)); - if (DEarthquake::StaticGetQuakeIntensities(viewpoint.camera, jiggers) > 0) + if (DEarthquake::StaticGetQuakeIntensities(viewpoint.TicFrac, viewpoint.camera, jiggers) > 0) { double quakefactor = r_quakeintensity; DAngle an; From 8787905fedd7b70bab683164e4bc16e32fa69ab9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 09:00:50 +0200 Subject: [PATCH 02/58] Fixed: DBaseStatusBar::Draw did not use its ticFrac parameter when being called from scripts Instead it directly went to the global viewpoint again which would be inconsistent. --- src/d_main.cpp | 6 +++--- src/g_statusbar/sbar.h | 8 ++++---- src/g_statusbar/shared_sbar.cpp | 11 ++++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 6542b950a..c507ecc57 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -817,7 +817,7 @@ void D_Display () { StatusBar->DrawCrosshair(); } - StatusBar->CallDraw (HUD_AltHud); + StatusBar->CallDraw (HUD_AltHud, r_viewpoint.TicFrac); StatusBar->DrawTopStuff (HUD_AltHud); } else @@ -825,13 +825,13 @@ void D_Display () { EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None; StatusBar->DrawBottomStuff (state); - StatusBar->CallDraw (state); + StatusBar->CallDraw (state, r_viewpoint.TicFrac); StatusBar->DrawTopStuff (state); } else { StatusBar->DrawBottomStuff (HUD_StatusBar); - StatusBar->CallDraw (HUD_StatusBar); + StatusBar->CallDraw (HUD_StatusBar, r_viewpoint.TicFrac); StatusBar->DrawTopStuff (HUD_StatusBar); } //stb.Unclock(); diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h index a0f8edbda..a98111a29 100644 --- a/src/g_statusbar/sbar.h +++ b/src/g_statusbar/sbar.h @@ -397,10 +397,10 @@ public: void SetScale(); virtual void Tick (); void CallTick(); - virtual void Draw (EHudState state); - void CallDraw(EHudState state); - void DrawBottomStuff (EHudState state); - void DrawTopStuff (EHudState state); + virtual void Draw (EHudState state, double ticFrac); + void CallDraw(EHudState state, double ticFrac); + void DrawBottomStuff (EHudState state); + void DrawTopStuff (EHudState state); void FlashItem (const PClass *itemtype); void AttachToPlayer(player_t *player); DVector2 GetHUDScale() const; diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index 4d6d6e737..e0e2cc9d5 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -1006,7 +1006,7 @@ void DBaseStatusBar::DrawMessages (int layer, int bottom) // //--------------------------------------------------------------------------- -void DBaseStatusBar::Draw (EHudState state) +void DBaseStatusBar::Draw (EHudState state, double ticFrac) { // HUD_AltHud state is for popups only if (state == HUD_AltHud) @@ -1048,18 +1048,19 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, Draw) { PARAM_SELF_PROLOGUE(DBaseStatusBar); PARAM_INT(state); - self->Draw((EHudState)state); + PARAM_FLOAT(ticFrac); + self->Draw((EHudState)state, ticFrac); return 0; } -void DBaseStatusBar::CallDraw(EHudState state) +void DBaseStatusBar::CallDraw(EHudState state, double ticFrac) { IFVIRTUAL(DBaseStatusBar, Draw) { - VMValue params[] = { (DObject*)this, state, r_viewpoint.TicFrac }; + VMValue params[] = { (DObject*)this, state, ticFrac }; VMCall(func, params, countof(params), nullptr, 0); } - else Draw(state); + else Draw(state, ticFrac); screen->ClearClipRect(); // make sure the scripts don't leave a valid clipping rect behind. BeginStatusBar(BaseSBarHorizontalResolution, BaseSBarVerticalResolution, BaseRelTop, false); } From 46d73e7b4b230a68bd579bd56bcdfafed1978977 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 09:18:27 +0200 Subject: [PATCH 03/58] Pass ticFrac to the model renderer as a parameter This also removes the rather pointless gl_RenderModel functions because with their two lines of content they can just be placed inline. --- src/gl/models/gl_models.cpp | 24 ------------------------ src/gl/models/gl_models.h | 2 -- src/gl/scene/gl_sprite.cpp | 3 ++- src/gl/scene/gl_weapon.cpp | 3 ++- src/polyrenderer/scene/poly_model.cpp | 2 +- src/r_data/models/models.cpp | 4 ++-- src/r_data/models/models.h | 2 +- src/swrenderer/things/r_model.cpp | 2 +- 8 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index 8669d7bab..25b8182ca 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -338,27 +338,3 @@ void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame } } } - -//=========================================================================== -// -// gl_RenderModel -// -//=========================================================================== - -void gl_RenderModel(GLSprite * spr, int mli) -{ - FGLModelRenderer renderer(mli); - renderer.RenderModel(spr->x, spr->y, spr->z, spr->modelframe, spr->actor); -} - -//=========================================================================== -// -// gl_RenderHUDModel -// -//=========================================================================== - -void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY, int mli) -{ - FGLModelRenderer renderer(mli); - renderer.RenderHUDModel(psp, ofsX, ofsY); -} diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index 0fb82f9f4..9cab5ecfc 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -51,5 +51,3 @@ public: void DrawElements(int numIndices, size_t offset) override; }; -void gl_RenderModel(GLSprite * spr, int mli); -void gl_RenderHUDModel(DPSprite *psp, float ofsx, float ofsy, int mli); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index b4116d60e..cc988ec73 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -276,7 +276,8 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) } else { - gl_RenderModel(sprite, sprite->dynlightindex); + FGLModelRenderer renderer(sprite->dynlightindex); + renderer.RenderModel(sprite->x, sprite->y, sprite->z, sprite->modelframe, sprite->actor, r_viewpoint.TicFrac); } } diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index 6ab0d5222..a842eda6e 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -66,7 +66,8 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds) if (huds->mframe) { gl_RenderState.AlphaFunc(GL_GEQUAL, 0); - gl_RenderHUDModel(huds->weapon, huds->mx, huds->my, huds->lightindex); + FGLModelRenderer renderer(huds->lightindex); + renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my); } else { diff --git a/src/polyrenderer/scene/poly_model.cpp b/src/polyrenderer/scene/poly_model.cpp index 46d064a7b..4251589d9 100644 --- a/src/polyrenderer/scene/poly_model.cpp +++ b/src/polyrenderer/scene/poly_model.cpp @@ -37,7 +37,7 @@ void PolyRenderModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_ { PolyModelRenderer renderer(thread, worldToClip, stencilValue); renderer.AddLights(actor); - renderer.RenderModel(x, y, z, smf, actor); + renderer.RenderModel(x, y, z, smf, actor, r_viewpoint.TicFrac); PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f); } diff --git a/src/r_data/models/models.cpp b/src/r_data/models/models.cpp index 316281f65..b248f2895 100644 --- a/src/r_data/models/models.cpp +++ b/src/r_data/models/models.cpp @@ -54,7 +54,7 @@ extern TDeletingArray VoxelDefs; DeletingModelArray Models; -void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) +void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor, double ticFrac) { // Setup transformation. @@ -128,7 +128,7 @@ void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *s if (actor->renderflags & RF_INTERPOLATEANGLES) { // [Nash] use interpolated angles - DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac); + DRotator Angles = actor->InterpolatedAngles(ticFrac); angle = Angles.Yaw.Degrees; } diff --git a/src/r_data/models/models.h b/src/r_data/models/models.h index 213e9ab21..e4da6efac 100644 --- a/src/r_data/models/models.h +++ b/src/r_data/models/models.h @@ -60,7 +60,7 @@ class FModelRenderer public: virtual ~FModelRenderer() { } - void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor); + void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor, double ticFrac); void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); virtual ModelRendererType GetType() const = 0; diff --git a/src/swrenderer/things/r_model.cpp b/src/swrenderer/things/r_model.cpp index aa9437670..0991d1d45 100644 --- a/src/swrenderer/things/r_model.cpp +++ b/src/swrenderer/things/r_model.cpp @@ -76,7 +76,7 @@ namespace swrenderer { SWModelRenderer renderer(thread, clip3DFloor, &WorldToClip, MirrorWorldToClip); renderer.AddLights(actor); - renderer.RenderModel(x, y, z, smf, actor); + renderer.RenderModel(x, y, z, smf, actor, r_viewpoint.TicFrac); PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f); } From 74d8c929cc0b08fd2424ba261dfd7459047367fb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 09:37:49 +0200 Subject: [PATCH 04/58] Give the clipper a reference to the current viewpoint This is both for efficiency and encapsulation. At last on MSVC in 64 bit, accessing global variables is very inefficient and the clipper was doing it repeatedly in its worker functions. It is also one less place where the global viewpoint gets checked. --- src/gl/scene/gl_drawinfo.cpp | 1 + src/gl/scene/gl_portal.cpp | 4 +--- src/hwrenderer/scene/hw_clipper.cpp | 15 ++++++++------- src/hwrenderer/scene/hw_clipper.h | 25 ++++++++++++++++--------- src/r_defs.h | 2 -- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index abcdbe70b..c7e02314c 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -201,6 +201,7 @@ FDrawInfo *FDrawInfo::StartDrawInfo(GLSceneDrawer *drawer) di->mDrawer = drawer; di->mVBO = GLRenderer->mVBO; di->mClipper = &staticClipper; + di->mClipper->SetViewpoint(r_viewpoint); staticClipper.Clear(); di->FixedColormap = drawer->FixedColormap; di->StartScene(); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 3f8ede410..bb0d09adb 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -941,9 +941,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) angle_t af = drawer->FrustumAngle(); if (afmClipper->SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs()+af, r_viewpoint.Angles.Yaw.BAMs()-af); - angle_t a2 = linedef->v1->GetClipAngle(); - angle_t a1 = linedef->v2->GetClipAngle(); - di->mClipper->SafeAddClipRange(a1,a2); + di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); gl_RenderState.SetClipLine(linedef); gl_RenderState.EnableClipLine(true); diff --git a/src/hwrenderer/scene/hw_clipper.cpp b/src/hwrenderer/scene/hw_clipper.cpp index e1811dd8c..85646f84c 100644 --- a/src/hwrenderer/scene/hw_clipper.cpp +++ b/src/hwrenderer/scene/hw_clipper.cpp @@ -373,10 +373,10 @@ angle_t Clipper::AngleToPseudo(angle_t ang) // //----------------------------------------------------------------------------- -angle_t R_PointToPseudoAngle(double x, double y) +angle_t Clipper::PointToPseudoAngle(double x, double y) { - double vecx = x - r_viewpoint.Pos.X; - double vecy = y - r_viewpoint.Pos.Y; + double vecx = x - viewpoint->Pos.X; + double vecy = y - viewpoint->Pos.Y; if (vecx == 0 && vecy == 0) { @@ -427,14 +427,15 @@ bool Clipper::CheckBox(const float *bspcoord) // Find the corners of the box // that define the edges from current viewpoint. - boxpos = (r_viewpoint.Pos.X <= bspcoord[BOXLEFT] ? 0 : r_viewpoint.Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) + - (r_viewpoint.Pos.Y >= bspcoord[BOXTOP ] ? 0 : r_viewpoint.Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8); + auto &vp = viewpoint; + boxpos = (vp->Pos.X <= bspcoord[BOXLEFT] ? 0 : vp->Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) + + (vp->Pos.Y >= bspcoord[BOXTOP ] ? 0 : vp->Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8); if (boxpos == 5) return true; check = checkcoord[boxpos]; - angle1 = R_PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]); - angle2 = R_PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]); + angle1 = PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]); + angle2 = PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]); return SafeCheckRange(angle2, angle1); } diff --git a/src/hwrenderer/scene/hw_clipper.h b/src/hwrenderer/scene/hw_clipper.h index c3897bd56..e8da8172c 100644 --- a/src/hwrenderer/scene/hw_clipper.h +++ b/src/hwrenderer/scene/hw_clipper.h @@ -6,14 +6,6 @@ #include "r_utility.h" #include "memarena.h" -angle_t R_PointToPseudoAngle(double x, double y); - -// Used to speed up angle calculations during clipping -inline angle_t vertex_t::GetClipAngle() -{ - return R_PointToPseudoAngle(p.X, p.Y); -} - class ClipNode { friend class Clipper; @@ -37,6 +29,7 @@ class Clipper ClipNode * clipnodes = nullptr; ClipNode * cliphead = nullptr; ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called + const FRenderViewpoint *viewpoint = nullptr; bool blocked = false; static angle_t AngleToPseudo(angle_t ang); @@ -78,6 +71,11 @@ public: c->next = c->prev = NULL; return c; } + + void SetViewpoint(const FRenderViewpoint &vp) + { + viewpoint = &vp; + } void SetSilhouette(); @@ -105,6 +103,13 @@ public: AddClipRange(startangle, endangle); } } + + void SafeAddClipRange(const vertex_t *v1, const vertex_t *v2) + { + angle_t a2 = PointToPseudoAngle(v1->p.X, v1->p.Y); + angle_t a1 = PointToPseudoAngle(v2->p.X, v2->p.Y); + SafeAddClipRange(a1,a2); + } void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle) { @@ -141,13 +146,15 @@ public: { return blocked; } + + angle_t PointToPseudoAngle(double x, double y); bool CheckBox(const float *bspcoord); // Used to speed up angle calculations during clipping inline angle_t GetClipAngle(vertex_t *v) { - return unsigned(v->angletime) == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = R_PointToPseudoAngle(v->p.X, v->p.Y)); + return unsigned(v->angletime) == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = PointToPseudoAngle(v->p.X, v->p.Y)); } }; diff --git a/src/r_defs.h b/src/r_defs.h index 4ba0d45d3..4422fbf8d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -180,8 +180,6 @@ struct vertex_t { p.Zero(); } - - angle_t GetClipAngle(); }; // Forward of LineDefs, for Sectors. From f857d14e765e47d6912c846c27dd7614508eabd0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:22:27 +0200 Subject: [PATCH 05/58] -Code shortening --- src/hwrenderer/utility/hw_clock.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hwrenderer/utility/hw_clock.cpp b/src/hwrenderer/utility/hw_clock.cpp index f0480f2c4..74aa096a4 100644 --- a/src/hwrenderer/utility/hw_clock.cpp +++ b/src/hwrenderer/utility/hw_clock.cpp @@ -160,8 +160,9 @@ void CheckBench() FString compose; + auto &vp = r_viewpoint; compose.Format("Map %s: \"%s\",\nx = %1.4f, y = %1.4f, z = %1.4f, angle = %1.4f, pitch = %1.4f\n", - level.MapName.GetChars(), level.LevelName.GetChars(), r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw.Degrees, r_viewpoint.Angles.Pitch.Degrees); + level.MapName.GetChars(), level.LevelName.GetChars(), vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw.Degrees, vp.Angles.Pitch.Degrees); AppendRenderStats(compose); AppendRenderTimes(compose); From cf1641d0f6134db1662542f40c2870c271ada21e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:25:11 +0200 Subject: [PATCH 06/58] - reduce number of places in weapon code which access r_viewpoint directly. --- src/hwrenderer/scene/hw_weapon.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/hwrenderer/scene/hw_weapon.cpp b/src/hwrenderer/scene/hw_weapon.cpp index c22d2183a..8a0835765 100644 --- a/src/hwrenderer/scene/hw_weapon.cpp +++ b/src/hwrenderer/scene/hw_weapon.cpp @@ -76,10 +76,10 @@ static bool isBright(DPSprite *psp) // //========================================================================== -static WeaponPosition GetWeaponPosition(player_t *player) +static WeaponPosition GetWeaponPosition(player_t *player, double ticFrac) { WeaponPosition w; - P_BobWeapon(player, &w.bobx, &w.boby, r_viewpoint.TicFrac); + P_BobWeapon(player, &w.bobx, &w.boby, ticFrac); // Interpolate the main weapon layer once so as to be able to add it to other layers. if ((w.weapon = player->FindPSprite(PSP_WEAPON)) != nullptr) @@ -91,8 +91,8 @@ static WeaponPosition GetWeaponPosition(player_t *player) } else { - w.wx = (float)(w.weapon->oldx + (w.weapon->x - w.weapon->oldx) * r_viewpoint.TicFrac); - w.wy = (float)(w.weapon->oldy + (w.weapon->y - w.weapon->oldy) * r_viewpoint.TicFrac); + w.wx = (float)(w.weapon->oldx + (w.weapon->x - w.weapon->oldx) * ticFrac); + w.wy = (float)(w.weapon->oldy + (w.weapon->y - w.weapon->oldy) * ticFrac); } } else @@ -109,7 +109,7 @@ static WeaponPosition GetWeaponPosition(player_t *player) // //========================================================================== -static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp) +static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp, double ticFrac) { if (psp->firstTic) { // Can't interpolate the first tic. @@ -118,8 +118,8 @@ static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp) psp->oldy = psp->y; } - float sx = float(psp->oldx + (psp->x - psp->oldx) * r_viewpoint.TicFrac); - float sy = float(psp->oldy + (psp->y - psp->oldy) * r_viewpoint.TicFrac); + float sx = float(psp->oldx + (psp->x - psp->oldx) * ticFrac); + float sy = float(psp->oldy + (psp->y - psp->oldy) * ticFrac); if (psp->Flags & PSPF_ADDBOB) { @@ -169,14 +169,14 @@ static WeaponLighting GetWeaponLighting(sector_t *viewsector, const DVector3 &po if (ifloorplane.ZatPoint(r_viewpoint.Pos); + lightbottom = viewsector->floorplane.ZatPoint(pos); } - if (lightbottomplayer; const bool hudModelStep = IsHUDModelForPlayerAvailable(player); + + auto &vp = r_viewpoint; - AActor *camera = r_viewpoint.camera; + AActor *camera = vp.camera; // this is the same as the software renderer if (!player || @@ -437,8 +439,8 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area) (r_deathcamera && camera->health <= 0)) return; - WeaponPosition weap = GetWeaponPosition(camera->player); - WeaponLighting light = GetWeaponLighting(viewsector, r_viewpoint.Pos, FixedColormap, in_area, camera->Pos()); + WeaponPosition weap = GetWeaponPosition(camera->player, vp.TicFrac); + WeaponLighting light = GetWeaponLighting(viewsector, vp.Pos, FixedColormap, in_area, camera->Pos()); // hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change // light mode here to draw the weapon sprite. @@ -460,7 +462,7 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area) if (!hudsprite.GetWeaponRenderStyle(psp, camera, viewsector, light)) continue; - FVector2 spos = BobWeapon(weap, psp); + FVector2 spos = BobWeapon(weap, psp, vp.TicFrac); hudsprite.dynrgb[0] = hudsprite.dynrgb[1] = hudsprite.dynrgb[2] = 0; hudsprite.lightindex = -1; From 18f43dea1ed20ebbfeb291a999bf3b020edd9bdb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:29:09 +0200 Subject: [PATCH 07/58] - reduced number of references to r_viewpoint in hw_sprites.cpp --- src/hwrenderer/scene/hw_sprites.cpp | 37 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index 0e86b88ba..e5660c26b 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -403,7 +403,8 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t return; } - AActor *camera = r_viewpoint.camera; + auto &vp = r_viewpoint; + AActor *camera = vp.camera; if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->Alpha)) { @@ -425,26 +426,26 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t } // [RH] Interpolate the sprite's position to make it look smooth - DVector3 thingpos = thing->InterpolatedPosition(r_viewpoint.TicFrac); + DVector3 thingpos = thing->InterpolatedPosition(vp.TicFrac); if (thruportal == 1) thingpos += level.Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup); // Some added checks if the camera actor is not supposed to be seen. It can happen that some portal setup has this actor in view in which case it may not be skipped here - if (thing == camera && !r_viewpoint.showviewer) + if (thing == camera && !vp.showviewer) { DVector3 thingorigin = thing->Pos(); if (thruportal == 1) thingorigin += level.Displacements.getOffset(thing->Sector->PortalGroup, sector->PortalGroup); - if (fabs(thingorigin.X - r_viewpoint.ActorPos.X) < 2 && fabs(thingorigin.Y - r_viewpoint.ActorPos.Y) < 2) return; + if (fabs(thingorigin.X - vp.ActorPos.X) < 2 && fabs(thingorigin.Y - vp.ActorPos.Y) < 2) return; } // Thing is invisible if close to the camera. if (thing->renderflags & RF_MAYBEINVISIBLE) { - if (fabs(thingpos.X - r_viewpoint.Pos.X) < 32 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 32) return; + if (fabs(thingpos.X - vp.Pos.X) < 32 && fabs(thingpos.Y - vp.Pos.Y) < 32) return; } // Too close to the camera. This doesn't look good if it is a sprite. - if (fabs(thingpos.X - r_viewpoint.Pos.X) < 2 && fabs(thingpos.Y - r_viewpoint.Pos.Y) < 2) + if (fabs(thingpos.X - vp.Pos.X) < 2 && fabs(thingpos.Y - vp.Pos.Y) < 2) { - if (r_viewpoint.Pos.Z >= thingpos.Z - 2 && r_viewpoint.Pos.Z <= thingpos.Z + thing->Height + 2) + if (vp.Pos.Z >= thingpos.Z - 2 && vp.Pos.Z <= thingpos.Z + thing->Height + 2) { // exclude vertically moving objects from this check. if (!thing->Vel.isZero()) @@ -466,7 +467,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t if (speed >= thing->target->radius / 2) { double clipdist = clamp(thing->Speed, thing->target->radius, thing->target->radius * 2); - if ((thingpos - r_viewpoint.Pos).LengthSquared() < clipdist * clipdist) return; + if ((thingpos - vp.Pos).LengthSquared() < clipdist * clipdist) return; } } thing->flags7 |= MF7_FLYCHEAT; // do this only once for the very first frame, but not if it gets into range again. @@ -479,7 +480,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t } // disabled because almost none of the actual game code is even remotely prepared for this. If desired, use the INTERPOLATE flag. if (thing->renderflags & RF_INTERPOLATEANGLES) - Angles = thing->InterpolatedAngles(r_viewpoint.TicFrac); + Angles = thing->InterpolatedAngles(vp.TicFrac); else Angles = thing->Angles; @@ -506,7 +507,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t // [RH] Make floatbobbing a renderer-only effect. if (thing->flags2 & MF2_FLOATBOB) { - float fz = thing->GetBobOffset(r_viewpoint.TicFrac); + float fz = thing->GetBobOffset(vp.TicFrac); z += fz; } @@ -514,7 +515,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t if (!modelframe) { bool mirror; - DAngle ang = (thingpos - r_viewpoint.Pos).Angle(); + DAngle ang = (thingpos - vp.Pos).Angle(); FTextureID patch; // [ZZ] add direct picnum override if (isPicnumOverride) @@ -531,7 +532,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t int rot; if (!(thing->renderflags & RF_FLATSPRITE) || thing->flags7 & MF7_SPRITEANGLE) { - sprangle = thing->GetSpriteAngle(ang, r_viewpoint.TicFrac); + sprangle = thing->GetSpriteAngle(ang, vp.TicFrac); rot = -1; } else @@ -632,7 +633,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t gltexture = nullptr; } - depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin); + depth = FloatToFixed((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin); // light calculation @@ -927,7 +928,8 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t * } } - double timefrac = r_viewpoint.TicFrac; + auto &vp = r_viewpoint; + double timefrac = vp.TicFrac; if (paused || bglobal.freeze || (level.flags2 & LEVEL2_FROZEN)) timefrac = 0.; float xvf = (particle->Vel.X) * timefrac; @@ -954,7 +956,7 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t * z1=z-scalefac; z2=z+scalefac; - depth = FloatToFixed((x - r_viewpoint.Pos.X) * r_viewpoint.TanCos + (y - r_viewpoint.Pos.Y) * r_viewpoint.TanSin); + depth = FloatToFixed((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin); actor=nullptr; this->particle=particle; @@ -984,6 +986,7 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area) TMap processcheck; if (glport->validcount == validcount) return; // only process once per frame glport->validcount = validcount; + auto &vp = r_viewpoint; for (auto port : glport->lines) { line_t *line = port->mOrigin; @@ -1007,9 +1010,9 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area) DVector3 newpos = savedpos; sector_t fakesector; - if (!r_viewpoint.showviewer && th == r_viewpoint.camera) + if (!vp.showviewer && th == vp.camera) { - if (fabs(savedpos.X - r_viewpoint.ActorPos.X) < 2 && fabs(savedpos.Y - r_viewpoint.ActorPos.Y) < 2) + if (fabs(savedpos.X - vp.ActorPos.X) < 2 && fabs(savedpos.Y - vp.ActorPos.Y) < 2) { continue; } From 3eb4b137f3b6d9a6a42aba0b2f320f77731187c1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:30:18 +0200 Subject: [PATCH 08/58] - reduced number of references to r_viewpoint in hw_fakeflat.cpp --- src/hwrenderer/scene/hw_fakeflat.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hwrenderer/scene/hw_fakeflat.cpp b/src/hwrenderer/scene/hw_fakeflat.cpp index e4e986ca8..c68677b13 100644 --- a/src/hwrenderer/scene/hw_fakeflat.cpp +++ b/src/hwrenderer/scene/hw_fakeflat.cpp @@ -392,15 +392,16 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac //----------------------------------------------------------------------------- void HWDrawInfo::SetViewArea() { + auto &vp = r_viewpoint; // The render_sector is better suited to represent the current position in GL - r_viewpoint.sector = R_PointInSubsector(r_viewpoint.Pos)->render_sector; + vp.sector = R_PointInSubsector(vp.Pos)->render_sector; // Get the heightsec state from the render sector, not the current one! - if (r_viewpoint.sector->GetHeightSec()) + if (vp.sector->GetHeightSec()) { - in_area = r_viewpoint.Pos.Z <= r_viewpoint.sector->heightsec->floorplane.ZatPoint(r_viewpoint.Pos) ? area_below : - (r_viewpoint.Pos.Z > r_viewpoint.sector->heightsec->ceilingplane.ZatPoint(r_viewpoint.Pos) && - !(r_viewpoint.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal; + in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below : + (vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) && + !(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal; } else { From fba5b872be4efb795f3503b90a3838732cabf7e8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:31:25 +0200 Subject: [PATCH 09/58] - reduced number of references to r_viewpoint in hw_flats --- src/hwrenderer/scene/hw_flats.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 27e31843f..d4e01fa9a 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -263,6 +263,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) dynlightindex = -1; uint8_t &srf = di->sectorrenderflags[sector->sectornum]; + auto &vp = r_viewpoint; // // @@ -271,7 +272,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) // // // - if (frontsector->floorplane.ZatPoint(r_viewpoint.Pos) <= r_viewpoint.Pos.Z) + if (frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z) { // process the original floor first. @@ -331,7 +332,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) // // // - if (frontsector->ceilingplane.ZatPoint(r_viewpoint.Pos) >= r_viewpoint.Pos.Z) + if (frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z) { // process the original ceiling first. @@ -422,7 +423,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) double ff_top = rover->top.plane->ZatPoint(sector->centerspot); if (ff_top < lastceilingheight) { - if (r_viewpoint.Pos.Z <= rover->top.plane->ZatPoint(r_viewpoint.Pos)) + if (vp.Pos.Z <= rover->top.plane->ZatPoint(vp.Pos)) { SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->Colormap.FadeColor; @@ -436,7 +437,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot); if (ff_bottom < lastceilingheight) { - if (r_viewpoint.Pos.Z <= rover->bottom.plane->ZatPoint(r_viewpoint.Pos)) + if (vp.Pos.Z <= rover->bottom.plane->ZatPoint(vp.Pos)) { SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->Colormap.FadeColor; @@ -462,7 +463,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot); if (ff_bottom > lastfloorheight || (rover->flags&FF_FIX)) { - if (r_viewpoint.Pos.Z >= rover->bottom.plane->ZatPoint(r_viewpoint.Pos)) + if (vp.Pos.Z >= rover->bottom.plane->ZatPoint(vp.Pos)) { SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->Colormap.FadeColor; @@ -483,7 +484,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) double ff_top = rover->top.plane->ZatPoint(sector->centerspot); if (ff_top > lastfloorheight) { - if (r_viewpoint.Pos.Z >= rover->top.plane->ZatPoint(r_viewpoint.Pos)) + if (vp.Pos.Z >= rover->top.plane->ZatPoint(vp.Pos)) { SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); Colormap.FadeColor = frontsector->Colormap.FadeColor; From 12ad0caec058f8ebaa46b53d8f8782993cf63079 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:33:59 +0200 Subject: [PATCH 10/58] - small optimization --- src/hwrenderer/scene/hw_sky.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hwrenderer/scene/hw_sky.cpp b/src/hwrenderer/scene/hw_sky.cpp index 24bd48e93..666aeecde 100644 --- a/src/hwrenderer/scene/hw_sky.cpp +++ b/src/hwrenderer/scene/hw_sky.cpp @@ -154,8 +154,9 @@ void GLWall::SkyPlane(HWDrawInfo *di, sector_t *sector, int plane, bool allowref } else if (allowreflect && sector->GetReflect(plane) > 0) { - if ((plane == sector_t::ceiling && r_viewpoint.Pos.Z > sector->ceilingplane.fD()) || - (plane == sector_t::floor && r_viewpoint.Pos.Z < -sector->floorplane.fD())) return; + auto vpz = r_viewpoint.Pos.Z; + if ((plane == sector_t::ceiling && vpz > sector->ceilingplane.fD()) || + (plane == sector_t::floor && vpz < -sector->floorplane.fD())) return; ptype = PORTALTYPE_PLANEMIRROR; planemirror = plane == sector_t::ceiling ? §or->ceilingplane : §or->floorplane; } From ae7053ee985faf6e51c21600e13d5c4d7c94fcce Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:40:50 +0200 Subject: [PATCH 11/58] - another small optimization --- src/hwrenderer/scene/hw_walls.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index f8ed6d30d..3c676f71d 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -444,10 +444,11 @@ bool GLWall::DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1, ztop[1] = ztop[0] = fs->GetPlaneTexZ(sector_t::ceiling); zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); - if (r_viewpoint.Pos.Z < fs->GetPlaneTexZ(sector_t::ceiling)) + auto vpz = r_viewpoint.Pos.Z; + if (vpz < fs->GetPlaneTexZ(sector_t::ceiling)) { - if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor)) - zbottom[1] = zbottom[0] = r_viewpoint.Pos.Z; + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) + zbottom[1] = zbottom[0] = vpz; if (fs->GetTexture(sector_t::ceiling) == skyflatnum) { @@ -475,7 +476,7 @@ bool GLWall::DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1, ztop[1] = ztop[0] = zbottom[0]; } - if (r_viewpoint.Pos.Z > fs->GetPlaneTexZ(sector_t::floor)) + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) { zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); if (fs->GetTexture(sector_t::floor) == skyflatnum) From 86975a71e8b4e7da68265d2841f9d838d22f7ab5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 10:50:48 +0200 Subject: [PATCH 12/58] - don't use r_viewpoint directly when sorting translucent geometry. Better store the view Z in a member variable and use that. --- src/hwrenderer/scene/hw_drawlist.cpp | 7 ++++--- src/hwrenderer/scene/hw_drawlist.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hwrenderer/scene/hw_drawlist.cpp b/src/hwrenderer/scene/hw_drawlist.cpp index 4d70e2f82..1df8d5763 100644 --- a/src/hwrenderer/scene/hw_drawlist.cpp +++ b/src/hwrenderer/scene/hw_drawlist.cpp @@ -121,7 +121,7 @@ inline void SortNode::AddToEqual(SortNode *child) { child->UnlinkFromChain(); child->equal=equal; - equal=child; + equal=child; } //========================================================================== @@ -269,7 +269,7 @@ void HWDrawList::SortWallIntoPlane(SortNode * head, SortNode * sort) GLFlat * fh = flats[drawitems[head->itemindex].index]; GLWall * ws = walls[drawitems[sort->itemindex].index]; - bool ceiling = fh->z > r_viewpoint.Pos.Z; + bool ceiling = fh->z > SortZ; if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z)) { @@ -327,7 +327,7 @@ void HWDrawList::SortSpriteIntoPlane(SortNode * head, SortNode * sort) GLFlat * fh = flats[drawitems[head->itemindex].index]; GLSprite * ss = sprites[drawitems[sort->itemindex].index]; - bool ceiling = fh->z > r_viewpoint.Pos.Z; + bool ceiling = fh->z > SortZ; auto hiz = ss->z1 > ss->z2 ? ss->z1 : ss->z2; auto loz = ss->z1 < ss->z2 ? ss->z1 : ss->z2; @@ -688,6 +688,7 @@ SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head) //========================================================================== void HWDrawList::Sort(HWDrawInfo *di) { + SortZ = r_viewpoint.Pos.Z; MakeSortList(); sorted = DoSort(di, SortNodes[SortNodeStart]); } diff --git a/src/hwrenderer/scene/hw_drawlist.h b/src/hwrenderer/scene/hw_drawlist.h index 5e440df81..22bf1cb31 100644 --- a/src/hwrenderer/scene/hw_drawlist.h +++ b/src/hwrenderer/scene/hw_drawlist.h @@ -61,6 +61,7 @@ struct HWDrawList TArray sprites; TArray drawitems; int SortNodeStart; + float SortZ; SortNode * sorted; public: From 548ccb1df446a49e4fa58dabbe49a4563e6bb7d2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 11:20:36 +0200 Subject: [PATCH 13/58] - removed some references to r_viewpoint in game code Mainly to reduce number of search results. --- src/am_map.cpp | 7 ++++--- src/d_main.cpp | 11 ++++++----- src/events.cpp | 13 +++++++------ src/posix/sdl/i_main.cpp | 9 +++++---- src/v_draw.cpp | 9 +++++---- src/win32/i_main.cpp | 9 +++++---- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 20f99de36..e0296e797 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2123,13 +2123,14 @@ void AM_drawSubsectors() double secx; double secy; double seczb, seczt; - double cmpz = r_viewpoint.Pos.Z; + auto &vp = r_viewpoint; + double cmpz = vp.Pos.Z; if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector) { // For the actual camera sector use the current viewpoint as reference. - secx = r_viewpoint.Pos.X; - secy = r_viewpoint.Pos.Y; + secx = vp.Pos.X; + secy = vp.Pos.Y; } else { diff --git a/src/d_main.cpp b/src/d_main.cpp index c507ecc57..4172909bc 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -679,6 +679,7 @@ void D_Display () players[consoleplayer].camera = players[consoleplayer].mo; } + auto &vp = r_viewpoint; if (viewactive) { DAngle fov = 90.f; @@ -689,7 +690,7 @@ void D_Display () fov = cam->player->FOV; else fov = cam->CameraFOV; } - R_SetFOV(r_viewpoint, fov); + R_SetFOV(vp, fov); } // [RH] change the screen mode if needed @@ -718,7 +719,7 @@ void D_Display () // change the view size if needed if (setsizeneeded && StatusBar != NULL) { - R_ExecuteSetViewSize (r_viewpoint, r_viewwindow); + R_ExecuteSetViewSize (vp, r_viewwindow); } setmodeneeded = false; @@ -817,7 +818,7 @@ void D_Display () { StatusBar->DrawCrosshair(); } - StatusBar->CallDraw (HUD_AltHud, r_viewpoint.TicFrac); + StatusBar->CallDraw (HUD_AltHud, vp.TicFrac); StatusBar->DrawTopStuff (HUD_AltHud); } else @@ -825,13 +826,13 @@ void D_Display () { EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None; StatusBar->DrawBottomStuff (state); - StatusBar->CallDraw (state, r_viewpoint.TicFrac); + StatusBar->CallDraw (state, vp.TicFrac); StatusBar->DrawTopStuff (state); } else { StatusBar->DrawBottomStuff (HUD_StatusBar); - StatusBar->CallDraw (HUD_StatusBar, r_viewpoint.TicFrac); + StatusBar->CallDraw (HUD_StatusBar, vp.TicFrac); StatusBar->DrawTopStuff (HUD_StatusBar); } //stb.Unclock(); diff --git a/src/events.cpp b/src/events.cpp index c520359a5..eabf1b931 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -857,12 +857,13 @@ void DStaticEventHandler::WorldTick() static FRenderEvent E_SetupRenderEvent() { FRenderEvent e; - e.ViewPos = r_viewpoint.Pos; - e.ViewAngle = r_viewpoint.Angles.Yaw; - e.ViewPitch = r_viewpoint.Angles.Pitch; - e.ViewRoll = r_viewpoint.Angles.Roll; - e.FracTic = r_viewpoint.TicFrac; - e.Camera = r_viewpoint.camera; + auto &vp = r_viewpoint; + e.ViewPos = vp.Pos; + e.ViewAngle = vp.Angles.Yaw; + e.ViewPitch = vp.Angles.Pitch; + e.ViewRoll = vp.Angles.Roll; + e.FracTic = vp.TicFrac; + e.Camera = vp.camera; return e; } diff --git a/src/posix/sdl/i_main.cpp b/src/posix/sdl/i_main.cpp index 127198df8..38f035515 100644 --- a/src/posix/sdl/i_main.cpp +++ b/src/posix/sdl/i_main.cpp @@ -169,10 +169,11 @@ static int DoomSpecificInfo (char *buffer, char *end) } else { - p += snprintf (buffer+p, size-p, "\n\nviewx = %f", r_viewpoint.Pos.X); - p += snprintf (buffer+p, size-p, "\nviewy = %f", r_viewpoint.Pos.Y); - p += snprintf (buffer+p, size-p, "\nviewz = %f", r_viewpoint.Pos.Z); - p += snprintf (buffer+p, size-p, "\nviewangle = %f", r_viewpoint.Angles.Yaw.Degrees); + auto &vp = r_viewpoint; + p += snprintf (buffer+p, size-p, "\n\nviewx = %f", vp.Pos.X); + p += snprintf (buffer+p, size-p, "\nviewy = %f", vp.Pos.Y); + p += snprintf (buffer+p, size-p, "\nviewz = %f", vp.Pos.Z); + p += snprintf (buffer+p, size-p, "\nviewangle = %f", vp.Angles.Yaw.Degrees); } } buffer[p++] = '\n'; diff --git a/src/v_draw.cpp b/src/v_draw.cpp index f43bffd19..49e445995 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -1341,12 +1341,13 @@ void DFrameBuffer::DrawBlend(sector_t * viewsector) // don't draw sector based blends when any fullbright screen effect is active. if (!fullbright) { + const auto &vpp = r_viewpoint.Pos; if (!viewsector->e->XFloor.ffloors.Size()) { if (viewsector->GetHeightSec()) { auto s = viewsector->heightsec; - blendv = s->floorplane.PointOnSide(r_viewpoint.Pos) < 0 ? s->bottommap : s->ceilingplane.PointOnSide(r_viewpoint.Pos) < 0 ? s->topmap : s->midmap; + blendv = s->floorplane.PointOnSide(vpp) < 0 ? s->bottommap : s->ceilingplane.PointOnSide(vpp) < 0 ? s->topmap : s->midmap; } } else @@ -1357,11 +1358,11 @@ void DFrameBuffer::DrawBlend(sector_t * viewsector) { double lightbottom; if (i < lightlist.Size() - 1) - lightbottom = lightlist[i + 1].plane.ZatPoint(r_viewpoint.Pos); + lightbottom = lightlist[i + 1].plane.ZatPoint(vpp); else - lightbottom = viewsector->floorplane.ZatPoint(r_viewpoint.Pos); + lightbottom = viewsector->floorplane.ZatPoint(vpp); - if (lightbottom < r_viewpoint.Pos.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS))) + if (lightbottom < vpp.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS))) { // 3d floor 'fog' is rendered as a blending value blendv = lightlist[i].blend; diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 21b758094..b1d7a7bb7 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -1105,10 +1105,11 @@ void DoomSpecificInfo (char *buffer, size_t bufflen) } else { - buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %f", r_viewpoint.Pos.X); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %f", r_viewpoint.Pos.Y); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %f", r_viewpoint.Pos.Z); - buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %f", r_viewpoint.Angles.Yaw); + auto &vp = r_viewpoint; + buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %f", vp.Pos.X); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %f", vp.Pos.Y); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %f", vp.Pos.Z); + buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %f", vp.Angles.Yaw); } } *buffer++ = '\r'; From 22b8d0a8d7864e8c305e61c5a1139db164a51c86 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 11:21:13 +0200 Subject: [PATCH 14/58] - r_viewpoint reduction in hwrenderer --- src/hwrenderer/scene/hw_bsp.cpp | 5 +++-- src/hwrenderer/scene/hw_drawstructs.h | 2 +- src/hwrenderer/scene/hw_sprites.cpp | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 4393424c1..d44bf0679 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -353,6 +353,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector) SetupSprite.Clock(); sector_t * sec=sub->sector; // Handle all things in sector. + auto &vp = r_viewpoint; for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) { auto thing = p->m_thing; @@ -362,7 +363,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector) FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != nullptr && *cvar >= 0) { - double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); + double dist = (thing->Pos() - vp.Pos).LengthSquared(); double check = (double)**cvar; if (dist >= check * check) { @@ -383,7 +384,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector) FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != nullptr && *cvar >= 0) { - double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); + double dist = (thing->Pos() - vp.Pos).LengthSquared(); double check = (double)**cvar; if (dist >= check * check) { diff --git a/src/hwrenderer/scene/hw_drawstructs.h b/src/hwrenderer/scene/hw_drawstructs.h index 8dcc4632a..e98f4a389 100644 --- a/src/hwrenderer/scene/hw_drawstructs.h +++ b/src/hwrenderer/scene/hw_drawstructs.h @@ -383,7 +383,7 @@ public: void SplitSprite(HWDrawInfo *di, sector_t * frontsector, bool translucent); void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight); - bool CalculateVertices(HWDrawInfo *di, FVector3 *v); + bool CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp); public: diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index e5660c26b..882712145 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -68,7 +68,7 @@ EXTERN_CVAR(Float, transsouls) // //========================================================================== -bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v) +bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) { if (actor != nullptr && (actor->renderflags & RF_SPRITETYPEMASK) == RF_FLATSPRITE) { @@ -146,8 +146,8 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v) { // [CMB] Rotate relative to camera XY position, not just camera direction, // which is nicer in VR - float xrel = xcenter - r_viewpoint.Pos.X; - float yrel = ycenter - r_viewpoint.Pos.Y; + float xrel = xcenter - vp->X; + float yrel = ycenter - vp->Y; float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); float counterRotationDeg = 270. - di->mAngles.Yaw.Degrees; // counteracts existing sprite rotation float relAngleDeg = counterRotationDeg + absAngleDeg; From 2c655322c3aaa82a93c2c0a993aec518540a6b9d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 11:21:32 +0200 Subject: [PATCH 15/58] =?UTF-8?q?=E2=80=A6=20and=20in=20gl=5Fsprites.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gl/scene/gl_sprite.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index cc988ec73..cfdbbdae7 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -79,6 +79,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) bool additivefog = false; bool foglayer = false; int rel = sprite->fullbright? 0 : getExtraLight(); + auto &vp = r_viewpoint; if (pass==GLPASS_TRANSLUCENT) { @@ -112,7 +113,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) // fog + fuzz don't work well without some fiddling with the alpha value! if (!sprite->Colormap.FadeColor.isBlack()) { - float dist=Dist2(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, sprite->x, sprite->y); + float dist=Dist2(vp.Pos.X, vp.Pos.Y, sprite->x, sprite->y); int fogd = hw_GetFogDensity(sprite->lightlevel, sprite->Colormap.FadeColor, sprite->Colormap.FogDensity); // this value was determined by trial and error and is scale dependent! @@ -221,7 +222,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) secplane_t *lowplane = i == (*lightlist).Size() - 1 ? &bottomp : &(*lightlist)[i + 1].plane; int thislight = (*lightlist)[i].caster != nullptr ? hw_ClampLight(*(*lightlist)[i].p_lightlevel) : sprite->lightlevel; - int thisll = sprite->actor == nullptr? thislight : (uint8_t)sprite->actor->Sector->CheckSpriteGlow(thislight, sprite->actor->InterpolatedPosition(r_viewpoint.TicFrac)); + int thisll = sprite->actor == nullptr? thislight : (uint8_t)sprite->actor->Sector->CheckSpriteGlow(thislight, sprite->actor->InterpolatedPosition(vp.TicFrac)); FColormap thiscm; thiscm.CopyFog(sprite->Colormap); @@ -249,7 +250,8 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) FVector3 v[4]; gl_RenderState.SetNormal(0, 0, 0); - if (sprite->CalculateVertices(this, v)) + + if (sprite->CalculateVertices(this, v, &vp.Pos)) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -128.0f); @@ -277,7 +279,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) else { FGLModelRenderer renderer(sprite->dynlightindex); - renderer.RenderModel(sprite->x, sprite->y, sprite->z, sprite->modelframe, sprite->actor, r_viewpoint.TicFrac); + renderer.RenderModel(sprite->x, sprite->y, sprite->z, sprite->modelframe, sprite->actor, vp.TicFrac); } } From 753cd0aeded2412a39ace246fd4504cd28be1661 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 17:44:11 +0200 Subject: [PATCH 16/58] - made R_SetViewAngle a member of r_viewpoint --- src/gl/scene/gl_scene.cpp | 5 +++-- src/polyrenderer/scene/poly_portal.cpp | 8 ++++---- src/r_utility.cpp | 12 ++++++------ src/r_utility.h | 5 +++++ src/swrenderer/scene/r_portal.cpp | 4 ++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 95f915b1e..5afd63bad 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -148,12 +148,13 @@ void GLSceneDrawer::Set3DViewport(bool mainview) void GLSceneDrawer::SetViewAngle(DAngle viewangle) { + FRenderViewpoint &vp = r_viewpoint; GLRenderer->mAngles.Yaw = float(270.0-viewangle.Degrees); - DVector2 v = r_viewpoint.Angles.Yaw.ToVector(); + DVector2 v = vp.Angles.Yaw.ToVector(); GLRenderer->mViewVector.X = v.X; GLRenderer->mViewVector.Y = v.Y; - R_SetViewAngle(r_viewpoint, r_viewwindow); + vp.SetViewAngle(r_viewwindow); } diff --git a/src/polyrenderer/scene/poly_portal.cpp b/src/polyrenderer/scene/poly_portal.cpp index f85314564..a4a7ec467 100644 --- a/src/polyrenderer/scene/poly_portal.cpp +++ b/src/polyrenderer/scene/poly_portal.cpp @@ -88,7 +88,7 @@ void PolyDrawSectorPortal::SaveGlobals() viewpoint.camera = nullptr; viewpoint.sector = Portal->mDestination; - R_SetViewAngle(viewpoint, viewwindow); + viewpoint.SetViewAngle(viewwindow); Portal->mFlags |= PORTSF_INSKYBOX; if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX; @@ -114,7 +114,7 @@ void PolyDrawSectorPortal::RestoreGlobals() //PolyRenderer::Instance()->Light.SetVisibility(savedvisibility); - R_SetViewAngle(viewpoint, viewwindow); + viewpoint.SetViewAngle(viewwindow); } ///////////////////////////////////////////////////////////////////////////// @@ -236,7 +236,7 @@ void PolyDrawLinePortal::SaveGlobals() viewpoint.camera = nullptr; viewpoint.sector = R_PointInSubsector(viewpoint.Pos)->sector; - R_SetViewAngle(viewpoint, viewwindow); + viewpoint.SetViewAngle(viewwindow); } void PolyDrawLinePortal::RestoreGlobals() @@ -254,5 +254,5 @@ void PolyDrawLinePortal::RestoreGlobals() viewpoint.camera->renderflags &= ~RF_INVISIBLE; } - R_SetViewAngle(viewpoint, viewwindow); + viewpoint.SetViewAngle(viewwindow); } diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 127ac4597..82ab62f2b 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -602,13 +602,13 @@ void R_ResetViewInterpolation () // //========================================================================== -void R_SetViewAngle (FRenderViewpoint &viewpoint, const FViewWindow &viewwindow) +void FRenderViewpoint::SetViewAngle (const FViewWindow &viewwindow) { - viewpoint.Sin = viewpoint.Angles.Yaw.Sin(); - viewpoint.Cos = viewpoint.Angles.Yaw.Cos(); + Sin = Angles.Yaw.Sin(); + Cos = Angles.Yaw.Cos(); - viewpoint.TanSin = viewwindow.FocalTangent * viewpoint.Sin; - viewpoint.TanCos = viewwindow.FocalTangent * viewpoint.Cos; + TanSin = viewwindow.FocalTangent * Sin; + TanCos = viewwindow.FocalTangent * Cos; } //========================================================================== @@ -844,7 +844,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor } R_InterpolateView (viewpoint, player, viewpoint.TicFrac, iview); - R_SetViewAngle (viewpoint, viewwindow); + viewpoint.SetViewAngle (viewwindow); interpolator.DoInterpolations (viewpoint.TicFrac); diff --git a/src/r_utility.h b/src/r_utility.h index 387461426..122e6f079 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -5,6 +5,7 @@ #include "vectors.h" class FSerializer; +struct FViewWindow; // // Stuff from r_main.h that's needed outside the rendering code. @@ -36,6 +37,10 @@ struct FRenderViewpoint int extralight; // extralight to be added to this viewpoint bool showviewer; // show the camera actor? + + + void SetViewAngle(const FViewWindow &viewwindow); + }; extern FRenderViewpoint r_viewpoint; diff --git a/src/swrenderer/scene/r_portal.cpp b/src/swrenderer/scene/r_portal.cpp index 323480c32..b56ddc457 100644 --- a/src/swrenderer/scene/r_portal.cpp +++ b/src/swrenderer/scene/r_portal.cpp @@ -172,7 +172,7 @@ namespace swrenderer Thread->Viewport->viewpoint.camera = nullptr; Thread->Viewport->viewpoint.sector = port->mDestination; assert(Thread->Viewport->viewpoint.sector != nullptr); - R_SetViewAngle(Thread->Viewport->viewpoint, Thread->Viewport->viewwindow); + Thread->Viewport->viewpoint.SetViewAngle(Thread->Viewport->viewwindow); Thread->Viewport->SetupPolyViewport(Thread); Thread->OpaquePass->ClearSeenSprites(); Thread->Clip3D->ClearFakeFloors(); @@ -260,7 +260,7 @@ namespace swrenderer Thread->Light->SetVisibility(Thread->Viewport.get(), savedvisibility); Thread->Viewport->viewpoint.extralight = savedextralight; Thread->Viewport->viewpoint.Angles = savedangles; - R_SetViewAngle(Thread->Viewport->viewpoint, Thread->Viewport->viewwindow); + Thread->Viewport->viewpoint.SetViewAngle(Thread->Viewport->viewwindow); Thread->Viewport->SetupPolyViewport(Thread); CurrentPortalInSkybox = false; From 87dfa34f1feb0d2b9480be7a621ddd3ad1a38c76 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 17:55:42 +0200 Subject: [PATCH 17/58] - save r_viewpoint as a whole when processing portals. --- src/gl/renderer/gl_renderer.cpp | 1 - src/gl/renderer/gl_renderer.h | 1 - src/gl/scene/gl_portal.cpp | 26 +++++++------------------- src/gl/scene/gl_portal.h | 7 ++----- src/gl/scene/gl_scene.cpp | 5 ++--- src/hwrenderer/scene/hw_drawinfo.h | 1 - src/hwrenderer/scene/hw_sprites.cpp | 8 ++++---- src/r_utility.h | 1 + 8 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 81b9a401d..8d0a7c43e 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -90,7 +90,6 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mMirrorCount = 0; mPlaneMirrorCount = 0; mAngles = FRotator(0.f, 0.f, 0.f); - mViewVector = FVector2(0,0); mVBO = nullptr; mSkyVBO = nullptr; mShaderManager = nullptr; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index a537d15b5..24356c1ee 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -129,7 +129,6 @@ public: FShadowMap mShadowMap; FRotator mAngles; - FVector2 mViewVector; FFlatVertexBuffer *mVBO; FSkyVertexBuffer *mSkyVBO; diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index bb0d09adb..f2fd1a61e 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -266,13 +266,8 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo **pDi) } // save viewpoint - savedViewPos = r_viewpoint.Pos; - savedViewActorPos = r_viewpoint.ActorPos; - savedshowviewer = r_viewpoint.showviewer; - savedAngles = r_viewpoint.Angles; + savedviewpoint = r_viewpoint; savedviewactor=GLRenderer->mViewActor; - savedviewpath[0] = r_viewpoint.Path[0]; - savedviewpath[1] = r_viewpoint.Path[1]; savedvisibility = r_viewpoint.camera ? r_viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); @@ -286,7 +281,8 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo **pDi) inline void GLPortal::ClearClipper(FDrawInfo *di) { - DAngle angleOffset = deltaangle(savedAngles.Yaw, r_viewpoint.Angles.Yaw); + FRenderViewpoint &oldvp = savedviewpoint; + DAngle angleOffset = deltaangle(oldvp.Angles.Yaw, r_viewpoint.Angles.Yaw); di->mClipper->Clear(); @@ -294,8 +290,8 @@ inline void GLPortal::ClearClipper(FDrawInfo *di) di->mClipper->SafeAddClipRange(0,0xffffffff); for (unsigned int i = 0; i < lines.Size(); i++) { - DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - savedViewPos).Angle() + angleOffset; - DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - savedViewPos).Angle() + angleOffset; + DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - oldvp.Pos).Angle() + angleOffset; + DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - oldvp.Pos).Angle() + angleOffset; if (deltaangle(endAngle, startAngle) < 0) { @@ -329,12 +325,7 @@ void GLPortal::End(bool usestencil) if (needdepth) FDrawInfo::EndDrawInfo(); // Restore the old view - r_viewpoint.Path[0] = savedviewpath[0]; - r_viewpoint.Path[1] = savedviewpath[1]; - r_viewpoint.Pos = savedViewPos; - r_viewpoint.showviewer = savedshowviewer; - r_viewpoint.ActorPos = savedViewActorPos; - r_viewpoint.Angles = savedAngles; + r_viewpoint = savedviewpoint; GLRenderer->mViewActor=savedviewactor; if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags = (r_viewpoint.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); @@ -389,10 +380,7 @@ void GLPortal::End(bool usestencil) glDepthMask(true); } // Restore the old view - r_viewpoint.showviewer = savedshowviewer; - r_viewpoint.ActorPos = savedViewActorPos; - r_viewpoint.Pos = savedViewPos; - r_viewpoint.Angles = savedAngles; + r_viewpoint = savedviewpoint; GLRenderer->mViewActor=savedviewactor; if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags |= savedvisibility; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index 5f646f41c..7f219541a 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -37,6 +37,7 @@ #define __GL_PORTAL_H #include "tarray.h" +#include "r_utility.h" #include "actor.h" #include "gl/renderer/gl_renderer.h" #include "gl/scene/gl_drawinfo.h" @@ -69,13 +70,9 @@ public: private: void DrawPortalStencil(); - DVector3 savedviewpath[2]; - DVector3 savedViewPos; - DVector3 savedViewActorPos; - DRotator savedAngles; - bool savedshowviewer; AActor * savedviewactor; ActorRenderFlags savedvisibility; + FRenderViewpoint savedviewpoint; GLPortal *PrevPortal; TArray mPrimIndices; diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 5afd63bad..b822c32a7 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -151,8 +151,8 @@ void GLSceneDrawer::SetViewAngle(DAngle viewangle) FRenderViewpoint &vp = r_viewpoint; GLRenderer->mAngles.Yaw = float(270.0-viewangle.Degrees); DVector2 v = vp.Angles.Yaw.ToVector(); - GLRenderer->mViewVector.X = v.X; - GLRenderer->mViewVector.Y = v.Y; + vp.ViewVector.X = v.X; + vp.ViewVector.Y = v.Y; vp.SetViewAngle(r_viewwindow); } @@ -236,7 +236,6 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) validcount++; // used for processing sidedefs only once by the renderer. di->mAngles = GLRenderer->mAngles; - di->mViewVector = GLRenderer->mViewVector; di->mViewActor = GLRenderer->mViewActor; di->mShadowMap = &GLRenderer->mShadowMap; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 8bec0df27..9eff4ec28 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -84,7 +84,6 @@ struct HWDrawInfo std::atomic spriteindex; IPortal *mClipPortal; FRotator mAngles; - FVector2 mViewVector; AActor *mViewActor; IShadowMap *mShadowMap; Clipper *mClipper; diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index 882712145..29b522c0e 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -597,8 +597,8 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t switch (spritetype) { case RF_FACESPRITE: - viewvecX = di->mViewVector.X; - viewvecY = di->mViewVector.Y; + viewvecX = vp.ViewVector.X; + viewvecY = vp.ViewVector.Y; x1 = x - viewvecY*leftfac; x2 = x - viewvecY*rightfac; @@ -946,8 +946,8 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t * else factor = 1 / 7.f; float scalefac=particle->size * factor; - float viewvecX = di->mViewVector.X; - float viewvecY = di->mViewVector.Y; + float viewvecX = vp.ViewVector.X; + float viewvecY = vp.ViewVector.Y; x1=x+viewvecY*scalefac; x2=x-viewvecY*scalefac; diff --git a/src/r_utility.h b/src/r_utility.h index 122e6f079..ef093c8ed 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -21,6 +21,7 @@ struct FRenderViewpoint DVector3 Pos; // Camera position DVector3 ActorPos; // Camera actor's position DRotator Angles; // Camera angles + DVector2 ViewVector; // HWR only: direction the camera is facing. DVector3 Path[2]; // View path for portal calculations double Cos; // cos(Angles.Yaw) From eddb179e36ea8271ed978b6e77c22ffc51df601f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 18:51:46 +0200 Subject: [PATCH 18/58] - moved ViewActor variable into FRenderViewpoint. --- src/gl/renderer/gl_renderer.h | 1 - src/gl/scene/gl_portal.cpp | 31 +++++++++++++---------------- src/gl/scene/gl_scene.cpp | 9 ++++----- src/hwrenderer/scene/hw_drawinfo.h | 1 - src/hwrenderer/scene/hw_sprites.cpp | 2 +- src/r_utility.h | 1 + 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 24356c1ee..a66d5ced2 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -93,7 +93,6 @@ public: int mMirrorCount; int mPlaneMirrorCount; float mCurrentFoV; - AActor *mViewActor; FShaderManager *mShaderManager; FSamplerManager *mSamplerManager; unsigned int mFBID; diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index f2fd1a61e..42cc61ab7 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -267,14 +267,13 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo **pDi) // save viewpoint savedviewpoint = r_viewpoint; - savedviewactor=GLRenderer->mViewActor; savedvisibility = r_viewpoint.camera ? r_viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); PrevPortal = GLRenderer->mCurrentPortal; GLRenderer->mCurrentPortal = this; - if (PrevPortal != NULL) PrevPortal->PushState(); + if (PrevPortal != nullptr) PrevPortal->PushState(); return true; } @@ -317,7 +316,7 @@ void GLPortal::End(bool usestencil) bool needdepth = NeedDepthBuffer(); Clocker c(PortalAll); - if (PrevPortal != NULL) PrevPortal->PopState(); + if (PrevPortal != nullptr) PrevPortal->PopState(); GLRenderer->mCurrentPortal = PrevPortal; if (usestencil) @@ -326,7 +325,6 @@ void GLPortal::End(bool usestencil) // Restore the old view r_viewpoint = savedviewpoint; - GLRenderer->mViewActor=savedviewactor; if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags = (r_viewpoint.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); @@ -381,7 +379,6 @@ void GLPortal::End(bool usestencil) } // Restore the old view r_viewpoint = savedviewpoint; - GLRenderer->mViewActor=savedviewactor; if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags |= savedvisibility; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); @@ -458,7 +455,7 @@ void GLPortal::EndFrame() // Only use occlusion query if there are more than 2 portals. // Otherwise there's too much overhead. - // (And don't forget to consider the separating NULL pointers!) + // (And don't forget to consider the separating nullptr pointers!) bool usequery = portals.Size() > 2 + (unsigned)renderdepth; while (portals.Pop(p) && p) @@ -494,13 +491,13 @@ void GLPortal::EndFrame() bool GLPortal::RenderFirstSkyPortal(int recursion) { GLPortal * p; - GLPortal * best = NULL; + GLPortal * best = nullptr; unsigned bestindex=0; // Find the one with the highest amount of lines. // Normally this is also the one that saves the largest amount // of time by drawing it before the scene itself. - for(int i = portals.Size()-1; i >= 0 && portals[i] != NULL; --i) + for(int i = portals.Size()-1; i >= 0 && portals[i] != nullptr; --i) { p=portals[i]; if (p->lines.Size() > 0 && p->IsSky()) @@ -538,7 +535,7 @@ GLPortal * GLPortal::FindPortal(const void * src) int i=portals.Size()-1; while (i>=0 && portals[i] && portals[i]->GetSource()!=src) i--; - return i>=0? portals[i]:NULL; + return i>=0? portals[i]:nullptr; } @@ -587,7 +584,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) if (r_viewpoint.Pos.Z < floorh + 4) r_viewpoint.Pos.Z = floorh + 4; if (r_viewpoint.Pos.Z > ceilh - 4) r_viewpoint.Pos.Z = ceilh - 4; - GLRenderer->mViewActor = origin; + r_viewpoint.ViewActor = origin; inskybox = true; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); @@ -625,7 +622,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) GLSectorStackPortal *FSectorPortalGroup::GetRenderState() { - if (glportal == NULL) glportal = new GLSectorStackPortal(this); + if (glportal == nullptr) glportal = new GLSectorStackPortal(this); return glportal; } @@ -633,9 +630,9 @@ GLSectorStackPortal *FSectorPortalGroup::GetRenderState() GLSectorStackPortal::~GLSectorStackPortal() { - if (origin != NULL && origin->glportal == this) + if (origin != nullptr && origin->glportal == this) { - origin->glportal = NULL; + origin->glportal = nullptr; } } @@ -692,7 +689,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) r_viewpoint.Pos += origin->mDisplacement; r_viewpoint.ActorPos += origin->mDisplacement; - GLRenderer->mViewActor = NULL; + r_viewpoint.ViewActor = nullptr; // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; @@ -748,7 +745,7 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) double planez = origin->ZatPoint(r_viewpoint.Pos); r_viewpoint.Pos.Z = 2 * planez - r_viewpoint.Pos.Z; - GLRenderer->mViewActor = NULL; + r_viewpoint.ViewActor = nullptr; PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; @@ -919,7 +916,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) } r_viewpoint.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; - GLRenderer->mViewActor = NULL; + r_viewpoint.ViewActor = nullptr; MirrorFlag++; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); @@ -998,7 +995,7 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) di->CurrentMapSections.Set(sub->mapsection); } - GLRenderer->mViewActor = nullptr; + r_viewpoint.ViewActor = nullptr; drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b822c32a7..c64f0cc27 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -236,7 +236,6 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) validcount++; // used for processing sidedefs only once by the renderer. di->mAngles = GLRenderer->mAngles; - di->mViewActor = GLRenderer->mViewActor; di->mShadowMap = &GLRenderer->mShadowMap; di->RenderBSPNode (level.HeadNode()); @@ -633,14 +632,14 @@ sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, IntRect * bounds, fl GLRenderer->mAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); GLRenderer->mAngles.Roll.Degrees = r_viewpoint.Angles.Roll.Degrees; - if (camera->player && camera->player-players==consoleplayer && - ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera==camera->player->mo) + if (camera->player && camera->player - players == consoleplayer && + ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera == camera->player->mo) { - GLRenderer->mViewActor=NULL; + r_viewpoint.ViewActor = nullptr; } else { - GLRenderer->mViewActor=camera; + r_viewpoint.ViewActor = camera; } // 'viewsector' will not survive the rendering so it cannot be used anymore below. diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 9eff4ec28..e30beeec6 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -84,7 +84,6 @@ struct HWDrawInfo std::atomic spriteindex; IPortal *mClipPortal; FRotator mAngles; - AActor *mViewActor; IShadowMap *mShadowMap; Clipper *mClipper; diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index 29b522c0e..d1595756a 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -461,7 +461,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t // don't draw first frame of a player missile if (thing->flags&MF_MISSILE) { - if (!(thing->flags7 & MF7_FLYCHEAT) && thing->target == di->mViewActor && di->mViewActor != nullptr) + if (!(thing->flags7 & MF7_FLYCHEAT) && thing->target == vp.ViewActor && vp.ViewActor != nullptr) { double speed = thing->Vel.Length(); if (speed >= thing->target->radius / 2) diff --git a/src/r_utility.h b/src/r_utility.h index ef093c8ed..35cedde8f 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -22,6 +22,7 @@ struct FRenderViewpoint DVector3 ActorPos; // Camera actor's position DRotator Angles; // Camera angles DVector2 ViewVector; // HWR only: direction the camera is facing. + AActor *ViewActor; // either the same as camera or nullptr DVector3 Path[2]; // View path for portal calculations double Cos; // cos(Angles.Yaw) From 54970b60e80c5ec87bd62cc28497b6bd2f3e1e94 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 23:16:22 +0200 Subject: [PATCH 19/58] - use locally stored viewpoint variables in the hardware renderer. - move a few variables from SceneDrawer to FRenderViewpoint. The global r_viewpoint variable is left alone now to always represent the current viewpoint to the play code. The main reason behind this change is to reduce the amount of global variables being used by the hardware renderer's scene processing code. --- src/gl/renderer/gl_renderer.cpp | 6 +- src/gl/renderer/gl_renderer.h | 3 +- src/gl/scene/gl_drawinfo.cpp | 20 +-- src/gl/scene/gl_drawinfo.h | 4 +- src/gl/scene/gl_portal.cpp | 168 ++++++++++++------------ src/gl/scene/gl_portal.h | 9 +- src/gl/scene/gl_scene.cpp | 117 ++++++----------- src/gl/scene/gl_scenedrawer.h | 9 +- src/gl/scene/gl_skydome.cpp | 3 +- src/gl/scene/gl_sprite.cpp | 2 +- src/gl/scene/gl_walls_draw.cpp | 2 +- src/hwrenderer/scene/hw_bsp.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.h | 5 +- src/hwrenderer/scene/hw_drawlist.cpp | 2 +- src/hwrenderer/scene/hw_fakeflat.cpp | 25 +++- src/hwrenderer/scene/hw_flats.cpp | 2 +- src/hwrenderer/scene/hw_portal.h | 2 +- src/hwrenderer/scene/hw_renderhacks.cpp | 14 +- src/hwrenderer/scene/hw_sky.cpp | 4 +- src/hwrenderer/scene/hw_skydome.cpp | 2 +- src/hwrenderer/scene/hw_sprites.cpp | 15 ++- src/hwrenderer/scene/hw_walls.cpp | 4 +- src/hwrenderer/scene/hw_weapon.cpp | 4 +- src/r_utility.cpp | 9 +- src/r_utility.h | 1 + src/vectors.h | 2 +- 26 files changed, 221 insertions(+), 217 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 73563d64b..c438f0934 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -89,7 +89,6 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mCurrentPortal = nullptr; mMirrorCount = 0; mPlaneMirrorCount = 0; - mAngles = FRotator(0.f, 0.f, 0.f); mVBO = nullptr; mSkyVBO = nullptr; mShaderManager = nullptr; @@ -326,7 +325,7 @@ sector_t *FGLRenderer::RenderView(player_t* player) GLSceneDrawer drawer; mShadowMap.Update(); - retsec = drawer.RenderViewpoint(player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); + retsec = drawer.RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); } All.Unclock(); return retsec; @@ -354,7 +353,8 @@ void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, doub bounds.height = FHardwareTexture::GetTexDimension(gltex->GetHeight()); GLSceneDrawer drawer; - drawer.RenderViewpoint(Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false); + FRenderViewpoint texvp; + drawer.RenderViewpoint(texvp, Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false); EndOffscreen(); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index e74ea4d6c..29e4e081d 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -69,7 +69,6 @@ public: GLPortal *mCurrentPortal; int mMirrorCount; int mPlaneMirrorCount; - float mCurrentFoV; FShaderManager *mShaderManager; FSamplerManager *mSamplerManager; unsigned int mFBID; @@ -104,7 +103,7 @@ public: FShadowMap mShadowMap; - FRotator mAngles; + //FRotator mAngles; FFlatVertexBuffer *mVBO; FSkyVertexBuffer *mSkyVBO; diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index efab1d84f..60991e2fb 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -63,7 +63,7 @@ void FDrawInfo::DoDrawSorted(HWDrawList *dl, SortNode * head) if (dl->drawitems[head->itemindex].rendertype == GLDIT_FLAT) { z = dl->flats[dl->drawitems[head->itemindex].index]->z; - relation = z > r_viewpoint.Pos.Z ? 1 : -1; + relation = z > Viewpoint.Pos.Z ? 1 : -1; } @@ -190,13 +190,14 @@ FDrawInfo::~FDrawInfo() // OpenGL has no use for multiple clippers so use the same one for all DrawInfos. static Clipper staticClipper; -FDrawInfo *FDrawInfo::StartDrawInfo(GLSceneDrawer *drawer) +FDrawInfo *FDrawInfo::StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp) { FDrawInfo *di=di_list.GetNew(); di->mDrawer = drawer; di->mVBO = GLRenderer->mVBO; di->mClipper = &staticClipper; - di->mClipper->SetViewpoint(r_viewpoint); + di->Viewpoint = parentvp; + di->mClipper->SetViewpoint(di->Viewpoint); staticClipper.Clear(); di->StartScene(); return di; @@ -224,7 +225,7 @@ void FDrawInfo::StartScene() // // //========================================================================== -void FDrawInfo::EndDrawInfo() +FDrawInfo *FDrawInfo::EndDrawInfo() { FDrawInfo * di = gl_drawinfo; @@ -233,6 +234,7 @@ void FDrawInfo::EndDrawInfo() di_list.Release(di); if (gl_drawinfo == nullptr) ResetRenderDataAllocator(); + return gl_drawinfo; } @@ -342,9 +344,9 @@ void FDrawInfo::DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, boo SetFog(lightlevel, rel, &Colormap, false); gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false); - float fviewx = r_viewpoint.Pos.X; - float fviewy = r_viewpoint.Pos.Y; - float fviewz = r_viewpoint.Pos.Z; + float fviewx = Viewpoint.Pos.X; + float fviewy = Viewpoint.Pos.Y; + float fviewz = Viewpoint.Pos.Z; gl_RenderState.SetPlaneTextureRotation(&plane, gltexture); gl_RenderState.Apply(); @@ -395,7 +397,7 @@ void FDrawInfo::FloodUpperGap(seg_t * seg) double frontz = fakefsector->ceilingplane.ZatPoint(seg->v1); if (fakebsector->GetTexture(sector_t::ceiling)==skyflatnum) return; - if (backz < r_viewpoint.Pos.Z) return; + if (backz < Viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { @@ -448,7 +450,7 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return; - if (fakebsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) return; + if (fakebsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index f069f2736..e732c8c8f 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -106,8 +106,8 @@ struct FDrawInfo : public HWDrawInfo void ProcessLowerMinisegs(TArray &lowersegs) override; void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override; - static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer); - static void EndDrawInfo(); + static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp); + FDrawInfo *EndDrawInfo(); gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector) { diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index a3e457ceb..afdfc3c14 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -228,7 +228,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn return false; } } - *pDi = FDrawInfo::StartDrawInfo(drawer); + *pDi = FDrawInfo::StartDrawInfo(drawer, outer_di->Viewpoint); } else { @@ -257,7 +257,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn { if (NeedDepthBuffer()) { - *pDi = FDrawInfo::StartDrawInfo(drawer); + *pDi = FDrawInfo::StartDrawInfo(drawer, outer_di->Viewpoint); } else { @@ -268,8 +268,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn } // save viewpoint - savedviewpoint = r_viewpoint; - savedvisibility = r_viewpoint.camera ? r_viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); + savedvisibility = outer_di->Viewpoint.camera ? outer_di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); PrevPortal = GLRenderer->mCurrentPortal; @@ -282,8 +281,8 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn inline void GLPortal::ClearClipper(FDrawInfo *di) { - FRenderViewpoint &oldvp = savedviewpoint; - DAngle angleOffset = deltaangle(oldvp.Angles.Yaw, r_viewpoint.Angles.Yaw); + auto outer_di = di->next; + DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); di->mClipper->Clear(); @@ -291,8 +290,8 @@ inline void GLPortal::ClearClipper(FDrawInfo *di) di->mClipper->SafeAddClipRange(0,0xffffffff); for (unsigned int i = 0; i < lines.Size(); i++) { - DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - oldvp.Pos).Angle() + angleOffset; - DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - oldvp.Pos).Angle() + angleOffset; + DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; + DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset; if (deltaangle(endAngle, startAngle) < 0) { @@ -301,8 +300,8 @@ inline void GLPortal::ClearClipper(FDrawInfo *di) } // and finally clip it to the visible area - angle_t a1 = drawer->FrustumAngle(); - if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs() + a1, r_viewpoint.Angles.Yaw.BAMs() - a1); + angle_t a1 = di->FrustumAngle(); + if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); // lock the parts that have just been clipped out. di->mClipper->SetSilhouette(); @@ -313,7 +312,7 @@ inline void GLPortal::ClearClipper(FDrawInfo *di) // End // //----------------------------------------------------------------------------- -void GLPortal::End(bool usestencil) +void GLPortal::End(FDrawInfo *di, bool usestencil) { bool needdepth = NeedDepthBuffer(); @@ -323,12 +322,12 @@ void GLPortal::End(bool usestencil) if (usestencil) { - if (needdepth) FDrawInfo::EndDrawInfo(); + if (needdepth) di = di->EndDrawInfo(); + auto &vp = di->Viewpoint; // Restore the old view - r_viewpoint = savedviewpoint; - if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags = (r_viewpoint.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -371,7 +370,7 @@ void GLPortal::End(bool usestencil) { if (needdepth) { - FDrawInfo::EndDrawInfo(); + di = di->EndDrawInfo(); glClear(GL_DEPTH_BUFFER_BIT); } else @@ -379,10 +378,11 @@ void GLPortal::End(bool usestencil) glEnable(GL_DEPTH_TEST); glDepthMask(true); } + auto &vp = di->Viewpoint; + // Restore the old view - r_viewpoint = savedviewpoint; - if (r_viewpoint.camera != nullptr) r_viewpoint.camera->renderflags |= savedvisibility; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -560,36 +560,36 @@ GLPortal * GLPortal::FindPortal(const void * src) void GLSkyboxPortal::DrawContents(FDrawInfo *di) { int old_pm = PlaneMirrorMode; - int saved_extralight = r_viewpoint.extralight; if (skyboxrecursion >= 3) { ClearScreen(); return; } + auto &vp = di->Viewpoint; skyboxrecursion++; AActor *origin = portal->mSkybox; portal->mFlags |= PORTSF_INSKYBOX; - r_viewpoint.extralight = 0; + vp.extralight = 0; PlaneMirrorMode = 0; bool oldclamp = gl_RenderState.SetDepthClamp(false); - r_viewpoint.Pos = origin->InterpolatedPosition(r_viewpoint.TicFrac); - r_viewpoint.ActorPos = origin->Pos(); - r_viewpoint.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * r_viewpoint.TicFrac); + vp.Pos = origin->InterpolatedPosition(vp.TicFrac); + vp.ActorPos = origin->Pos(); + vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); // Don't let the viewpoint be too close to a floor or ceiling double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); - if (r_viewpoint.Pos.Z < floorh + 4) r_viewpoint.Pos.Z = floorh + 4; - if (r_viewpoint.Pos.Z > ceilh - 4) r_viewpoint.Pos.Z = ceilh - 4; + if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; + if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; - r_viewpoint.ViewActor = origin; + vp.ViewActor = origin; inskybox = true; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); di->SetViewArea(); ClearClipper(di); @@ -602,7 +602,6 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) skyboxrecursion--; PlaneMirrorMode = old_pm; - r_viewpoint.extralight = saved_extralight; } //----------------------------------------------------------------------------- @@ -688,21 +687,22 @@ void GLSectorStackPortal::SetupCoverage(FDrawInfo *di) void GLSectorStackPortal::DrawContents(FDrawInfo *di) { FSectorPortalGroup *portal = origin; + auto &vp = di->Viewpoint; - r_viewpoint.Pos += origin->mDisplacement; - r_viewpoint.ActorPos += origin->mDisplacement; - r_viewpoint.ViewActor = nullptr; + vp.Pos += origin->mDisplacement; + vp.ActorPos += origin->mDisplacement; + vp.ViewActor = nullptr; // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); SetupCoverage(di); ClearClipper(di); // If the viewpoint is not within the portal, we need to invalidate the entire clip area. // The portal will re-validate the necessary parts when its subsectors get traversed. - subsector_t *sub = R_PointInSubsector(r_viewpoint.Pos); + subsector_t *sub = R_PointInSubsector(vp.Pos); if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) { di->mClipper->SafeAddClipRange(0, ANGLE_MAX); @@ -740,18 +740,19 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); + auto &vp = di->Viewpoint; int old_pm = PlaneMirrorMode; // the player is always visible in a mirror. - r_viewpoint.showviewer = true; + vp.showviewer = true; - double planez = origin->ZatPoint(r_viewpoint.Pos); - r_viewpoint.Pos.Z = 2 * planez - r_viewpoint.Pos.Z; - r_viewpoint.ViewActor = nullptr; + double planez = origin->ZatPoint(vp.Pos); + vp.Pos.Z = 2 * planez - vp.Pos.Z; + vp.ViewActor = nullptr; PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); @@ -808,14 +809,14 @@ void GLLinePortal::PopState() gl_RenderState.EnableClipLine(e != 0); } -int GLLinePortal::ClipSeg(seg_t *seg) +int GLLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) { line_t *linedef = seg->linedef; if (!linedef) { return PClip_Inside; // should be handled properly. } - return P_ClipLineToPortal(linedef, line(), r_viewpoint.Pos) ? PClip_InFront : PClip_Inside; + return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside; } int GLLinePortal::ClipSubsector(subsector_t *sub) @@ -861,35 +862,36 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) return; } + auto &vp = di->Viewpoint; di->UpdateCurrentMapSection(); di->mClipPortal = this; - DAngle StartAngle = r_viewpoint.Angles.Yaw; - DVector3 StartPos = r_viewpoint.Pos; + DAngle StartAngle = vp.Angles.Yaw; + DVector3 StartPos = vp.Pos; vertex_t *v1 = linedef->v1; vertex_t *v2 = linedef->v2; // the player is always visible in a mirror. - r_viewpoint.showviewer = true; + vp.showviewer = true; // Reflect the current view behind the mirror. if (linedef->Delta().X == 0) { // vertical mirror - r_viewpoint.Pos.X = 2 * v1->fX() - StartPos.X; + vp.Pos.X = 2 * v1->fX() - StartPos.X; // Compensation for reendering inaccuracies - if (StartPos.X < v1->fX()) r_viewpoint.Pos.X -= 0.1; - else r_viewpoint.Pos.X += 0.1; + if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1; + else vp.Pos.X += 0.1; } else if (linedef->Delta().Y == 0) { // horizontal mirror - r_viewpoint.Pos.Y = 2*v1->fY() - StartPos.Y; + vp.Pos.Y = 2*v1->fY() - StartPos.Y; // Compensation for reendering inaccuracies - if (StartPos.YfY()) r_viewpoint.Pos.Y -= 0.1; - else r_viewpoint.Pos.Y += 0.1; + if (StartPos.YfY()) vp.Pos.Y -= 0.1; + else vp.Pos.Y += 0.1; } else { @@ -906,27 +908,27 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) // the above two cases catch len == 0 double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - r_viewpoint.Pos.X = (x1 + r * dx)*2 - x; - r_viewpoint.Pos.Y = (y1 + r * dy)*2 - y; + vp.Pos.X = (x1 + r * dx)*2 - x; + vp.Pos.Y = (y1 + r * dy)*2 - y; // Compensation for reendering inaccuracies FVector2 v(-dx, dy); v.MakeUnit(); - r_viewpoint.Pos.X+= v[1] * renderdepth / 2; - r_viewpoint.Pos.Y+= v[0] * renderdepth / 2; + vp.Pos.X+= v[1] * renderdepth / 2; + vp.Pos.Y+= v[0] * renderdepth / 2; } - r_viewpoint.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; + vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; - r_viewpoint.ViewActor = nullptr; + vp.ViewActor = nullptr; MirrorFlag++; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); di->mClipper->Clear(); - angle_t af = drawer->FrustumAngle(); - if (afmClipper->SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs()+af, r_viewpoint.Angles.Yaw.BAMs()-af); + angle_t af = di->FrustumAngle(); + if (afmClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs()+af, vp.Angles.Yaw.BAMs()-af); di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); @@ -961,27 +963,27 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) ClearScreen(); return; } - + auto &vp = di->Viewpoint; di->mClipPortal = this; line_t *origin = glport->lines[0]->mOrigin; - P_TranslatePortalXY(origin, r_viewpoint.Pos.X, r_viewpoint.Pos.Y); - P_TranslatePortalXY(origin, r_viewpoint.ActorPos.X, r_viewpoint.ActorPos.Y); - P_TranslatePortalAngle(origin, r_viewpoint.Angles.Yaw); - P_TranslatePortalZ(origin, r_viewpoint.Pos.Z); - P_TranslatePortalXY(origin, r_viewpoint.Path[0].X, r_viewpoint.Path[0].Y); - P_TranslatePortalXY(origin, r_viewpoint.Path[1].X, r_viewpoint.Path[1].Y); - if (!r_viewpoint.showviewer && r_viewpoint.camera != nullptr && P_PointOnLineSidePrecise(r_viewpoint.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(r_viewpoint.Path[1], glport->lines[0]->mDestination)) + P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); + P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); + P_TranslatePortalAngle(origin, vp.Angles.Yaw); + P_TranslatePortalZ(origin, vp.Pos.Z); + P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); + P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); + if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) { - double distp = (r_viewpoint.Path[0] - r_viewpoint.Path[1]).Length(); + double distp = (vp.Path[0] - vp.Path[1]).Length(); if (distp > EQUAL_EPSILON) { - double dist1 = (r_viewpoint.Pos - r_viewpoint.Path[0]).Length(); - double dist2 = (r_viewpoint.Pos - r_viewpoint.Path[1]).Length(); + double dist1 = (vp.Pos - vp.Path[0]).Length(); + double dist2 = (vp.Pos - vp.Path[1]).Length(); if (dist1 + dist2 < distp + 1) { - r_viewpoint.camera->renderflags |= RF_MAYBEINVISIBLE; + vp.camera->renderflags |= RF_MAYBEINVISIBLE; } } } @@ -997,8 +999,8 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) di->CurrentMapSections.Set(sub->mapsection); } - r_viewpoint.ViewActor = nullptr; - drawer->SetupView(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, r_viewpoint.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + vp.ViewActor = nullptr; + drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); @@ -1034,16 +1036,16 @@ void GLLineToLinePortal::RenderAttached(FDrawInfo *di) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -GLHorizonPortal::GLHorizonPortal(GLHorizonInfo * pt, bool local) +GLHorizonPortal::GLHorizonPortal(GLHorizonInfo * pt, FRenderViewpoint &vp, bool local) : GLPortal(local) { origin = pt; // create the vertex data for this horizon portal. GLSectorPlane * sp = &origin->plane; - const float vx = r_viewpoint.Pos.X; - const float vy = r_viewpoint.Pos.Y; - const float vz = r_viewpoint.Pos.Z; + const float vx = vp.Pos.X; + const float vy = vp.Pos.Y; + const float vz = vp.Pos.Z; const float z = sp->Texheight; const float tz = (z - vz); @@ -1106,6 +1108,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) PalEntry color; player_t * player=&players[consoleplayer]; GLSectorPlane * sp = &origin->plane; + auto &vp = di->Viewpoint; gltexture=FMaterial::ValidateTexture(sp->texture, false, true); if (!gltexture) @@ -1113,7 +1116,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) ClearScreen(); return; } - gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); + gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); if (gltexture && gltexture->tex->isFullbright()) @@ -1171,6 +1174,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) void GLEEHorizonPortal::DrawContents(FDrawInfo *di) { + auto &vp = di->Viewpoint; sector_t *sector = portal->mOrigin; if (sector->GetTexture(sector_t::floor) == skyflatnum || sector->GetTexture(sector_t::ceiling) == skyflatnum) @@ -1189,9 +1193,9 @@ void GLEEHorizonPortal::DrawContents(FDrawInfo *di) horz.specialcolor = 0xffffffff; if (portal->mType == PORTS_PLANE) { - horz.plane.Texheight = r_viewpoint.Pos.Z + fabs(horz.plane.Texheight); + horz.plane.Texheight = vp.Pos.Z + fabs(horz.plane.Texheight); } - GLHorizonPortal ceil(&horz, true); + GLHorizonPortal ceil(&horz, di->Viewpoint, true); ceil.DrawContents(di); } if (sector->GetTexture(sector_t::floor) != skyflatnum) @@ -1203,9 +1207,9 @@ void GLEEHorizonPortal::DrawContents(FDrawInfo *di) horz.specialcolor = 0xffffffff; if (portal->mType == PORTS_PLANE) { - horz.plane.Texheight = r_viewpoint.Pos.Z - fabs(horz.plane.Texheight); + horz.plane.Texheight = vp.Pos.Z - fabs(horz.plane.Texheight); } - GLHorizonPortal floor(&horz, true); + GLHorizonPortal floor(&horz, di->Viewpoint, true); floor.DrawContents(di); } } diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index b9d2a242d..cc5b5d09d 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -72,7 +72,6 @@ private: AActor * savedviewactor; ActorRenderFlags savedvisibility; - FRenderViewpoint savedviewpoint; GLPortal *PrevPortal; TArray mPrimIndices; @@ -84,7 +83,7 @@ protected: virtual ~GLPortal() { } bool Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawInfo **pDi); - void End(bool usestencil); + void End(FDrawInfo *di, bool usestencil); virtual void DrawContents(FDrawInfo *di)=0; virtual void * GetSource() const =0; // GetSource MUST be implemented! void ClearClipper(FDrawInfo *di); @@ -107,7 +106,7 @@ public: if (Start(usestencil, doquery, outer_di, &di)) { DrawContents(di); - End(usestencil); + End(di, usestencil); } } @@ -179,7 +178,7 @@ struct GLLinePortal : public GLPortal return reinterpret_cast(pv); } - virtual int ClipSeg(seg_t *seg); + virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos); virtual int ClipSubsector(subsector_t *sub); virtual int ClipPoint(const DVector2 &pos); virtual bool NeedCap() { return false; } @@ -335,7 +334,7 @@ protected: public: - GLHorizonPortal(GLHorizonInfo * pt, bool local = false); + GLHorizonPortal(GLHorizonInfo * pt, FRenderViewpoint &vp, bool local = false); }; struct GLEEHorizonPortal : public GLPortal diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index a34c332ea..f11161788 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,26 +72,6 @@ EXTERN_CVAR (Bool, r_deathcamera) EXTERN_CVAR (Float, r_visibility) EXTERN_CVAR (Bool, r_drawvoxels) -//----------------------------------------------------------------------------- -// -// R_FrustumAngle -// -//----------------------------------------------------------------------------- -angle_t GLSceneDrawer::FrustumAngle() -{ - float tilt = fabs(GLRenderer->mAngles.Pitch.Degrees); - - // If the pitch is larger than this you can look all around at a FOV of 90° - if (tilt > 46.0f) return 0xffffffff; - - // ok, this is a gross hack that barely works... - // but at least it doesn't overestimate too much... - double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*GLRenderer->mCurrentFoV*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; - angle_t a1 = DAngle(floatangle).BAMs(); - if (a1 >= ANGLE_180) return 0xffffffff; - return a1; -} - //----------------------------------------------------------------------------- // // resets the 3D viewport @@ -140,24 +120,6 @@ void GLSceneDrawer::Set3DViewport(bool mainview) glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); } -//----------------------------------------------------------------------------- -// -// Setup the camera position -// -//----------------------------------------------------------------------------- - -void GLSceneDrawer::SetViewAngle(DAngle viewangle) -{ - FRenderViewpoint &vp = r_viewpoint; - GLRenderer->mAngles.Yaw = float(270.0-viewangle.Degrees); - DVector2 v = vp.Angles.Yaw.ToVector(); - vp.ViewVector.X = v.X; - vp.ViewVector.Y = v.Y; - - vp.SetViewAngle(r_viewwindow); -} - - //----------------------------------------------------------------------------- // // SetProjection @@ -177,15 +139,15 @@ void GLSceneDrawer::SetProjection(VSMatrix matrix) // //----------------------------------------------------------------------------- -void GLSceneDrawer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror) +void GLSceneDrawer::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) { float mult = mirror? -1:1; float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch; gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Roll.Degrees, 0.0f, 0.0f, 1.0f); - gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); - gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Yaw.Degrees, 0.0f, mult, 0.0f); + gl_RenderState.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + gl_RenderState.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + gl_RenderState.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); gl_RenderState.mViewMatrix.translate(vx * mult, -vz * planemult , -vy); gl_RenderState.mViewMatrix.scale(-mult, planemult, 1); } @@ -197,10 +159,10 @@ void GLSceneDrawer::SetViewMatrix(float vx, float vy, float vz, bool mirror, boo // Setup the view rotation matrix for the given viewpoint // //----------------------------------------------------------------------------- -void GLSceneDrawer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) +void GLSceneDrawer::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) { - SetViewAngle(va); - SetViewMatrix(vx, vy, vz, mirror, planemirror); + vp.SetViewAngle(r_viewwindow); + SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); gl_RenderState.ApplyMatrices(); } @@ -214,8 +176,9 @@ void GLSceneDrawer::SetupView(float vx, float vy, float vz, DAngle va, bool mirr void GLSceneDrawer::CreateScene(FDrawInfo *di) { - angle_t a1 = FrustumAngle(); - di->mClipper->SafeAddClipRangeRealAngles(r_viewpoint.Angles.Yaw.BAMs() + a1, r_viewpoint.Angles.Yaw.BAMs() - a1); + const auto &vp = di->Viewpoint; + angle_t a1 = di->FrustumAngle(); + di->mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); // reset the portal manager GLPortal::StartFrame(); @@ -230,16 +193,15 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) GLRenderer->mLights->Begin(); // Give the DrawInfo the viewpoint in fixed point because that's what the nodes are. - di->viewx = FLOAT2FIXED(r_viewpoint.Pos.X); - di->viewy = FLOAT2FIXED(r_viewpoint.Pos.Y); + di->viewx = FLOAT2FIXED(vp.Pos.X); + di->viewy = FLOAT2FIXED(vp.Pos.Y); validcount++; // used for processing sidedefs only once by the renderer. - di->mAngles = GLRenderer->mAngles; di->mShadowMap = &GLRenderer->mShadowMap; di->RenderBSPNode (level.HeadNode()); - di->PreparePlayerSprites(r_viewpoint.sector, di->in_area); + di->PreparePlayerSprites(vp.sector, di->in_area); // Process all the sprites on the current portal's back side which touch the portal. if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(di); @@ -269,12 +231,13 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) { + const auto &vp = di->Viewpoint; RenderAll.Clock(); glDepthMask(true); if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, di); - gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); + gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); gl_RenderState.EnableFog(true); gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); @@ -379,9 +342,11 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) void GLSceneDrawer::RenderTranslucent(FDrawInfo *di) { + const auto &vp = di->Viewpoint; + RenderAll.Clock(); - gl_RenderState.SetCameraPos(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z); + gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); // final pass: translucent stuff gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold); @@ -414,6 +379,7 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) { static int recursion=0; static int ssao_portals_available = 0; + const auto &vp = di->Viewpoint; bool applySSAO = false; if (drawmode == DM_MAINVIEW) @@ -431,11 +397,11 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) ssao_portals_available--; } - if (r_viewpoint.camera != nullptr) + if (vp.camera != nullptr) { - ActorRenderFlags savedflags = r_viewpoint.camera->renderflags; + ActorRenderFlags savedflags = vp.camera->renderflags; CreateScene(di); - r_viewpoint.camera->renderflags = savedflags; + vp.camera->renderflags = savedflags; } else { @@ -527,7 +493,7 @@ void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen) iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; GLPortal::BeginScene(); - int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection; + int mapsection = R_PointInSubsector(di->Viewpoint.Pos)->mapsection; di->CurrentMapSections.Set(mapsection); GLRenderer->mCurrentPortal = nullptr; DrawScene(di, toscreen ? DM_MAINVIEW : DM_OFFSCREEN); @@ -540,37 +506,37 @@ void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen) // //----------------------------------------------------------------------------- -sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { sector_t * lviewsector; GLRenderer->mSceneClearColor[0] = 0.0f; GLRenderer->mSceneClearColor[1] = 0.0f; GLRenderer->mSceneClearColor[2] = 0.0f; - R_SetupFrame (r_viewpoint, r_viewwindow, camera); + R_SetupFrame (mainvp, r_viewwindow, camera); GLRenderer->mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); // We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. - double radPitch = r_viewpoint.Angles.Pitch.Normalized180().Radians(); + double radPitch = mainvp.Angles.Pitch.Normalized180().Radians(); double angx = cos(radPitch); double angy = sin(radPitch) * level.info->pixelstretch; double alen = sqrt(angx*angx + angy*angy); - GLRenderer->mAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); - GLRenderer->mAngles.Roll.Degrees = r_viewpoint.Angles.Roll.Degrees; + mainvp.HWAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); + mainvp.HWAngles.Roll.Degrees = mainvp.Angles.Roll.Degrees; if (camera->player && camera->player - players == consoleplayer && ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera == camera->player->mo) { - r_viewpoint.ViewActor = nullptr; + mainvp.ViewActor = nullptr; } else { - r_viewpoint.ViewActor = camera; + mainvp.ViewActor = camera; } // 'viewsector' will not survive the rendering so it cannot be used anymore below. - lviewsector = r_viewpoint.sector; + lviewsector = mainvp.sector; // Render (potentially) multiple views for stereo 3d float viewShift[3]; @@ -583,20 +549,21 @@ sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, IntRect * bounds, fl screen->SetViewportRects(bounds); Set3DViewport(mainview); GLRenderer->mDrawingScene2D = true; - GLRenderer->mCurrentFoV = fov; - FDrawInfo *di = FDrawInfo::StartDrawInfo(this); + FDrawInfo *di = FDrawInfo::StartDrawInfo(this, mainvp); + auto vp = di->Viewpoint; di->SetViewArea(); - auto cm = di->SetFullbrightFlags(mainview ? r_viewpoint.camera->player : nullptr); + auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr); + di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) // Stereo mode specific perspective projection SetProjection( eye->GetProjection(fov, ratio, fovratio) ); // SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper - SetViewAngle(r_viewpoint.Angles.Yaw); + vp.SetViewAngle(r_viewwindow); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos - eye->GetViewShift(GLRenderer->mAngles.Yaw.Degrees, viewShift); - ScopedViewShifter viewShifter(r_viewpoint.Pos, viewShift); - SetViewMatrix(r_viewpoint.Pos.X, r_viewpoint.Pos.Y, r_viewpoint.Pos.Z, false, false); + eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); + ScopedViewShifter viewShifter(vp.Pos, viewShift); + SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); gl_RenderState.ApplyMatrices(); ProcessScene(di, toscreen); @@ -617,7 +584,7 @@ sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, IntRect * bounds, fl gl_RenderState.ApplyMatrices(); } } - FDrawInfo::EndDrawInfo(); + di->EndDrawInfo(); GLRenderer->mDrawingScene2D = false; if (!stereo3dMode.IsMono()) GLRenderer->mBuffers->BlitToEyeTexture(eye_ix); @@ -654,7 +621,9 @@ void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, GLRenderer->mVBO->Reset(); GLRenderer->mLights->Clear(); - sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); + // This shouldn't overwrite the global viewpoint even for a short time. + FRenderViewpoint savevp; + sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); glDisable(GL_STENCIL_TEST); gl_RenderState.SetSoftLightLevel(-1); GLRenderer->CopyToBackbuffer(&bounds, false); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index 3f088fd6b..2382c550d 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -28,11 +28,8 @@ public: GLPortal::drawer = this; } - angle_t FrustumAngle(); - - void SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror); - void SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); - void SetViewAngle(DAngle viewangle); + void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); + void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); void SetProjection(VSMatrix matrix); void Set3DViewport(bool mainview); void Reset3DViewport(); @@ -41,7 +38,7 @@ public: void EndDrawScene(FDrawInfo *di, sector_t * viewsector); void DrawEndScene2D(FDrawInfo *di, sector_t * viewsector); - sector_t *RenderViewpoint(AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); + sector_t *RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); sector_t *RenderView(player_t *player); void WriteSavePic(player_t *player, FileWriter *file, int width, int height); }; diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 30ff36e9e..a916c535a 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -212,6 +212,7 @@ static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool void GLSkyPortal::DrawContents(FDrawInfo *di) { bool drawBoth = false; + auto &vp = di->Viewpoint; // We have no use for Doom lighting special handling here, so disable it for this function. int oldlightmode = ::level.lightmode; @@ -229,7 +230,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) bool oldClamp = gl_RenderState.SetDepthClamp(true); gl_MatrixStack.Push(gl_RenderState.mViewMatrix); - drawer->SetupView(0, 0, 0, r_viewpoint.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + drawer->SetupView(vp, 0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->bSkybox) diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 02f477513..1a417c577 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -79,7 +79,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) bool additivefog = false; bool foglayer = false; int rel = sprite->fullbright? 0 : getExtraLight(); - auto &vp = r_viewpoint; + auto &vp = Viewpoint; if (pass==GLPASS_TRANSLUCENT) { diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 173c3311e..117c42823 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -349,7 +349,7 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) case PORTALTYPE_HORIZON: wall->horizon = UniqueHorizons.Get(wall->horizon); portal = GLPortal::FindPortal(wall->horizon); - if (!portal) portal = new GLHorizonPortal(wall->horizon); + if (!portal) portal = new GLHorizonPortal(wall->horizon, Viewpoint); portal->AddLine(wall); break; diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index d44bf0679..18884dab0 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -89,7 +89,7 @@ void HWDrawInfo::AddLine (seg_t *seg, bool portalclip) if (portalclip) { - int clipres = mClipPortal->ClipSeg(seg); + int clipres = mClipPortal->ClipSeg(seg, Viewpoint.Pos); if (clipres == PClip_InFront) return; } @@ -353,7 +353,7 @@ void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector) SetupSprite.Clock(); sector_t * sec=sub->sector; // Handle all things in sector. - auto &vp = r_viewpoint; + const auto &vp = Viewpoint; for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) { auto thing = p->m_thing; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 0de3cd359..06a42f1eb 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -2,6 +2,7 @@ #include #include "r_defs.h" +#include "r_utility.h" struct FSectorPortalGroup; @@ -94,9 +95,10 @@ struct HWDrawInfo int FullbrightFlags; std::atomic spriteindex; IPortal *mClipPortal; - FRotator mAngles; + //FRotator mAngles; IShadowMap *mShadowMap; Clipper *mClipper; + FRenderViewpoint Viewpoint; TArray MissingUpperTextures; TArray MissingLowerTextures; @@ -186,6 +188,7 @@ public: void PrepareTargeterSprites(); void UpdateCurrentMapSection(); + angle_t FrustumAngle(); virtual void DrawWall(GLWall *wall, int pass) = 0; virtual void DrawFlat(GLFlat *flat, int pass, bool trans) = 0; diff --git a/src/hwrenderer/scene/hw_drawlist.cpp b/src/hwrenderer/scene/hw_drawlist.cpp index 1df8d5763..c1677646b 100644 --- a/src/hwrenderer/scene/hw_drawlist.cpp +++ b/src/hwrenderer/scene/hw_drawlist.cpp @@ -688,7 +688,7 @@ SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head) //========================================================================== void HWDrawList::Sort(HWDrawInfo *di) { - SortZ = r_viewpoint.Pos.Z; + SortZ = di->Viewpoint.Pos.Z; MakeSortList(); sorted = DoSort(di, SortNodes[SortNodeStart]); } diff --git a/src/hwrenderer/scene/hw_fakeflat.cpp b/src/hwrenderer/scene/hw_fakeflat.cpp index 5b877704d..3dbdff4c9 100644 --- a/src/hwrenderer/scene/hw_fakeflat.cpp +++ b/src/hwrenderer/scene/hw_fakeflat.cpp @@ -394,7 +394,7 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac //----------------------------------------------------------------------------- void HWDrawInfo::SetViewArea() { - auto &vp = r_viewpoint; + auto &vp = Viewpoint; // The render_sector is better suited to represent the current position in GL vp.sector = R_PointInSubsector(vp.Pos)->render_sector; @@ -424,7 +424,7 @@ int HWDrawInfo::SetFullbrightFlags(player_t *player) if (cplayer->extralight == INT_MIN) { cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; - r_viewpoint.extralight = 0; + Viewpoint.extralight = 0; FullbrightFlags = Fullbright; // This does never set stealth vision. } @@ -463,3 +463,24 @@ int HWDrawInfo::SetFullbrightFlags(player_t *player) return CM_DEFAULT; } } + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- +angle_t HWDrawInfo::FrustumAngle() +{ + float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt > 46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 >= ANGLE_180) return 0xffffffff; + return a1; +} + diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 0fa14f2e1..91cb8027e 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -306,7 +306,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) dynlightindex = -1; uint8_t &srf = di->sectorrenderflags[sector->sectornum]; - auto &vp = r_viewpoint; + const auto &vp = di->Viewpoint; // // diff --git a/src/hwrenderer/scene/hw_portal.h b/src/hwrenderer/scene/hw_portal.h index 695daaa98..b4a313c94 100644 --- a/src/hwrenderer/scene/hw_portal.h +++ b/src/hwrenderer/scene/hw_portal.h @@ -37,7 +37,7 @@ class IPortal { public: virtual ~IPortal() {} - virtual int ClipSeg(seg_t *seg) { return PClip_Inside; } + virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } virtual line_t *ClipLine() { return nullptr; } diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 74c5b3f0e..7277b3704 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -88,7 +88,7 @@ void HWDrawInfo::ClearBuffers() void HWDrawInfo::UpdateCurrentMapSection() { - const int mapsection = R_PointInSubsector(r_viewpoint.Pos)->mapsection; + const int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; CurrentMapSections.Set(mapsection); } @@ -484,7 +484,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) HandledSubsectors.Clear(); validcount++; - if (MissingUpperTextures[i].Planez > r_viewpoint.Pos.Z) + if (MissingUpperTextures[i].Planez > Viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. @@ -556,7 +556,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) HandledSubsectors.Clear(); validcount++; - if (MissingLowerTextures[i].Planez < r_viewpoint.Pos.Z) + if (MissingLowerTextures[i].Planez < Viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. @@ -642,7 +642,7 @@ void HWDrawInfo::DrawUnhandledMissingTextures() // already done! if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; - if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < r_viewpoint.Pos.Z) continue; // out of sight + if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < Viewpoint.Pos.Z) continue; // out of sight // FIXME: The check for degenerate subsectors should be more precise if (seg->PartnerSeg && (seg->PartnerSeg->Subsector->flags & SSECF_DEGENERATE)) continue; @@ -664,7 +664,7 @@ void HWDrawInfo::DrawUnhandledMissingTextures() if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue; - if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > r_viewpoint.Pos.Z) continue; // out of sight + if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue; if (seg->backsector->ValidatePortal(sector_t::floor) != NULL) continue; @@ -757,7 +757,7 @@ bool HWDrawInfo::CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor) sub->render_sector->GetPlaneTexZ(sector_t::floor) != anchor->GetPlaneTexZ(sector_t::floor) || sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) { - if (sub == viewsubsector && r_viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true; + if (sub == viewsubsector && Viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true; HandledSubsectors.Push(sub); } } @@ -903,7 +903,7 @@ bool HWDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) void HWDrawInfo::HandleHackedSubsectors() { - viewsubsector = R_PointInSubsector(r_viewpoint.Pos); + viewsubsector = R_PointInSubsector(Viewpoint.Pos); // Each subsector may only be processed once in this loop! validcount++; diff --git a/src/hwrenderer/scene/hw_sky.cpp b/src/hwrenderer/scene/hw_sky.cpp index 666aeecde..57eeb0eee 100644 --- a/src/hwrenderer/scene/hw_sky.cpp +++ b/src/hwrenderer/scene/hw_sky.cpp @@ -154,7 +154,7 @@ void GLWall::SkyPlane(HWDrawInfo *di, sector_t *sector, int plane, bool allowref } else if (allowreflect && sector->GetReflect(plane) > 0) { - auto vpz = r_viewpoint.Pos.Z; + auto vpz = di->Viewpoint.Pos.Z; if ((plane == sector_t::ceiling && vpz > sector->ceilingplane.fD()) || (plane == sector_t::floor && vpz < -sector->floorplane.fD())) return; ptype = PORTALTYPE_PLANEMIRROR; @@ -341,7 +341,7 @@ void GLWall::SkyBottom(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,v else { // Special hack for Vrack2b - if (bs->floorplane.ZatPoint(r_viewpoint.Pos) > r_viewpoint.Pos.Z) return; + if (bs->floorplane.ZatPoint(di->Viewpoint.Pos) > di->Viewpoint.Pos.Z) return; } } zbottom[0]=zbottom[1]=-32768.0f; diff --git a/src/hwrenderer/scene/hw_skydome.cpp b/src/hwrenderer/scene/hw_skydome.cpp index 1de0999f6..de8a51af1 100644 --- a/src/hwrenderer/scene/hw_skydome.cpp +++ b/src/hwrenderer/scene/hw_skydome.cpp @@ -289,7 +289,7 @@ void FSkyDomeCreator::SetupMatrices(FMaterial *tex, float x_offset, float y_offs // smaller sky textures must be tiled. We restrict it to 128 sky pixels, though modelMatrix.translate(0.f, -1250.f, 0.f); modelMatrix.scale(1.f, 128 / 230.f, 1.f); - yscale = 128 / texh; // intentionally left as integer. + yscale = float(128 / texh); // intentionally left as integer. } else if (texh < 200) { diff --git a/src/hwrenderer/scene/hw_sprites.cpp b/src/hwrenderer/scene/hw_sprites.cpp index e0f9b3bed..abdc50853 100644 --- a/src/hwrenderer/scene/hw_sprites.cpp +++ b/src/hwrenderer/scene/hw_sprites.cpp @@ -70,6 +70,7 @@ EXTERN_CVAR(Float, transsouls) bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) { + const auto &HWAngles = di->Viewpoint.HWAngles; if (actor != nullptr && (actor->renderflags & RF_SPRITETYPEMASK) == RF_FLATSPRITE) { Matrix3x4 mat; @@ -149,7 +150,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) float xrel = xcenter - vp->X; float yrel = ycenter - vp->Y; float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); - float counterRotationDeg = 270. - di->mAngles.Yaw.Degrees; // counteracts existing sprite rotation + float counterRotationDeg = 270. - HWAngles.Yaw.Degrees; // counteracts existing sprite rotation float relAngleDeg = counterRotationDeg + absAngleDeg; mat.Rotate(0, 1, 0, relAngleDeg); @@ -157,7 +158,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) // [fgsfds] calculate yaw vectors float yawvecX = 0, yawvecY = 0, rollDegrees = 0; - float angleRad = (270. - di->mAngles.Yaw).Radians(); + float angleRad = (270. - HWAngles.Yaw).Radians(); if (actor) rollDegrees = Angles.Roll.Degrees; if (isFlatSprite) { @@ -181,7 +182,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) if (useOffsets) mat.Translate(xx, zz, yy); if (drawWithXYBillboard) { - mat.Rotate(-sin(angleRad), 0, cos(angleRad), -di->mAngles.Pitch.Degrees); + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees); } mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees); if (useOffsets) mat.Translate(-xx, -zz, -yy); @@ -191,7 +192,7 @@ bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) // Rotate the sprite about the vector starting at the center of the sprite // triangle strip and with direction orthogonal to where the player is looking // in the x/y plane. - mat.Rotate(-sin(angleRad), 0, cos(angleRad), -di->mAngles.Pitch.Degrees); + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees); } mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center @@ -403,7 +404,7 @@ void GLSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t return; } - auto &vp = r_viewpoint; + const auto &vp = di->Viewpoint; AActor *camera = vp.camera; if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->Alpha)) @@ -924,7 +925,7 @@ void GLSprite::ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t * } } - auto &vp = r_viewpoint; + const auto &vp = di->Viewpoint; double timefrac = vp.TicFrac; if (paused || bglobal.freeze || (level.flags2 & LEVEL2_FROZEN)) timefrac = 0.; @@ -982,7 +983,7 @@ void HWDrawInfo::ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area) TMap processcheck; if (glport->validcount == validcount) return; // only process once per frame glport->validcount = validcount; - auto &vp = r_viewpoint; + const auto &vp = Viewpoint; for (auto port : glport->lines) { line_t *line = port->mOrigin; diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index 3cb181378..6c22e560a 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -169,7 +169,7 @@ void GLWall::PutWall(HWDrawInfo *di, bool translucent) if (translucent) { flags |= GLWF_TRANSLUCENT; - ViewDistance = (r_viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared(); + ViewDistance = (di->Viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared(); } if (di->isFullbrightScene()) @@ -443,7 +443,7 @@ bool GLWall::DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1, ztop[1] = ztop[0] = fs->GetPlaneTexZ(sector_t::ceiling); zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); - auto vpz = r_viewpoint.Pos.Z; + auto vpz = di->Viewpoint.Pos.Z; if (vpz < fs->GetPlaneTexZ(sector_t::ceiling)) { if (vpz > fs->GetPlaneTexZ(sector_t::floor)) diff --git a/src/hwrenderer/scene/hw_weapon.cpp b/src/hwrenderer/scene/hw_weapon.cpp index 0125e3f8f..b0861e8bd 100644 --- a/src/hwrenderer/scene/hw_weapon.cpp +++ b/src/hwrenderer/scene/hw_weapon.cpp @@ -427,7 +427,7 @@ void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area) player_t * player = playermo->player; const bool hudModelStep = IsHUDModelForPlayerAvailable(player); - auto &vp = r_viewpoint; + const auto &vp = Viewpoint; AActor *camera = vp.camera; @@ -507,7 +507,7 @@ void HWDrawInfo::PrepareTargeterSprites() { AActor * playermo = players[consoleplayer].camera; player_t * player = playermo->player; - AActor *camera = r_viewpoint.camera; + AActor *camera = Viewpoint.camera; // this is the same as above if (!player || diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 82ab62f2b..04875eb30 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -598,7 +598,8 @@ void R_ResetViewInterpolation () //========================================================================== // -// R_SetViewAngle +// R_SetViewAngle +// sets all values derived from the view angle. // //========================================================================== @@ -609,6 +610,12 @@ void FRenderViewpoint::SetViewAngle (const FViewWindow &viewwindow) TanSin = viewwindow.FocalTangent * Sin; TanCos = viewwindow.FocalTangent * Cos; + + DVector2 v = Angles.Yaw.ToVector(); + ViewVector.X = v.X; + ViewVector.Y = v.Y; + HWAngles.Yaw = float(270.0 - Angles.Yaw.Degrees); + } //========================================================================== diff --git a/src/r_utility.h b/src/r_utility.h index 35cedde8f..b18588ab9 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -21,6 +21,7 @@ struct FRenderViewpoint DVector3 Pos; // Camera position DVector3 ActorPos; // Camera actor's position DRotator Angles; // Camera angles + FRotator HWAngles; // Actual rotation angles for the hardware renderer DVector2 ViewVector; // HWR only: direction the camera is facing. AActor *ViewActor; // either the same as camera or nullptr diff --git a/src/vectors.h b/src/vectors.h index 5f20e4115..65a30345f 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -1180,7 +1180,7 @@ struct TAngle TAngle &operator= (double other) { - Degrees = other; + Degrees = (decltype(Degrees))other; return *this; } From 922c3a8d7590152d1fb1a765f4dbef467e4aefd4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 19 Jun 2018 23:52:01 +0200 Subject: [PATCH 20/58] - moved the basic methofs of HWDrawInfo to a dedicated file instead of storing them somewhere else. --- src/CMakeLists.txt | 1 + src/hwrenderer/scene/hw_drawinfo.cpp | 213 ++++++++++++++++++++++++ src/hwrenderer/scene/hw_fakeflat.cpp | 98 ----------- src/hwrenderer/scene/hw_renderhacks.cpp | 57 ------- 4 files changed, 214 insertions(+), 155 deletions(-) create mode 100644 src/hwrenderer/scene/hw_drawinfo.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59b1e706c..1a298824a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -837,6 +837,7 @@ set( FASTMATH_SOURCES hwrenderer/scene/hw_bsp.cpp hwrenderer/scene/hw_fakeflat.cpp hwrenderer/scene/hw_decal.cpp + hwrenderer/scene/hw_drawinfo.cpp hwrenderer/scene/hw_drawlist.cpp hwrenderer/scene/hw_clipper.cpp hwrenderer/scene/hw_flats.cpp diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp new file mode 100644 index 000000000..75943691d --- /dev/null +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -0,0 +1,213 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_drawinfo.cpp +** Basic scene draw info management class +** +*/ + +#include "a_sharedglobal.h" +#include "r_utility.h" +#include "r_sky.h" +#include "d_player.h" +#include "g_levellocals.h" +#include "hw_fakeflat.h" +#include "hw_drawinfo.h" +#include "hwrenderer/utility/hw_clock.h" +#include "hwrenderer/utility/hw_cvars.h" + +sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::ClearBuffers() +{ + for(unsigned int i=0;i< otherfloorplanes.Size();i++) + { + gl_subsectorrendernode * node = otherfloorplanes[i]; + while (node) + { + gl_subsectorrendernode * n = node; + node = node->next; + delete n; + } + } + otherfloorplanes.Clear(); + + for(unsigned int i=0;i< otherceilingplanes.Size();i++) + { + gl_subsectorrendernode * node = otherceilingplanes[i]; + while (node) + { + gl_subsectorrendernode * n = node; + node = node->next; + delete n; + } + } + otherceilingplanes.Clear(); + + // clear all the lists that might not have been cleared already + MissingUpperTextures.Clear(); + MissingLowerTextures.Clear(); + MissingUpperSegs.Clear(); + MissingLowerSegs.Clear(); + SubsectorHacks.Clear(); + CeilingStacks.Clear(); + FloorStacks.Clear(); + HandledSubsectors.Clear(); + spriteindex = 0; + + CurrentMapSections.Resize(level.NumMapSections); + CurrentMapSections.Zero(); + + sectorrenderflags.Resize(level.sectors.Size()); + ss_renderflags.Resize(level.subsectors.Size()); + no_renderflags.Resize(level.subsectors.Size()); + + memset(§orrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0])); + memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0])); + memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0])); + + mClipPortal = nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::UpdateCurrentMapSection() +{ + const int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; + CurrentMapSections.Set(mapsection); +} + + +//----------------------------------------------------------------------------- +// +// Sets the area the camera is in +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::SetViewArea() +{ + auto &vp = Viewpoint; + // The render_sector is better suited to represent the current position in GL + vp.sector = R_PointInSubsector(vp.Pos)->render_sector; + + // Get the heightsec state from the render sector, not the current one! + if (vp.sector->GetHeightSec()) + { + in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below : + (vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) && + !(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal; + } + else + { + in_area = level.HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs. + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +int HWDrawInfo::SetFullbrightFlags(player_t *player) +{ + FullbrightFlags = 0; + + // check for special colormaps + player_t * cplayer = player? player->camera->player : nullptr; + if (cplayer) + { + int cm = CM_DEFAULT; + if (cplayer->extralight == INT_MIN) + { + cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; + Viewpoint.extralight = 0; + FullbrightFlags = Fullbright; + // This does never set stealth vision. + } + else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP) + { + cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap; + FullbrightFlags = Fullbright; + if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision; + } + else if (cplayer->fixedlightlevel != -1) + { + auto torchtype = PClass::FindActor(NAME_PowerTorch); + auto litetype = PClass::FindActor(NAME_PowerLightAmp); + for (AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) + { + //PalEntry color = in->CallGetBlend(); + + // Need special handling for light amplifiers + if (in->IsKindOf(torchtype)) + { + FullbrightFlags = Fullbright; + if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision; + } + else if (in->IsKindOf(litetype)) + { + FullbrightFlags = Fullbright; + if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision; + if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision; + } + } + } + return cm; + } + else + { + return CM_DEFAULT; + } +} + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- + +angle_t HWDrawInfo::FrustumAngle() +{ + float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt > 46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 >= ANGLE_180) return 0xffffffff; + return a1; +} + diff --git a/src/hwrenderer/scene/hw_fakeflat.cpp b/src/hwrenderer/scene/hw_fakeflat.cpp index 3dbdff4c9..555504bc4 100644 --- a/src/hwrenderer/scene/hw_fakeflat.cpp +++ b/src/hwrenderer/scene/hw_fakeflat.cpp @@ -386,101 +386,3 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac } return dest; } - -//----------------------------------------------------------------------------- -// -// Sets the area the camera is in -// -//----------------------------------------------------------------------------- -void HWDrawInfo::SetViewArea() -{ - auto &vp = Viewpoint; - // The render_sector is better suited to represent the current position in GL - vp.sector = R_PointInSubsector(vp.Pos)->render_sector; - - // Get the heightsec state from the render sector, not the current one! - if (vp.sector->GetHeightSec()) - { - in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below : - (vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) && - !(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal; - } - else - { - in_area = level.HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs. - } -} - - -int HWDrawInfo::SetFullbrightFlags(player_t *player) -{ - FullbrightFlags = 0; - - // check for special colormaps - player_t * cplayer = player? player->camera->player : nullptr; - if (cplayer) - { - int cm = CM_DEFAULT; - if (cplayer->extralight == INT_MIN) - { - cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; - Viewpoint.extralight = 0; - FullbrightFlags = Fullbright; - // This does never set stealth vision. - } - else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP) - { - cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap; - FullbrightFlags = Fullbright; - if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision; - } - else if (cplayer->fixedlightlevel != -1) - { - auto torchtype = PClass::FindActor(NAME_PowerTorch); - auto litetype = PClass::FindActor(NAME_PowerLightAmp); - for (AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) - { - //PalEntry color = in->CallGetBlend(); - - // Need special handling for light amplifiers - if (in->IsKindOf(torchtype)) - { - FullbrightFlags = Fullbright; - if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision; - } - else if (in->IsKindOf(litetype)) - { - FullbrightFlags = Fullbright; - if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision; - if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision; - } - } - } - return cm; - } - else - { - return CM_DEFAULT; - } -} - -//----------------------------------------------------------------------------- -// -// R_FrustumAngle -// -//----------------------------------------------------------------------------- -angle_t HWDrawInfo::FrustumAngle() -{ - float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees); - - // If the pitch is larger than this you can look all around at a FOV of 90° - if (tilt > 46.0f) return 0xffffffff; - - // ok, this is a gross hack that barely works... - // but at least it doesn't overestimate too much... - double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0; - angle_t a1 = DAngle(floatangle).BAMs(); - if (a1 >= ANGLE_180) return 0xffffffff; - return a1; -} - diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 7277b3704..82deec698 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -35,63 +35,6 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); -void HWDrawInfo::ClearBuffers() -{ - for(unsigned int i=0;i< otherfloorplanes.Size();i++) - { - gl_subsectorrendernode * node = otherfloorplanes[i]; - while (node) - { - gl_subsectorrendernode * n = node; - node = node->next; - delete n; - } - } - otherfloorplanes.Clear(); - - for(unsigned int i=0;i< otherceilingplanes.Size();i++) - { - gl_subsectorrendernode * node = otherceilingplanes[i]; - while (node) - { - gl_subsectorrendernode * n = node; - node = node->next; - delete n; - } - } - otherceilingplanes.Clear(); - - // clear all the lists that might not have been cleared already - MissingUpperTextures.Clear(); - MissingLowerTextures.Clear(); - MissingUpperSegs.Clear(); - MissingLowerSegs.Clear(); - SubsectorHacks.Clear(); - CeilingStacks.Clear(); - FloorStacks.Clear(); - HandledSubsectors.Clear(); - spriteindex = 0; - - CurrentMapSections.Resize(level.NumMapSections); - CurrentMapSections.Zero(); - - sectorrenderflags.Resize(level.sectors.Size()); - ss_renderflags.Resize(level.subsectors.Size()); - no_renderflags.Resize(level.subsectors.Size()); - - memset(§orrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0])); - memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0])); - memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0])); - - mClipPortal = nullptr; -} - -void HWDrawInfo::UpdateCurrentMapSection() -{ - const int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; - CurrentMapSections.Set(mapsection); -} - //========================================================================== // // Adds a subsector plane to a sector's render list From 9bdb5f1a5d4df5d06d7bdebf51d9148fe7d826a3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 10:10:30 +0200 Subject: [PATCH 21/58] Moved ProcessLowerMinisegs back toAPI independent code. --- src/gl/scene/gl_drawinfo.cpp | 12 ------------ src/hwrenderer/scene/hw_drawinfo.h | 2 +- src/hwrenderer/scene/hw_renderhacks.cpp | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 60991e2fb..11ae0b825 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -481,18 +481,6 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) ClearFloodStencil(&ws); } -// This was temporarily moved out of gl_renderhacks.cpp so that the dependency on GLWall could be eliminated until things have progressed a bit. -void FDrawInfo::ProcessLowerMinisegs(TArray &lowersegs) -{ - for(unsigned int j=0;jSubsector->render_sector, seg->PartnerSeg->Subsector->render_sector); - rendered_lines++; - } -} - // Same here for the dependency on the portal. void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) { diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 06a42f1eb..317fea51d 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -196,7 +196,7 @@ public: virtual void FloodUpperGap(seg_t * seg) = 0; virtual void FloodLowerGap(seg_t * seg) = 0; - virtual void ProcessLowerMinisegs(TArray &lowersegs) = 0; + void ProcessLowerMinisegs(TArray &lowersegs); virtual void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) = 0; virtual void AddWall(GLWall *w) = 0; diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 82deec698..52f996f41 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -30,7 +30,8 @@ #include "r_sky.h" #include "g_levellocals.h" -#include "hwrenderer/scene/hw_drawinfo.h" +#include "hw_drawinfo.h" +#include "hw_drawstructs.h" #include "hwrenderer/utility/hw_clock.h" sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); @@ -844,6 +845,18 @@ bool HWDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) // //========================================================================== +void HWDrawInfo::ProcessLowerMinisegs(TArray &lowersegs) +{ + for(unsigned int j=0;jSubsector->render_sector, seg->PartnerSeg->Subsector->render_sector); + rendered_lines++; + } +} + + void HWDrawInfo::HandleHackedSubsectors() { viewsubsector = R_PointInSubsector(Viewpoint.Pos); From 20604026713a9fe974c33aa9e83fb1f077504b3e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 10:47:03 +0200 Subject: [PATCH 22/58] Moved the viewpoint setup code in RenderViewpoint to R_SetupFrame --- src/gl/scene/gl_scene.cpp | 33 +++++---------------------------- src/r_utility.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index f11161788..8cc767815 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -507,8 +507,7 @@ void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen) //----------------------------------------------------------------------------- sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) -{ - sector_t * lviewsector; +{ GLRenderer->mSceneClearColor[0] = 0.0f; GLRenderer->mSceneClearColor[1] = 0.0f; GLRenderer->mSceneClearColor[2] = 0.0f; @@ -516,29 +515,7 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca GLRenderer->mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); - // We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. - double radPitch = mainvp.Angles.Pitch.Normalized180().Radians(); - double angx = cos(radPitch); - double angy = sin(radPitch) * level.info->pixelstretch; - double alen = sqrt(angx*angx + angy*angy); - - mainvp.HWAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); - mainvp.HWAngles.Roll.Degrees = mainvp.Angles.Roll.Degrees; - - if (camera->player && camera->player - players == consoleplayer && - ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera == camera->player->mo) - { - mainvp.ViewActor = nullptr; - } - else - { - mainvp.ViewActor = camera; - } - - // 'viewsector' will not survive the rendering so it cannot be used anymore below. - lviewsector = mainvp.sector; - - // Render (potentially) multiple views for stereo 3d + // Render (potentially) multiple views for stereo 3d float viewShift[3]; const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode(); stereo3dMode.SetUp(); @@ -570,8 +547,8 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca if (mainview) { - if (toscreen) EndDrawScene(di, lviewsector); // do not call this for camera textures. - GLRenderer->PostProcessScene(cm, [&]() { DrawEndScene2D(di, lviewsector); }); + if (toscreen) EndDrawScene(di, mainvp.sector); // do not call this for camera textures. + GLRenderer->PostProcessScene(cm, [&]() { DrawEndScene2D(di, mainvp.sector); }); // This should be done after postprocessing, not before. GLRenderer->mBuffers->BindCurrentFB(); @@ -593,7 +570,7 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca stereo3dMode.TearDown(); interpolator.RestoreInterpolations (); - return lviewsector; + return mainvp.sector; } //=========================================================================== diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 04875eb30..96ea6a325 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1016,6 +1016,31 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor screen->SetClearColor(color); SWRenderer->SetClearColor(color); } + + + // And finally some info that is needed for the hardware renderer + + // Scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. + // However, to set up a projection matrix this needs to be adjusted. + double radPitch = viewpoint.Angles.Pitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * level.info->pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + viewpoint.HWAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); + + viewpoint.HWAngles.Roll.Degrees = viewpoint.Angles.Roll.Degrees; // copied for convenience. + + // ViewActor only gets set, if the camera actor should not be rendered + if (actor->player && actor->player - players == consoleplayer && + ((actor->player->cheats & CF_CHASECAM) || (r_deathcamera && actor->health <= 0)) && actor == actor->player->mo) + { + viewpoint.ViewActor = nullptr; + } + else + { + viewpoint.ViewActor = actor; + } + } From 677eba3392e1a6da226b3cadaf2c655321fef7e6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 10:48:24 +0200 Subject: [PATCH 23/58] - fix the header --- src/gl/scene/gl_drawinfo.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index e732c8c8f..c66da1184 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -103,7 +103,6 @@ struct FDrawInfo : public HWDrawInfo void DrawSorted(int listindex); // These two may be moved to the API independent part of the renderer later. - void ProcessLowerMinisegs(TArray &lowersegs) override; void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override; static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp); From b244fa2c8bca93a5bb6bc69e58b4c4acf0dbe0c3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 11:17:38 +0200 Subject: [PATCH 24/58] Took the main render function out of GLSceneDrawer and moved them into FDrawInfo First step of eliminating the intermediate SceneDrawer class which has become mostly redundant. --- src/gl/scene/gl_drawinfo.h | 4 ++ src/gl/scene/gl_scene.cpp | 89 +++++++++++++++++------------------ src/gl/scene/gl_scenedrawer.h | 9 ---- src/r_utility.cpp | 4 ++ 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index c66da1184..1eea98467 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -104,6 +104,10 @@ struct FDrawInfo : public HWDrawInfo // These two may be moved to the API independent part of the renderer later. void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override; + + void CreateScene(); + void RenderScene(int recursion); + void RenderTranslucent(); static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp); FDrawInfo *EndDrawInfo(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 8cc767815..724337c5b 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -174,11 +174,11 @@ void GLSceneDrawer::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz // //----------------------------------------------------------------------------- -void GLSceneDrawer::CreateScene(FDrawInfo *di) +void FDrawInfo::CreateScene() { - const auto &vp = di->Viewpoint; - angle_t a1 = di->FrustumAngle(); - di->mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); + const auto &vp = Viewpoint; + angle_t a1 = FrustumAngle(); + mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); // reset the portal manager GLPortal::StartFrame(); @@ -193,27 +193,27 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) GLRenderer->mLights->Begin(); // Give the DrawInfo the viewpoint in fixed point because that's what the nodes are. - di->viewx = FLOAT2FIXED(vp.Pos.X); - di->viewy = FLOAT2FIXED(vp.Pos.Y); + viewx = FLOAT2FIXED(vp.Pos.X); + viewy = FLOAT2FIXED(vp.Pos.Y); validcount++; // used for processing sidedefs only once by the renderer. - di->mShadowMap = &GLRenderer->mShadowMap; + mShadowMap = &GLRenderer->mShadowMap; - di->RenderBSPNode (level.HeadNode()); - di->PreparePlayerSprites(vp.sector, di->in_area); + RenderBSPNode (level.HeadNode()); + PreparePlayerSprites(vp.sector, in_area); // Process all the sprites on the current portal's back side which touch the portal. - if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(di); + if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(this); Bsp.Unclock(); // And now the crappy hacks that have to be done to avoid rendering anomalies. // These cannot be multithreaded when the time comes because all these depend // on the global 'validcount' variable. - di->HandleMissingTextures(di->in_area); // Missing upper/lower textures - di->HandleHackedSubsectors(); // open sector hacks for deep water - di->ProcessSectorStacks(di->in_area); // merge visplanes of sector stacks + HandleMissingTextures(in_area); // Missing upper/lower textures + HandleHackedSubsectors(); // open sector hacks for deep water + ProcessSectorStacks(in_area); // merge visplanes of sector stacks GLRenderer->mLights->Finish(); GLRenderer->mVBO->Unmap(); @@ -229,13 +229,13 @@ void GLSceneDrawer::CreateScene(FDrawInfo *di) // //----------------------------------------------------------------------------- -void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) +void FDrawInfo::RenderScene(int recursion) { - const auto &vp = di->Viewpoint; + const auto &vp = Viewpoint; RenderAll.Clock(); glDepthMask(true); - if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, di); + if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, this); gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); @@ -244,22 +244,22 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) if (gl_sort_textures) { - di->drawlists[GLDL_PLAINWALLS].SortWalls(); - di->drawlists[GLDL_PLAINFLATS].SortFlats(); - di->drawlists[GLDL_MASKEDWALLS].SortWalls(); - di->drawlists[GLDL_MASKEDFLATS].SortFlats(); - di->drawlists[GLDL_MASKEDWALLSOFS].SortWalls(); + drawlists[GLDL_PLAINWALLS].SortWalls(); + drawlists[GLDL_PLAINFLATS].SortFlats(); + drawlists[GLDL_MASKEDWALLS].SortWalls(); + drawlists[GLDL_MASKEDFLATS].SortFlats(); + drawlists[GLDL_MASKEDWALLSOFS].SortWalls(); } // if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front, // so that we don't have to do repeated map/unmap calls on the buffer. - if (gl.lightmethod == LM_DEFERRED && level.HasDynamicLights && !di->isFullbrightScene()) + if (gl.lightmethod == LM_DEFERRED && level.HasDynamicLights && !isFullbrightScene()) { GLRenderer->mLights->Begin(); - di->drawlists[GLDL_PLAINFLATS].DrawFlats(di, GLPASS_LIGHTSONLY); - di->drawlists[GLDL_MASKEDFLATS].DrawFlats(di, GLPASS_LIGHTSONLY); - di->drawlists[GLDL_TRANSLUCENTBORDER].Draw(di, GLPASS_LIGHTSONLY); - di->drawlists[GLDL_TRANSLUCENT].Draw(di, GLPASS_LIGHTSONLY, true); + drawlists[GLDL_PLAINFLATS].DrawFlats(this, GLPASS_LIGHTSONLY); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, GLPASS_LIGHTSONLY); + drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, GLPASS_LIGHTSONLY); + drawlists[GLDL_TRANSLUCENT].Draw(this, GLPASS_LIGHTSONLY, true); GLRenderer->mLights->Finish(); } @@ -272,8 +272,8 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) gl_RenderState.EnableTexture(gl_texture); gl_RenderState.EnableBrightmap(true); - di->drawlists[GLDL_PLAINWALLS].DrawWalls(di, pass); - di->drawlists[GLDL_PLAINFLATS].DrawFlats(di, pass); + drawlists[GLDL_PLAINWALLS].DrawWalls(this, pass); + drawlists[GLDL_PLAINFLATS].DrawFlats(this, pass); // Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show @@ -283,20 +283,20 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) gl_RenderState.SetTextureMode(TM_MASK); } gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); - di->drawlists[GLDL_MASKEDWALLS].DrawWalls(di, pass); - di->drawlists[GLDL_MASKEDFLATS].DrawFlats(di, pass); + drawlists[GLDL_MASKEDWALLS].DrawWalls(this, pass); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, pass); // Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use. - if (di->drawlists[GLDL_MASKEDWALLSOFS].Size() > 0) + if (drawlists[GLDL_MASKEDWALLSOFS].Size() > 0) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -128.0f); - di->drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(di, pass); + drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(this, pass); glDisable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0, 0); } - di->drawlists[GLDL_MODELS].Draw(di, pass); + drawlists[GLDL_MODELS].Draw(this, pass); gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -305,7 +305,7 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -128.0f); glDepthMask(false); - di->DrawDecals(); + DrawDecals(); gl_RenderState.SetTextureMode(TM_MODULATE); @@ -323,7 +323,7 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) gl_RenderState.EnableFog(true); gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); - di->DrawUnhandledMissingTextures(); + DrawUnhandledMissingTextures(); glDepthMask(true); glPolygonOffset(0.0f, 0.0f); @@ -340,9 +340,9 @@ void GLSceneDrawer::RenderScene(FDrawInfo *di, int recursion) // //----------------------------------------------------------------------------- -void GLSceneDrawer::RenderTranslucent(FDrawInfo *di) +void FDrawInfo::RenderTranslucent() { - const auto &vp = di->Viewpoint; + const auto &vp = Viewpoint; RenderAll.Clock(); @@ -353,9 +353,9 @@ void GLSceneDrawer::RenderTranslucent(FDrawInfo *di) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); gl_RenderState.EnableBrightmap(true); - di->drawlists[GLDL_TRANSLUCENTBORDER].Draw(di, GLPASS_TRANSLUCENT); + drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, GLPASS_TRANSLUCENT); glDepthMask(false); - di->DrawSorted(GLDL_TRANSLUCENT); + DrawSorted(GLDL_TRANSLUCENT); gl_RenderState.EnableBrightmap(false); @@ -400,15 +400,15 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) if (vp.camera != nullptr) { ActorRenderFlags savedflags = vp.camera->renderflags; - CreateScene(di); + di->CreateScene(); vp.camera->renderflags = savedflags; } else { - CreateScene(di); + di->CreateScene(); } - RenderScene(di, recursion); + di->RenderScene(recursion); if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS) { @@ -425,7 +425,7 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) recursion++; GLPortal::EndFrame(di); recursion--; - RenderTranslucent(di); + di->RenderTranslucent(); } //----------------------------------------------------------------------------- @@ -508,9 +508,6 @@ void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen) sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { - GLRenderer->mSceneClearColor[0] = 0.0f; - GLRenderer->mSceneClearColor[1] = 0.0f; - GLRenderer->mSceneClearColor[2] = 0.0f; R_SetupFrame (mainvp, r_viewwindow, camera); GLRenderer->mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index 2382c550d..f300ee98f 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -13,15 +13,6 @@ struct HUDSprite; class GLSceneDrawer { - TMap weapondynlightindex; - - void RenderMultipassStuff(FDrawInfo *di); - - void RenderScene(FDrawInfo *di, int recursion); - void RenderTranslucent(FDrawInfo *di); - - void CreateScene(FDrawInfo *di); - public: GLSceneDrawer() { diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 96ea6a325..fc1aff738 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1016,6 +1016,10 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor screen->SetClearColor(color); SWRenderer->SetClearColor(color); } + else + { + screen->SetClearColor(GPalette.BlackIndex); + } // And finally some info that is needed for the hardware renderer From 3a80160ca06325a67d1860d47fd684e253942b8d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 11:38:45 +0200 Subject: [PATCH 25/58] More code from GLSceneDrawer to FDrawInfo --- src/gl/scene/gl_drawinfo.h | 4 +++ src/gl/scene/gl_scene.cpp | 50 +++++++++++++---------------------- src/gl/scene/gl_scenedrawer.h | 5 ---- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index 1eea98467..fb13b3224 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -108,6 +108,10 @@ struct FDrawInfo : public HWDrawInfo void CreateScene(); void RenderScene(int recursion); void RenderTranslucent(); + void DrawScene(int drawmode); + void ProcessScene(bool toscreen = false); + void EndDrawScene(sector_t * viewsector); + void DrawEndScene2D(sector_t * viewsector); static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp); FDrawInfo *EndDrawInfo(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 724337c5b..611b106e3 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,17 +72,6 @@ EXTERN_CVAR (Bool, r_deathcamera) EXTERN_CVAR (Float, r_visibility) EXTERN_CVAR (Bool, r_drawvoxels) -//----------------------------------------------------------------------------- -// -// resets the 3D viewport -// -//----------------------------------------------------------------------------- - -void GLSceneDrawer::Reset3DViewport() -{ - glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height); -} - //----------------------------------------------------------------------------- // // sets 3D viewport and initial state @@ -375,11 +364,11 @@ void FDrawInfo::RenderTranslucent() // //----------------------------------------------------------------------------- -void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) +void FDrawInfo::DrawScene(int drawmode) { static int recursion=0; static int ssao_portals_available = 0; - const auto &vp = di->Viewpoint; + const auto &vp = Viewpoint; bool applySSAO = false; if (drawmode == DM_MAINVIEW) @@ -400,15 +389,15 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) if (vp.camera != nullptr) { ActorRenderFlags savedflags = vp.camera->renderflags; - di->CreateScene(); + CreateScene(); vp.camera->renderflags = savedflags; } else { - di->CreateScene(); + CreateScene(); } - di->RenderScene(recursion); + RenderScene(recursion); if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS) { @@ -423,9 +412,9 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) // Handle all portals after rendering the opaque objects but before // doing all translucent stuff recursion++; - GLPortal::EndFrame(di); + GLPortal::EndFrame(this); recursion--; - di->RenderTranslucent(); + RenderTranslucent(); } //----------------------------------------------------------------------------- @@ -435,7 +424,7 @@ void GLSceneDrawer::DrawScene(FDrawInfo *di, int drawmode) //----------------------------------------------------------------------------- -void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector) +void FDrawInfo::EndDrawScene(sector_t * viewsector) { gl_RenderState.EnableFog(false); @@ -445,12 +434,11 @@ void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector) { // [BB] The HUD model should be drawn over everything else already drawn. glClear(GL_DEPTH_BUFFER_BIT); - di->DrawPlayerSprites(true); + DrawPlayerSprites(true); } glDisable(GL_STENCIL_TEST); - - Reset3DViewport(); + glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height); // Restore standard rendering state gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -459,7 +447,7 @@ void GLSceneDrawer::EndDrawScene(FDrawInfo *di, sector_t * viewsector) glDisable(GL_SCISSOR_TEST); } -void GLSceneDrawer::DrawEndScene2D(FDrawInfo *di, sector_t * viewsector) +void FDrawInfo::DrawEndScene2D(sector_t * viewsector) { const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); @@ -471,7 +459,7 @@ void GLSceneDrawer::DrawEndScene2D(FDrawInfo *di, sector_t * viewsector) glDisable(GL_MULTISAMPLE); - di->DrawPlayerSprites(false); + DrawPlayerSprites(false); gl_RenderState.SetSoftLightLevel(-1); @@ -488,15 +476,15 @@ void GLSceneDrawer::DrawEndScene2D(FDrawInfo *di, sector_t * viewsector) // //----------------------------------------------------------------------------- -void GLSceneDrawer::ProcessScene(FDrawInfo *di, bool toscreen) +void FDrawInfo::ProcessScene(bool toscreen) { iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; GLPortal::BeginScene(); - int mapsection = R_PointInSubsector(di->Viewpoint.Pos)->mapsection; - di->CurrentMapSections.Set(mapsection); + int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; + CurrentMapSections.Set(mapsection); GLRenderer->mCurrentPortal = nullptr; - DrawScene(di, toscreen ? DM_MAINVIEW : DM_OFFSCREEN); + DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); } @@ -540,12 +528,12 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); gl_RenderState.ApplyMatrices(); - ProcessScene(di, toscreen); + di->ProcessScene(toscreen); if (mainview) { - if (toscreen) EndDrawScene(di, mainvp.sector); // do not call this for camera textures. - GLRenderer->PostProcessScene(cm, [&]() { DrawEndScene2D(di, mainvp.sector); }); + if (toscreen) di->EndDrawScene(mainvp.sector); // do not call this for camera textures. + GLRenderer->PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); }); // This should be done after postprocessing, not before. GLRenderer->mBuffers->BindCurrentFB(); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index f300ee98f..e52cedebb 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -23,11 +23,6 @@ public: void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); void SetProjection(VSMatrix matrix); void Set3DViewport(bool mainview); - void Reset3DViewport(); - void DrawScene(FDrawInfo *di, int drawmode); - void ProcessScene(FDrawInfo *di, bool toscreen = false); - void EndDrawScene(FDrawInfo *di, sector_t * viewsector); - void DrawEndScene2D(FDrawInfo *di, sector_t * viewsector); sector_t *RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); sector_t *RenderView(player_t *player); From f7d8f321145f0a51f7842ba0d7e848be9662ffd2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 11:43:10 +0200 Subject: [PATCH 26/58] - removed some unused code from RenderViewpoint --- src/gl/scene/gl_scene.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 611b106e3..b46aad6bd 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -534,25 +534,12 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca { if (toscreen) di->EndDrawScene(mainvp.sector); // do not call this for camera textures. GLRenderer->PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); }); - - // This should be done after postprocessing, not before. - GLRenderer->mBuffers->BindCurrentFB(); - glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height); - - if (!toscreen) - { - gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.ortho(screen->mScreenViewport.left, screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mScreenViewport.top, -1.0f, 1.0f); - gl_RenderState.ApplyMatrices(); - } } di->EndDrawInfo(); GLRenderer->mDrawingScene2D = false; if (!stereo3dMode.IsMono()) GLRenderer->mBuffers->BlitToEyeTexture(eye_ix); - eye->TearDown(); } - stereo3dMode.TearDown(); interpolator.RestoreInterpolations (); return mainvp.sector; From 64ce4d50725989708c79d8f01bfd21ad53cfd56f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 11:46:25 +0200 Subject: [PATCH 27/58] - The Teardown virtuals aren't implemented anywhere so they can go away --- src/gl/stereo3d/gl_stereo3d.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h index ee632b31c..b7c4ef141 100644 --- a/src/gl/stereo3d/gl_stereo3d.h +++ b/src/gl/stereo3d/gl_stereo3d.h @@ -57,7 +57,6 @@ public: virtual Viewport GetViewport(const Viewport& fullViewport) const; virtual void GetViewShift(float yaw, float outViewShift[3]) const; virtual void SetUp() const {}; - virtual void TearDown() const {}; }; @@ -76,7 +75,6 @@ public: /* hooks for setup and cleanup operations for each stereo mode */ virtual void SetUp() const {}; - virtual void TearDown() const {}; virtual bool IsMono() const { return false; } virtual void AdjustViewports() const {}; From 469c9241eb9bf67b999835c090ef34bf14f7620d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 12:29:52 +0200 Subject: [PATCH 28/58] Moved WriteSavePic implementation to FGLRenderer --- src/gl/renderer/gl_renderer.cpp | 54 +++++++++++++++++++++++++++--- src/gl/scene/gl_scene.cpp | 44 ------------------------ src/hwrenderer/scene/hw_fakeflat.h | 2 +- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index c438f0934..c8c0fd271 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -361,14 +361,58 @@ void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, doub tex->SetUpdated(); } -void FGLRenderer::WriteSavePic(player_t *player, FileWriter *file, int width, int height) +//=========================================================================== +// +// Render the view to a savegame picture +// +//=========================================================================== + +void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { - // Todo: This needs to call the software renderer and process the returned image, if so desired. - // This also needs to take out parts of the scene drawer so they can be shared between renderers. - GLSceneDrawer drawer; - drawer.WriteSavePic(player, file, width, height); + IntRect bounds; + bounds.left = 0; + bounds.top = 0; + bounds.width = width; + bounds.height = height; + + // if GLRenderer->mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data. + glFinish(); + + // Switch to render buffers dimensioned for the savepic + mBuffers = mSaveBuffers; + + P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector. + gl_RenderState.SetVertexBuffer(mVBO); + mVBO->Reset(); + mLights->Clear(); + + // This shouldn't overwrite the global viewpoint even for a short time. + GLSceneDrawer drawer; + FRenderViewpoint savevp; + sector_t *viewsector = drawer.RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); + glDisable(GL_STENCIL_TEST); + gl_RenderState.SetSoftLightLevel(-1); + CopyToBackbuffer(&bounds, false); + + // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers + glFinish(); + + uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3); + glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr); + M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma); + M_Free(scr); + + // Switch back the screen render buffers + screen->SetViewportRects(nullptr); + mBuffers = mScreenBuffers; } +//=========================================================================== +// +// +// +//=========================================================================== + void FGLRenderer::BeginFrame() { buffersActive = GLRenderer->mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b46aad6bd..9d758b43f 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -545,47 +545,3 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca return mainvp.sector; } -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void GLSceneDrawer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) -{ - IntRect bounds; - bounds.left = 0; - bounds.top = 0; - bounds.width = width; - bounds.height = height; - - // if GLRenderer->mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data. - glFinish(); - - // Switch to render buffers dimensioned for the savepic - GLRenderer->mBuffers = GLRenderer->mSaveBuffers; - - P_FindParticleSubsectors(); // make sure that all recently spawned particles have a valid subsector. - gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); - GLRenderer->mVBO->Reset(); - GLRenderer->mLights->Clear(); - - // This shouldn't overwrite the global viewpoint even for a short time. - FRenderViewpoint savevp; - sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); - glDisable(GL_STENCIL_TEST); - gl_RenderState.SetSoftLightLevel(-1); - GLRenderer->CopyToBackbuffer(&bounds, false); - - // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers - glFinish(); - - uint8_t * scr = (uint8_t *)M_Malloc(width * height * 3); - glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr); - M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width * 3, Gamma); - M_Free(scr); - - // Switch back the screen render buffers - screen->SetViewportRects(nullptr); - GLRenderer->mBuffers = GLRenderer->mScreenBuffers; -} diff --git a/src/hwrenderer/scene/hw_fakeflat.h b/src/hwrenderer/scene/hw_fakeflat.h index 8e5b9dfec..c56f3798d 100644 --- a/src/hwrenderer/scene/hw_fakeflat.h +++ b/src/hwrenderer/scene/hw_fakeflat.h @@ -9,7 +9,7 @@ enum area_t : int }; -// Global functions. Make them members of GLRenderer later? +// Global functions. bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector); sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector); From f229b056752c0151032fe2f6610e0e9ae851c407 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 12:57:41 +0200 Subject: [PATCH 29/58] - Took more functions out of GLSceneDrawer --- src/gl/renderer/gl_renderer.cpp | 10 ++-- src/gl/renderer/gl_renderer.h | 5 +- src/gl/scene/gl_scene.cpp | 91 ++++++++++++++++----------------- src/gl/scene/gl_scenedrawer.h | 5 -- src/gl/stereo3d/gl_stereo3d.cpp | 1 - 5 files changed, 51 insertions(+), 61 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index c8c0fd271..7a8fb8b2c 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -322,10 +322,8 @@ sector_t *FGLRenderer::RenderView(player_t* player) fovratio = ratio; } - GLSceneDrawer drawer; - mShadowMap.Update(); - retsec = drawer.RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); + retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); } All.Unclock(); return retsec; @@ -352,9 +350,8 @@ void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, doub bounds.width = FHardwareTexture::GetTexDimension(gltex->GetWidth()); bounds.height = FHardwareTexture::GetTexDimension(gltex->GetHeight()); - GLSceneDrawer drawer; FRenderViewpoint texvp; - drawer.RenderViewpoint(texvp, Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false); + RenderViewpoint(texvp, Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false); EndOffscreen(); @@ -387,9 +384,8 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i mLights->Clear(); // This shouldn't overwrite the global viewpoint even for a short time. - GLSceneDrawer drawer; FRenderViewpoint savevp; - sector_t *viewsector = drawer.RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); + sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false); glDisable(GL_STENCIL_TEST); gl_RenderState.SetSoftLightLevel(-1); CopyToBackbuffer(&bounds, false); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 29e4e081d..b7a7afc0e 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -110,7 +110,6 @@ public: FLightBuffer *mLights; SWSceneDrawer *swdrawer = nullptr; - bool mDrawingScene2D = false; bool buffersActive = false; float mSceneClearColor[3]; @@ -148,6 +147,10 @@ public: void WriteSavePic(player_t *player, FileWriter *file, int width, int height); sector_t *RenderView(player_t *player); void BeginFrame(); + + void Set3DViewport(bool mainview); + sector_t *RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); + bool StartOffscreen(); void EndOffscreen(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 9d758b43f..0fca4eeaf 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,43 +72,6 @@ EXTERN_CVAR (Bool, r_deathcamera) EXTERN_CVAR (Float, r_visibility) EXTERN_CVAR (Bool, r_drawvoxels) -//----------------------------------------------------------------------------- -// -// sets 3D viewport and initial state -// -//----------------------------------------------------------------------------- - -void GLSceneDrawer::Set3DViewport(bool mainview) -{ - if (mainview && GLRenderer->buffersActive) - { - bool useSSAO = (gl_ssao != 0); - GLRenderer->mBuffers->BindSceneFB(useSSAO); - gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS); - gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); - gl_RenderState.Apply(); - } - - // Always clear all buffers with scissor test disabled. - // This is faster on newer hardware because it allows the GPU to skip - // reading from slower memory where the full buffers are stored. - glDisable(GL_SCISSOR_TEST); - glClearColor(GLRenderer->mSceneClearColor[0], GLRenderer->mSceneClearColor[1], GLRenderer->mSceneClearColor[2], 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - const auto &bounds = screen->mSceneViewport; - glViewport(bounds.left, bounds.top, bounds.width, bounds.height); - glScissor(bounds.left, bounds.top, bounds.width, bounds.height); - - glEnable(GL_SCISSOR_TEST); - - glEnable(GL_MULTISAMPLE); - glEnable(GL_DEPTH_TEST); - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS,0,~0); // default stencil - glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); -} - //----------------------------------------------------------------------------- // // SetProjection @@ -116,7 +79,7 @@ void GLSceneDrawer::Set3DViewport(bool mainview) // //----------------------------------------------------------------------------- -void GLSceneDrawer::SetProjection(VSMatrix matrix) +void SetProjection(VSMatrix matrix) { gl_RenderState.mProjectionMatrix.loadIdentity(); gl_RenderState.mProjectionMatrix.multMatrix(matrix); @@ -128,7 +91,7 @@ void GLSceneDrawer::SetProjection(VSMatrix matrix) // //----------------------------------------------------------------------------- -void GLSceneDrawer::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) +void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) { float mult = mirror? -1:1; float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch; @@ -488,17 +451,54 @@ void FDrawInfo::ProcessScene(bool toscreen) } +//----------------------------------------------------------------------------- +// +// sets 3D viewport and initial state +// +//----------------------------------------------------------------------------- + +void FGLRenderer::Set3DViewport(bool mainview) +{ + if (mainview && buffersActive) + { + bool useSSAO = (gl_ssao != 0); + mBuffers->BindSceneFB(useSSAO); + gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS); + gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); + gl_RenderState.Apply(); + } + + // Always clear all buffers with scissor test disabled. + // This is faster on newer hardware because it allows the GPU to skip + // reading from slower memory where the full buffers are stored. + glDisable(GL_SCISSOR_TEST); + glClearColor(mSceneClearColor[0], mSceneClearColor[1], mSceneClearColor[2], 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + const auto &bounds = screen->mSceneViewport; + glViewport(bounds.left, bounds.top, bounds.width, bounds.height); + glScissor(bounds.left, bounds.top, bounds.width, bounds.height); + + glEnable(GL_SCISSOR_TEST); + + glEnable(GL_MULTISAMPLE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS,0,~0); // default stencil + glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); +} + //----------------------------------------------------------------------------- // // Renders one viewpoint in a scene // //----------------------------------------------------------------------------- -sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { R_SetupFrame (mainvp, r_viewwindow, camera); - GLRenderer->mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); + mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); // Render (potentially) multiple views for stereo 3d float viewShift[3]; @@ -510,9 +510,8 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca eye->SetUp(); screen->SetViewportRects(bounds); Set3DViewport(mainview); - GLRenderer->mDrawingScene2D = true; - FDrawInfo *di = FDrawInfo::StartDrawInfo(this, mainvp); + FDrawInfo *di = FDrawInfo::StartDrawInfo(nullptr, mainvp); auto vp = di->Viewpoint; di->SetViewArea(); auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr); @@ -520,7 +519,6 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca // Stereo mode specific perspective projection SetProjection( eye->GetProjection(fov, ratio, fovratio) ); - // SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper vp.SetViewAngle(r_viewwindow); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); @@ -533,12 +531,11 @@ sector_t * GLSceneDrawer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * ca if (mainview) { if (toscreen) di->EndDrawScene(mainvp.sector); // do not call this for camera textures. - GLRenderer->PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); }); + PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); }); } di->EndDrawInfo(); - GLRenderer->mDrawingScene2D = false; if (!stereo3dMode.IsMono()) - GLRenderer->mBuffers->BlitToEyeTexture(eye_ix); + mBuffers->BlitToEyeTexture(eye_ix); } interpolator.RestoreInterpolations (); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index e52cedebb..219c5c0be 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -19,12 +19,7 @@ public: GLPortal::drawer = this; } - void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); - void SetProjection(VSMatrix matrix); - void Set3DViewport(bool mainview); - sector_t *RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); sector_t *RenderView(player_t *player); - void WriteSavePic(player_t *player, FileWriter *file, int width, int height); }; diff --git a/src/gl/stereo3d/gl_stereo3d.cpp b/src/gl/stereo3d/gl_stereo3d.cpp index 6fbc5e764..5a5a115d9 100644 --- a/src/gl/stereo3d/gl_stereo3d.cpp +++ b/src/gl/stereo3d/gl_stereo3d.cpp @@ -36,7 +36,6 @@ VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) co { VSMatrix result; - // Lifted from gl_scene.cpp FGLRenderer::SetProjection() float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); result.perspective(fovy, aspectRatio, FGLRenderer::GetZNear(), FGLRenderer::GetZFar()); From 7f5272c23f78bac0b94c6362e1202a17c055283c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 13:14:50 +0200 Subject: [PATCH 30/58] Deleted GLSceneDrawer Since it didn't contain any data anymore it was relatively pointless. Code has been moved to FDrawInfo and GLRenderer, depending on its scope. --- src/gl/renderer/gl_renderer.cpp | 2 +- src/gl/renderer/gl_renderer.h | 2 +- src/gl/scene/gl_drawinfo.cpp | 5 ++--- src/gl/scene/gl_drawinfo.h | 12 +++++++----- src/gl/scene/gl_flats.cpp | 1 - src/gl/scene/gl_portal.cpp | 30 ++++++++++++++---------------- src/gl/scene/gl_portal.h | 2 -- src/gl/scene/gl_scene.cpp | 13 ++++++------- src/gl/scene/gl_scenedrawer.h | 25 ------------------------- src/gl/scene/gl_skydome.cpp | 5 +++-- src/gl/scene/gl_sprite.cpp | 1 - src/gl/scene/gl_walls_draw.cpp | 1 - src/gl/scene/gl_weapon.cpp | 1 - 13 files changed, 34 insertions(+), 66 deletions(-) delete mode 100644 src/gl/scene/gl_scenedrawer.h diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 7a8fb8b2c..f442c882c 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -42,6 +42,7 @@ #include "gl_load/gl_interface.h" #include "gl/system/gl_framebuffer.h" #include "hwrenderer/utility/hw_cvars.h" +#include "gl/scene/gl_portal.h" #include "gl/system/gl_debug.h" #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_lightdata.h" @@ -49,7 +50,6 @@ #include "gl/renderer/gl_renderbuffers.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" -#include "gl/scene/gl_scenedrawer.h" #include "hwrenderer/postprocessing/hw_ambientshader.h" #include "hwrenderer/postprocessing/hw_bloomshader.h" #include "hwrenderer/postprocessing/hw_blurshader.h" diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index b7a7afc0e..2d3f1ae33 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -48,8 +48,8 @@ class FGL2DDrawer; class FHardwareTexture; class FShadowMapShader; class FCustomPostProcessShaders; -class GLSceneDrawer; class SWSceneDrawer; +struct FRenderViewpoint; #define NOQUEUE nullptr // just some token to be used as a placeholder enum diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 11ae0b825..20d816023 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -37,8 +37,8 @@ #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" +#include "hwrenderer/scene/hw_clipper.h" #include "gl/scene/gl_portal.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/renderer/gl_renderstate.h" #include "gl/stereo3d/scoped_color_mask.h" #include "gl/renderer/gl_quaddrawer.h" @@ -190,10 +190,9 @@ FDrawInfo::~FDrawInfo() // OpenGL has no use for multiple clippers so use the same one for all DrawInfos. static Clipper staticClipper; -FDrawInfo *FDrawInfo::StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp) +FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp) { FDrawInfo *di=di_list.GetNew(); - di->mDrawer = drawer; di->mVBO = GLRenderer->mVBO; di->mClipper = &staticClipper; di->Viewpoint = parentvp; diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index fb13b3224..ec84e711d 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -9,8 +9,6 @@ #pragma warning(disable:4244) #endif -class GLSceneDrawer; - enum DrawListType { GLDL_PLAINWALLS, @@ -36,8 +34,6 @@ enum Drawpasses struct FDrawInfo : public HWDrawInfo { - GLSceneDrawer *mDrawer; - FDrawInfo * next; HWDrawList drawlists[GLDL_TYPES]; TArray hudsprites; // These may just be stored by value. @@ -112,8 +108,14 @@ struct FDrawInfo : public HWDrawInfo void ProcessScene(bool toscreen = false); void EndDrawScene(sector_t * viewsector); void DrawEndScene2D(sector_t * viewsector); + + // These should go into hwrenderer later. + void SetProjection(VSMatrix matrix); + void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); + void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); - static FDrawInfo *StartDrawInfo(GLSceneDrawer *drawer, FRenderViewpoint &parentvp); + + static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp); FDrawInfo *EndDrawInfo(); gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector) diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 6438593f7..f7b0729f1 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -45,7 +45,6 @@ #include "gl/data/gl_vertexbuffer.h" #include "gl/dynlights/gl_lightbuffer.h" #include "gl/scene/gl_drawinfo.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/renderer/gl_quaddrawer.h" //========================================================================== diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index afdfc3c14..c531ca308 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -42,7 +42,6 @@ #include "gl/data/gl_vertexbuffer.h" #include "hwrenderer/scene/hw_clipper.h" #include "gl/scene/gl_portal.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/stereo3d/scoped_color_mask.h" //----------------------------------------------------------------------------- @@ -59,7 +58,6 @@ EXTERN_CVAR(Bool, gl_portals) EXTERN_CVAR(Bool, gl_noquery) EXTERN_CVAR(Int, r_mirror_recursions) -GLSceneDrawer *GLPortal::drawer; TArray GLPortal::portals; TArray GLPortal::planestack; int GLPortal::recursion; @@ -228,7 +226,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn return false; } } - *pDi = FDrawInfo::StartDrawInfo(drawer, outer_di->Viewpoint); + *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint); } else { @@ -257,7 +255,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn { if (NeedDepthBuffer()) { - *pDi = FDrawInfo::StartDrawInfo(drawer, outer_di->Viewpoint); + *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint); } else { @@ -327,7 +325,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -382,7 +380,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -589,13 +587,13 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) vp.ViewActor = origin; inskybox = true; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); di->SetViewArea(); ClearClipper(di); di->UpdateCurrentMapSection(); - drawer->DrawScene(di, DM_SKYPORTAL); + di->DrawScene(DM_SKYPORTAL); portal->mFlags &= ~PORTSF_INSKYBOX; inskybox = false; gl_RenderState.SetDepthClamp(oldclamp); @@ -696,7 +694,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); SetupCoverage(di); ClearClipper(di); @@ -709,7 +707,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) di->mClipper->SetBlocked(true); } - drawer->DrawScene(di, DM_PORTAL); + di->DrawScene(DM_PORTAL); if (origin->plane != -1) screen->instack[origin->plane]--; } @@ -752,13 +750,13 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); gl_RenderState.SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); - drawer->DrawScene(di, DM_PORTAL); + di->DrawScene(DM_PORTAL); gl_RenderState.SetClipHeight(0.f, 0.f); PlaneMirrorFlag--; PlaneMirrorMode = old_pm; @@ -923,7 +921,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) vp.ViewActor = nullptr; MirrorFlag++; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); di->mClipper->Clear(); @@ -934,7 +932,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) gl_RenderState.SetClipLine(linedef); gl_RenderState.EnableClipLine(true); - drawer->DrawScene(di, DM_PORTAL); + di->DrawScene(DM_PORTAL); gl_RenderState.EnableClipLine(false); MirrorFlag--; @@ -1000,12 +998,12 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) } vp.ViewActor = nullptr; - drawer->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); gl_RenderState.EnableClipLine(true); - drawer->DrawScene(di, DM_PORTAL); + di->DrawScene(DM_PORTAL); gl_RenderState.EnableClipLine(false); } diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index cc5b5d09d..cb05fb379 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -48,7 +48,6 @@ extern UniqueList UniqueSkies; extern UniqueList UniqueHorizons; extern UniqueList UniquePlaneMirrors; struct GLEEHorizonPortal; -class GLSceneDrawer; class GLPortal : public IPortal { @@ -62,7 +61,6 @@ protected: static int renderdepth; public: - static GLSceneDrawer *drawer; static int PlaneMirrorMode; static int inupperstack; static bool inskybox; diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 0fca4eeaf..27d517226 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -52,7 +52,6 @@ #include "hwrenderer/scene/hw_clipper.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/stereo3d/gl_stereo3d.h" #include "hwrenderer/utility/scoped_view_shifter.h" @@ -79,7 +78,7 @@ EXTERN_CVAR (Bool, r_drawvoxels) // //----------------------------------------------------------------------------- -void SetProjection(VSMatrix matrix) +void FDrawInfo::SetProjection(VSMatrix matrix) { gl_RenderState.mProjectionMatrix.loadIdentity(); gl_RenderState.mProjectionMatrix.multMatrix(matrix); @@ -91,7 +90,7 @@ void SetProjection(VSMatrix matrix) // //----------------------------------------------------------------------------- -void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) +void FDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) { float mult = mirror? -1:1; float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch; @@ -111,7 +110,7 @@ void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mi // Setup the view rotation matrix for the given viewpoint // //----------------------------------------------------------------------------- -void GLSceneDrawer::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) +void FDrawInfo::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) { vp.SetViewAngle(r_viewwindow); SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); @@ -511,19 +510,19 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came screen->SetViewportRects(bounds); Set3DViewport(mainview); - FDrawInfo *di = FDrawInfo::StartDrawInfo(nullptr, mainvp); + FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp); auto vp = di->Viewpoint; di->SetViewArea(); auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr); di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) // Stereo mode specific perspective projection - SetProjection( eye->GetProjection(fov, ratio, fovratio) ); + di->SetProjection( eye->GetProjection(fov, ratio, fovratio) ); vp.SetViewAngle(r_viewwindow); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); ScopedViewShifter viewShifter(vp.Pos, viewShift); - SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); + di->SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); gl_RenderState.ApplyMatrices(); di->ProcessScene(toscreen); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h deleted file mode 100644 index 219c5c0be..000000000 --- a/src/gl/scene/gl_scenedrawer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "r_defs.h" -#include "m_fixed.h" -#include "hwrenderer/scene/hw_clipper.h" -#include "gl_portal.h" -#include "gl/renderer/gl_lightdata.h" -#include "gl/renderer/gl_renderer.h" -#include "r_utility.h" -#include "c_cvars.h" - -struct HUDSprite; - -class GLSceneDrawer -{ -public: - GLSceneDrawer() - { - GLPortal::drawer = this; - } - - void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); - - sector_t *RenderView(player_t *player); -}; diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index a916c535a..59165ae31 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -32,9 +32,10 @@ #include "gl_load/gl_interface.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_renderstate.h" #include "gl/scene/gl_drawinfo.h" -#include "gl/scene/gl_scenedrawer.h" +#include "gl/scene/gl_portal.h" #include "gl/shaders/gl_shader.h" @@ -230,7 +231,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) bool oldClamp = gl_RenderState.SetDepthClamp(true); gl_MatrixStack.Push(gl_RenderState.mViewMatrix); - drawer->SetupView(vp, 0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp, 0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->bSkybox) diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 1a417c577..68765d91d 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -47,7 +47,6 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/renderer/gl_renderer.h" #include "gl/scene/gl_drawinfo.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/models/gl_models.h" #include "gl/renderer/gl_quaddrawer.h" #include "gl/dynlights/gl_lightbuffer.h" diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 117c42823..83f9aa2df 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -37,7 +37,6 @@ #include "gl/dynlights/gl_lightbuffer.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" -#include "gl/scene/gl_scenedrawer.h" EXTERN_CVAR(Bool, gl_seamless) diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index 8fe07eb00..5f44bf363 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -37,7 +37,6 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" -#include "gl/scene/gl_scenedrawer.h" #include "gl/models/gl_models.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/dynlights/gl_lightbuffer.h" From eb277cc101b8cfa87a5058cec7a70d3fd7ee3292 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 13:49:06 +0200 Subject: [PATCH 31/58] - calculate the normal view matrix at a higher level. --- src/gl/renderer/gl_renderstate.cpp | 5 ++++- src/gl/shaders/gl_shader.cpp | 21 +++++++++------------ src/gl/shaders/gl_shader.h | 4 ++-- src/hwrenderer/scene/hw_viewpointuniforms.h | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 src/hwrenderer/scene/hw_viewpointuniforms.h diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 9a5771982..69ada9f30 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -308,7 +308,10 @@ void FRenderState::ApplyMatrices() { if (GLRenderer->mShaderManager != NULL) { - GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType); + VSMatrix norm; + norm.computeNormalMatrix(mViewMatrix); + + GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, &norm, mPassType); } } diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 5293187fd..3641b8801 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -539,10 +539,10 @@ FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passT return nullptr; } -void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType) +void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm, EPassType passType) { if (passType < mPassShaders.Size()) - mPassShaders[passType]->ApplyMatrices(proj, view); + mPassShaders[passType]->ApplyMatrices(proj, view, norm); if (mActiveShader) mActiveShader->Bind(); @@ -687,28 +687,25 @@ FShader *FShaderCollection::BindEffect(int effect) //========================================================================== EXTERN_CVAR(Int, gl_fuzztype) -void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view) +void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm) { - VSMatrix norm; - norm.computeNormalMatrix(*view); - for (int i = 0; i < SHADER_NoTexture; i++) { - mMaterialShaders[i]->ApplyMatrices(proj, view, &norm); - mMaterialShadersNAT[i]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[i]->ApplyMatrices(proj, view, norm); + mMaterialShadersNAT[i]->ApplyMatrices(proj, view, norm); } - mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(proj, view, norm); if (gl_fuzztype != 0) { - mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(proj, view, norm); } for (unsigned i = FIRST_USER_SHADER; i < mMaterialShaders.Size(); i++) { - mMaterialShaders[i]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[i]->ApplyMatrices(proj, view, norm); } for (int i = 0; i < MAX_EFFECTS; i++) { - mEffectShaders[i]->ApplyMatrices(proj, view, &norm); + mEffectShaders[i]->ApplyMatrices(proj, view, norm); } } diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 6307fce8f..a3ca7eb7c 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -326,7 +326,7 @@ public: FShader *BindEffect(int effect, EPassType passType); FShader *Get(unsigned int eff, bool alphateston, EPassType passType); - void ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType); + void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm, EPassType passType); private: FShader *mActiveShader = nullptr; @@ -348,7 +348,7 @@ public: FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType); int Find(const char *mame); FShader *BindEffect(int effect); - void ApplyMatrices(VSMatrix *proj, VSMatrix *view); + void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm); FShader *Get(unsigned int eff, bool alphateston) { diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h new file mode 100644 index 000000000..5d0a888b8 --- /dev/null +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -0,0 +1,15 @@ +#pragma once + +#include "r_data/matrix.h" + +struct HWViewpointUniforms +{ + VSMatrix mProjectionMatrix; + VSMatrix mViewMatrix; + VSMatrix mNormalViewMatrix; + + void CalcDependencies() + { + mNormalViewMatrix.computeNormalMatrix(mViewMatrix); + } +}; From b106f72741faf1f8061386369991026e283742b4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 20 Jun 2018 22:18:31 +0200 Subject: [PATCH 32/58] - don't route calls to FMaterial::FlushAll through the framebuffer interface. --- src/gl/renderer/gl_renderer.cpp | 19 ++++--------------- src/gl/renderer/gl_renderer.h | 1 - src/gl/system/gl_framebuffer.cpp | 5 ----- src/gl/system/gl_framebuffer.h | 1 - src/hwrenderer/utility/hw_cvars.cpp | 5 +++-- src/posix/sdl/sdlglvideo.cpp | 1 - src/r_utility.cpp | 2 +- src/textures/hires/hqresize.cpp | 7 ++++--- src/v_video.h | 1 - src/win32/win32gliface.cpp | 1 - 10 files changed, 12 insertions(+), 31 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index f442c882c..6029c5139 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -236,17 +236,6 @@ void FGLRenderer::SetupLevel() // //=========================================================================== -void FGLRenderer::FlushTextures() -{ - FMaterial::FlushAll(); -} - -//=========================================================================== -// -// -// -//=========================================================================== - bool FGLRenderer::StartOffscreen() { bool firstBind = (mFBID == 0); @@ -372,7 +361,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i bounds.width = width; bounds.height = height; - // if GLRenderer->mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data. + // if mVBO is persistently mapped we must be sure the GPU finished reading from it before we fill it with new data. glFinish(); // Switch to render buffers dimensioned for the savepic @@ -411,9 +400,9 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i void FGLRenderer::BeginFrame() { - buffersActive = GLRenderer->mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height); + buffersActive = mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height); if (buffersActive) - buffersActive = GLRenderer->mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT); + buffersActive = mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT); } //=========================================================================== @@ -606,7 +595,7 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer) } glDisable(GL_SCISSOR_TEST); - gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); + gl_RenderState.SetVertexBuffer(mVBO); gl_RenderState.EnableTexture(true); gl_RenderState.EnableBrightmap(true); gl_RenderState.SetTextureMode(TM_MODULATE); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 2d3f1ae33..71368e644 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -123,7 +123,6 @@ public: void ClearBorders(); - void FlushTextures(); void SetupLevel(); void ResetSWScene(); diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 8b5f5acfc..da71df30c 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -361,11 +361,6 @@ void OpenGLFrameBuffer::UnbindTexUnit(int no) FHardwareTexture::Unbind(no); } -void OpenGLFrameBuffer::FlushTextures() -{ - if (GLRenderer) GLRenderer->FlushTextures(); -} - void OpenGLFrameBuffer::TextureFilterChanged() { if (GLRenderer != NULL && GLRenderer->mSamplerManager != NULL) GLRenderer->mSamplerManager->SetTextureFilterMode(); diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index d6f416d5e..dd002c929 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -37,7 +37,6 @@ public: IHardwareTexture *CreateHardwareTexture(FTexture *tex) override; FModelRenderer *CreateModelRenderer(int mli) override; void UnbindTexUnit(int no) override; - void FlushTextures() override; void TextureFilterChanged() override; void BeginFrame() override; void SetViewportRects(IntRect *bounds) override; diff --git a/src/hwrenderer/utility/hw_cvars.cpp b/src/hwrenderer/utility/hw_cvars.cpp index 26c061de7..c849e62d5 100644 --- a/src/hwrenderer/utility/hw_cvars.cpp +++ b/src/hwrenderer/utility/hw_cvars.cpp @@ -25,6 +25,7 @@ #include "c_dispatch.h" #include "v_video.h" #include "hw_cvars.h" +#include "hwrenderer/textures/hw_material.h" #include "menu/menu.h" @@ -91,7 +92,7 @@ CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCON CCMD(gl_flush) { - screen->FlushTextures(); + FMaterial::FlushAll(); } CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) @@ -102,7 +103,7 @@ CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINI CUSTOM_CVAR(Bool, gl_texture_usehires, true, CVAR_ARCHIVE|CVAR_NOINITCALL) { - screen->FlushTextures(); + FMaterial::FlushAll(); } CVAR(Bool, gl_precache, false, CVAR_ARCHIVE) diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index 83f0b1bae..4e7e0fbec 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -111,7 +111,6 @@ SDLGLVideo::SDLGLVideo (int parm) SDLGLVideo::~SDLGLVideo () { - if (GLRenderer != NULL) GLRenderer->FlushTextures(); } DFrameBuffer *SDLGLVideo::CreateFrameBuffer () diff --git a/src/r_utility.cpp b/src/r_utility.cpp index fc1aff738..02188cad3 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1030,7 +1030,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor double angx = cos(radPitch); double angy = sin(radPitch) * level.info->pixelstretch; double alen = sqrt(angx*angx + angy*angy); - viewpoint.HWAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); + viewpoint.HWAngles.Pitch = RAD2DEG((float)asin(angy / alen)); viewpoint.HWAngles.Roll.Degrees = viewpoint.Angles.Roll.Degrees; // copied for convenience. diff --git a/src/textures/hires/hqresize.cpp b/src/textures/hires/hqresize.cpp index eb121ced8..ccd025856 100644 --- a/src/textures/hires/hqresize.cpp +++ b/src/textures/hires/hqresize.cpp @@ -43,6 +43,7 @@ #include "xbr/xbrz.h" #include "xbr/xbrz_old.h" #include "parallel_for.h" +#include "hwrenderer/textures/hw_material.h" CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { @@ -56,18 +57,18 @@ CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR if (self == 8) self = 10; if (self == 9) self = 6; #endif - screen->FlushTextures(); + FMaterial::FlushAll(); } CUSTOM_CVAR(Int, gl_texture_hqresize_maxinputsize, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { if (self > 1024) self = 1024; - screen->FlushTextures(); + FMaterial::FlushAll(); } CUSTOM_CVAR(Int, gl_texture_hqresize_targets, 7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { - screen->FlushTextures(); + FMaterial::FlushAll(); } CVAR (Flag, gl_texture_hqresize_textures, gl_texture_hqresize_targets, 1); diff --git a/src/v_video.h b/src/v_video.h index 72208ec42..de16b85eb 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -419,7 +419,6 @@ public: virtual IHardwareTexture *CreateHardwareTexture(FTexture *tex) { return nullptr; } virtual FModelRenderer *CreateModelRenderer(int mli) { return nullptr; } virtual void UnbindTexUnit(int no) {} - virtual void FlushTextures() {} virtual void TextureFilterChanged() {} virtual void BeginFrame() {} diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index dd9c360b5..c8fc87ffb 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -155,7 +155,6 @@ Win32GLVideo::Win32GLVideo(int parm) Win32GLVideo::~Win32GLVideo() { - if (GLRenderer != NULL) GLRenderer->FlushTextures(); } //========================================================================== From 1967165633427aada58492cf0acff1ead65b5a98 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 21 Jun 2018 20:54:34 +0200 Subject: [PATCH 33/58] - move view and projection matrices to DrawInfo. --- src/gl/models/gl_models.cpp | 2 +- src/gl/models/gl_models.h | 4 +- src/gl/renderer/gl_lightdata.h | 2 +- src/gl/renderer/gl_postprocess.cpp | 4 +- src/gl/renderer/gl_renderer.cpp | 8 ++-- src/gl/renderer/gl_renderer.h | 2 +- src/gl/renderer/gl_renderstate.cpp | 13 ------- src/gl/renderer/gl_renderstate.h | 4 -- src/gl/scene/gl_drawinfo.cpp | 4 +- src/gl/scene/gl_drawinfo.h | 6 ++- src/gl/scene/gl_portal.cpp | 38 +++++++------------ src/gl/scene/gl_portal.h | 2 +- src/gl/scene/gl_scene.cpp | 42 +++++++++------------ src/gl/scene/gl_skydome.cpp | 4 +- src/gl/scene/gl_sprite.cpp | 2 +- src/gl/scene/gl_walls_draw.cpp | 1 - src/gl/scene/gl_weapon.cpp | 4 +- src/gl/stereo3d/gl_sidebyside3d.cpp | 7 ++-- src/gl/stereo3d/gl_sidebyside3d.h | 2 +- src/gl/stereo3d/gl_stereo3d.h | 2 +- src/gl/system/gl_framebuffer.cpp | 2 +- src/hwrenderer/scene/hw_drawinfo.h | 4 ++ src/hwrenderer/scene/hw_viewpointuniforms.h | 8 ++++ src/hwrenderer/scene/hw_weapon.h | 1 + src/r_utility.cpp | 2 +- src/swrenderer/scene/r_3dfloors.h | 2 +- wadsrc/static/shaders/glsl/main.vp | 2 +- 27 files changed, 80 insertions(+), 94 deletions(-) diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index 2ff7ff569..b92b832a8 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -50,7 +50,7 @@ CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) VSMatrix FGLModelRenderer::GetViewToWorldMatrix() { VSMatrix objectToWorldMatrix; - gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix); + di->VPUniforms.mViewMatrix.inverseMatrix(objectToWorldMatrix); return objectToWorldMatrix; } diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index 9cab5ecfc..fb044c49f 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -29,12 +29,14 @@ #include "r_data/models/models.h" class GLSprite; +struct FDrawInfo; class FGLModelRenderer : public FModelRenderer { int modellightindex = -1; + FDrawInfo *di; public: - FGLModelRenderer(int mli) : modellightindex(mli) + FGLModelRenderer(FDrawInfo *d, int mli) : modellightindex(mli), di(d) {} ModelRendererType GetType() const override { return GLModelRendererType; } void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) override; diff --git a/src/gl/renderer/gl_lightdata.h b/src/gl/renderer/gl_lightdata.h index db7a87e19..8fcd9809f 100644 --- a/src/gl/renderer/gl_lightdata.h +++ b/src/gl/renderer/gl_lightdata.h @@ -2,7 +2,7 @@ #define __GL_LIGHTDATA #include "v_palette.h" -#include "p_3dfloors.h" +#include "r_defs.h" #include "r_data/renderstyle.h" #include "hwrenderer/utility/hw_lighting.h" #include "r_data/colormaps.h" diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index ba9e605cb..61e69f2d7 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -81,7 +81,7 @@ void FGLRenderer::PostProcessScene(int fixedcm, const std::function &aft // //----------------------------------------------------------------------------- -void FGLRenderer::AmbientOccludeScene() +void FGLRenderer::AmbientOccludeScene(float m5) { FGLDebug::PushGroup("AmbientOccludeScene"); @@ -94,7 +94,7 @@ void FGLRenderer::AmbientOccludeScene() float aoStrength = gl_ssao_strength; //float tanHalfFovy = tan(fovy * (M_PI / 360.0f)); - float tanHalfFovy = 1.0f / gl_RenderState.mProjectionMatrix.get()[5]; + float tanHalfFovy = 1.0f / m5; float invFocalLenX = tanHalfFovy * (mBuffers->GetSceneWidth() / (float)mBuffers->GetSceneHeight()); float invFocalLenY = tanHalfFovy; float nDotVBias = clamp(bias, 0.0f, 1.0f); diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6029c5139..6bd2a6188 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -467,9 +467,11 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer) const auto &mScreenViewport = screen->mScreenViewport; glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); - gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); - gl_RenderState.ApplyMatrices(); + HWViewpointUniforms matrices; + matrices.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); + matrices.mViewMatrix.loadIdentity(); + matrices.CalcDependencies(); + GLRenderer->mShaderManager->ApplyMatrices(&matrices.mProjectionMatrix, &matrices.mViewMatrix, &matrices.mNormalViewMatrix, NORMAL_PASS); glDisable(GL_DEPTH_TEST); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 71368e644..4cd28c730 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -128,7 +128,7 @@ public: void RenderScreenQuad(); void PostProcessScene(int fixedcm, const std::function &afterBloomDrawEndScene2D); - void AmbientOccludeScene(); + void AmbientOccludeScene(float m5); void UpdateCameraExposure(); void BloomScene(int fixedcm); void TonemapScene(); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 69ada9f30..0d891efc8 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -107,8 +107,6 @@ void FRenderState::Reset() mDynColor.Set(0.0f, 0.0f, 0.0f, 0.0f); mEffectState = 0; activeShader = nullptr; - mProjectionMatrix.loadIdentity(); - mViewMatrix.loadIdentity(); mModelMatrix.loadIdentity(); mTextureMatrix.loadIdentity(); mPassType = NORMAL_PASS; @@ -304,17 +302,6 @@ void FRenderState::ApplyColorMask() } } -void FRenderState::ApplyMatrices() -{ - if (GLRenderer->mShaderManager != NULL) - { - VSMatrix norm; - norm.computeNormalMatrix(mViewMatrix); - - GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, &norm, mPassType); - } -} - void FRenderState::ApplyLightIndex(int index) { if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER) diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index b188b42d2..21d0f2265 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -131,11 +131,8 @@ class FRenderState public: - VSMatrix mProjectionMatrix; - VSMatrix mViewMatrix; VSMatrix mModelMatrix; VSMatrix mTextureMatrix; - VSMatrix mNormalViewMatrix; FRenderState() { @@ -162,7 +159,6 @@ public: void Apply(); void ApplyColorMask(); - void ApplyMatrices(); void ApplyLightIndex(int index); void SetVertexBuffer(FVertexBuffer *vb) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 20d816023..da5c5fa38 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -190,12 +190,14 @@ FDrawInfo::~FDrawInfo() // OpenGL has no use for multiple clippers so use the same one for all DrawInfos. static Clipper staticClipper; -FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp) +FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms) { FDrawInfo *di=di_list.GetNew(); di->mVBO = GLRenderer->mVBO; di->mClipper = &staticClipper; di->Viewpoint = parentvp; + if (uniforms) di->VPUniforms = *uniforms; + else di->VPUniforms.SetDefaults(); di->mClipper->SetViewpoint(di->Viewpoint); staticClipper.Clear(); di->StartScene(); diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index ec84e711d..988e5b939 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -4,6 +4,7 @@ #include "gl/renderer/gl_lightdata.h" #include "hwrenderer/scene/hw_drawlist.h" #include "hwrenderer/scene/hw_weapon.h" +#include "hwrenderer/scene/hw_viewpointuniforms.h" #ifdef _MSC_VER #pragma warning(disable:4244) @@ -42,6 +43,8 @@ struct FDrawInfo : public HWDrawInfo FDrawInfo(); ~FDrawInfo(); + void ApplyVPUniforms() override; + void AddWall(GLWall *wall) override; void AddMirrorSurface(GLWall *w) override; GLDecal *AddDecal(bool onmirror) override; @@ -110,12 +113,11 @@ struct FDrawInfo : public HWDrawInfo void DrawEndScene2D(sector_t * viewsector); // These should go into hwrenderer later. - void SetProjection(VSMatrix matrix); void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); void SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); - static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp); + static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); FDrawInfo *EndDrawInfo(); gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector) diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index c531ca308..d516e37ee 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -93,15 +93,13 @@ void GLPortal::BeginScene() // // //========================================================================== -void GLPortal::ClearScreen() +void GLPortal::ClearScreen(FDrawInfo *di) { bool multi = !!glIsEnabled(GL_MULTISAMPLE); - gl_MatrixStack.Push(gl_RenderState.mViewMatrix); - gl_MatrixStack.Push(gl_RenderState.mProjectionMatrix); - gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.ortho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1.0f, 1.0f); - gl_RenderState.ApplyMatrices(); + di->VPUniforms.mViewMatrix.loadIdentity(); + di->VPUniforms.mProjectionMatrix.ortho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1.0f, 1.0f); + di->ApplyVPUniforms(); gl_RenderState.SetColor(0, 0, 0); gl_RenderState.Apply(); @@ -111,9 +109,6 @@ void GLPortal::ClearScreen() glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4); glEnable(GL_DEPTH_TEST); - gl_MatrixStack.Pop(gl_RenderState.mProjectionMatrix); - gl_MatrixStack.Pop(gl_RenderState.mViewMatrix); - gl_RenderState.ApplyMatrices(); if (multi) glEnable(GL_MULTISAMPLE); } @@ -162,6 +157,8 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn *pDi = nullptr; rendered_portals++; Clocker c(PortalAll); + + *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms); if (usestencil) { if (!gl_portals) @@ -226,7 +223,6 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn return false; } } - *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint); } else { @@ -244,7 +240,6 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn gl_RenderState.SetEffect(EFF_NONE); glDisable(GL_DEPTH_TEST); glDepthMask(false); // don't write to Z-buffer! - *pDi = outer_di; } } recursion++; @@ -253,15 +248,10 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn } else { - if (NeedDepthBuffer()) - { - *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint); - } - else + if (!NeedDepthBuffer()) { glDepthMask(false); glDisable(GL_DEPTH_TEST); - *pDi = outer_di; } } @@ -318,9 +308,10 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) if (PrevPortal != nullptr) PrevPortal->PopState(); GLRenderer->mCurrentPortal = PrevPortal; + di = di->EndDrawInfo(); + di->ApplyVPUniforms(); if (usestencil) { - if (needdepth) di = di->EndDrawInfo(); auto &vp = di->Viewpoint; // Restore the old view @@ -368,7 +359,6 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) { if (needdepth) { - di = di->EndDrawInfo(); glClear(GL_DEPTH_BUFFER_BIT); } else @@ -561,7 +551,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) if (skyboxrecursion >= 3) { - ClearScreen(); + ClearScreen(di); return; } auto &vp = di->Viewpoint; @@ -732,7 +722,7 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) { if (renderdepth > r_mirror_recursions) { - ClearScreen(); + ClearScreen(di); return; } // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. @@ -856,7 +846,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) { if (renderdepth>r_mirror_recursions) { - ClearScreen(); + ClearScreen(di); return; } @@ -958,7 +948,7 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) // TODO: Handle recursion more intelligently if (renderdepth>r_mirror_recursions) { - ClearScreen(); + ClearScreen(di); return; } auto &vp = di->Viewpoint; @@ -1111,7 +1101,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) gltexture=FMaterial::ValidateTexture(sp->texture, false, true); if (!gltexture) { - ClearScreen(); + ClearScreen(di); return; } gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index cb05fb379..a6d153aa7 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -88,7 +88,7 @@ protected: virtual bool IsSky() { return false; } virtual bool NeedCap() { return true; } virtual bool NeedDepthBuffer() { return true; } - void ClearScreen(); + void ClearScreen(FDrawInfo *di); virtual const char *GetName() = 0; virtual void PushState() {} virtual void PopState() {} diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 27d517226..75874aafe 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -71,17 +71,11 @@ EXTERN_CVAR (Bool, r_deathcamera) EXTERN_CVAR (Float, r_visibility) EXTERN_CVAR (Bool, r_drawvoxels) -//----------------------------------------------------------------------------- -// -// SetProjection -// sets projection matrix -// -//----------------------------------------------------------------------------- -void FDrawInfo::SetProjection(VSMatrix matrix) +void FDrawInfo::ApplyVPUniforms() { - gl_RenderState.mProjectionMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.multMatrix(matrix); + VPUniforms.CalcDependencies(); + GLRenderer->mShaderManager->ApplyMatrices(&VPUniforms.mProjectionMatrix, &VPUniforms.mViewMatrix, &VPUniforms.mNormalViewMatrix, NORMAL_PASS); } //----------------------------------------------------------------------------- @@ -95,12 +89,12 @@ void FDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float float mult = mirror? -1:1; float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch; - gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); - gl_RenderState.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); - gl_RenderState.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); - gl_RenderState.mViewMatrix.translate(vx * mult, -vz * planemult , -vy); - gl_RenderState.mViewMatrix.scale(-mult, planemult, 1); + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); + VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult , -vy); + VPUniforms.mViewMatrix.scale(-mult, planemult, 1); } @@ -114,7 +108,7 @@ void FDrawInfo::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DA { vp.SetViewAngle(r_viewwindow); SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); - gl_RenderState.ApplyMatrices(); + ApplyVPUniforms(); } //----------------------------------------------------------------------------- @@ -364,11 +358,11 @@ void FDrawInfo::DrawScene(int drawmode) if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS) { gl_RenderState.EnableDrawBuffers(1); - GLRenderer->AmbientOccludeScene(); + GLRenderer->AmbientOccludeScene(VPUniforms.mProjectionMatrix.get()[5]); GLRenderer->mBuffers->BindSceneFB(true); gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); gl_RenderState.Apply(); - gl_RenderState.ApplyMatrices(); + ApplyVPUniforms(); } // Handle all portals after rendering the opaque objects but before @@ -414,9 +408,9 @@ void FDrawInfo::DrawEndScene2D(sector_t * viewsector) const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); // This should be removed once all 2D stuff is really done through the 2D interface. - gl_RenderState.mViewMatrix.loadIdentity(); - gl_RenderState.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); - gl_RenderState.ApplyMatrices(); + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); + ApplyVPUniforms(); glDisable(GL_DEPTH_TEST); glDisable(GL_MULTISAMPLE); @@ -510,20 +504,20 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came screen->SetViewportRects(bounds); Set3DViewport(mainview); - FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp); + FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp, nullptr); auto vp = di->Viewpoint; di->SetViewArea(); auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr); di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) // Stereo mode specific perspective projection - di->SetProjection( eye->GetProjection(fov, ratio, fovratio) ); + di->VPUniforms.mProjectionMatrix = eye->GetProjection(fov, ratio, fovratio); vp.SetViewAngle(r_viewwindow); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); ScopedViewShifter viewShifter(vp.Pos, viewShift); di->SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); - gl_RenderState.ApplyMatrices(); + di->ApplyVPUniforms(); di->ProcessScene(toscreen); diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 59165ae31..1ca523705 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -230,7 +230,6 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bool oldClamp = gl_RenderState.SetDepthClamp(true); - gl_MatrixStack.Push(gl_RenderState.mViewMatrix); di->SetupView(vp, 0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); @@ -270,8 +269,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) } } gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); - gl_MatrixStack.Pop(gl_RenderState.mViewMatrix); - gl_RenderState.ApplyMatrices(); + di->ApplyVPUniforms(); ::level.lightmode = oldlightmode; gl_RenderState.SetDepthClamp(oldClamp); } diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 68765d91d..a17241142 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -277,7 +277,7 @@ void FDrawInfo::DrawSprite(GLSprite *sprite, int pass) } else { - FGLModelRenderer renderer(sprite->dynlightindex); + FGLModelRenderer renderer(this, sprite->dynlightindex); renderer.RenderModel(sprite->x, sprite->y, sprite->z, sprite->modelframe, sprite->actor, vp.TicFrac); } } diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 83f9aa2df..8c989313b 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -93,7 +93,6 @@ void FDrawInfo::RenderMirrorSurface(GLWall *wall) // we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is. gl_RenderState.EnableTextureMatrix(true); - gl_RenderState.mTextureMatrix.computeNormalMatrix(gl_RenderState.mViewMatrix); // Use sphere mapping for this gl_RenderState.SetEffect(EFF_SPHEREMAP); diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index 5f44bf363..fdc10ef19 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -65,7 +65,7 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds) if (huds->mframe) { gl_RenderState.AlphaFunc(GL_GEQUAL, 0); - FGLModelRenderer renderer(huds->lightindex); + FGLModelRenderer renderer(this, huds->lightindex); renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my); } else @@ -91,7 +91,7 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds) void FDrawInfo::DrawPlayerSprites(bool hudModelStep) { - s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites(); + s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites(this); int oldlightmode = level.lightmode; if (!hudModelStep && level.lightmode == 8) level.lightmode = 2; // Software lighting cannot handle 2D content so revert to lightmode 2 for that. diff --git a/src/gl/stereo3d/gl_sidebyside3d.cpp b/src/gl/stereo3d/gl_sidebyside3d.cpp index a8a216dd8..4f18033e1 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.cpp +++ b/src/gl/stereo3d/gl_sidebyside3d.cpp @@ -33,6 +33,7 @@ ** */ +#include "gl/scene/gl_drawinfo.h" #include "gl_sidebyside3d.h" #include "gl/renderer/gl_renderbuffers.h" @@ -98,13 +99,13 @@ SideBySideFull::SideBySideFull(double ipdMeters) } /* virtual */ -void SideBySideFull::AdjustPlayerSprites() const /* override */ +void SideBySideFull::AdjustPlayerSprites(FDrawInfo *di) const /* override */ { // Show weapon at double width, so it would appear normal width after rescaling int w = screen->mScreenViewport.width; int h = screen->mScreenViewport.height; - gl_RenderState.mProjectionMatrix.ortho(w/2, w + w/2, h, 0, -1.0f, 1.0f); - gl_RenderState.ApplyMatrices(); + di->VPUniforms.mProjectionMatrix.ortho(w/2, w + w/2, h, 0, -1.0f, 1.0f); + di->ApplyVPUniforms(); } /* static */ diff --git a/src/gl/stereo3d/gl_sidebyside3d.h b/src/gl/stereo3d/gl_sidebyside3d.h index 78c07d0ad..071474c46 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.h +++ b/src/gl/stereo3d/gl_sidebyside3d.h @@ -82,7 +82,7 @@ class SideBySideFull : public SideBySideBase public: static const SideBySideFull& getInstance(float ipd); SideBySideFull(double ipdMeters); - virtual void AdjustPlayerSprites() const override; + virtual void AdjustPlayerSprites(FDrawInfo *di) const override; private: SBSFLeftEyePose leftEye; SBSFRightEyePose rightEye; diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h index b7c4ef141..542d45551 100644 --- a/src/gl/stereo3d/gl_stereo3d.h +++ b/src/gl/stereo3d/gl_stereo3d.h @@ -78,7 +78,7 @@ public: virtual bool IsMono() const { return false; } virtual void AdjustViewports() const {}; - virtual void AdjustPlayerSprites() const {}; + virtual void AdjustPlayerSprites(FDrawInfo *di) const {}; virtual void Present() const = 0; protected: diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index da71df30c..37e5f9f45 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -342,7 +342,7 @@ IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture(FTexture *tex) FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli) { - return new FGLModelRenderer(mli); + return new FGLModelRenderer(nullptr, mli); } IUniformBuffer *OpenGLFrameBuffer::CreateUniformBuffer(size_t size, bool staticuse) diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 317fea51d..11c3c8e5f 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -3,6 +3,7 @@ #include #include "r_defs.h" #include "r_utility.h" +#include "hw_viewpointuniforms.h" struct FSectorPortalGroup; @@ -19,6 +20,7 @@ struct HUDSprite; class Clipper; class IPortal; class FFlatVertexGenerator; +class IRenderQueue; //========================================================================== // @@ -99,6 +101,7 @@ struct HWDrawInfo IShadowMap *mShadowMap; Clipper *mClipper; FRenderViewpoint Viewpoint; + HWViewpointUniforms VPUniforms; // per-viewpoint uniform state TArray MissingUpperTextures; TArray MissingLowerTextures; @@ -207,6 +210,7 @@ public: virtual void AddHUDSprite(HUDSprite *huds) = 0; virtual int UploadLights(FDynLightData &data) = 0; + virtual void ApplyVPUniforms() = 0; virtual GLDecal *AddDecal(bool onmirror) = 0; virtual std::pair AllocVertices(unsigned int count) = 0; diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 5d0a888b8..1fdbcc4d1 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -12,4 +12,12 @@ struct HWViewpointUniforms { mNormalViewMatrix.computeNormalMatrix(mViewMatrix); } + + void SetDefaults() + { + mProjectionMatrix.loadIdentity(); + mViewMatrix.loadIdentity(); + mNormalViewMatrix.loadIdentity(); + } + }; diff --git a/src/hwrenderer/scene/hw_weapon.h b/src/hwrenderer/scene/hw_weapon.h index 935aabb75..62c5a70a0 100644 --- a/src/hwrenderer/scene/hw_weapon.h +++ b/src/hwrenderer/scene/hw_weapon.h @@ -8,6 +8,7 @@ class AActor; enum area_t : int; struct FSpriteModelFrame; struct HWDrawInfo; +class FMaterial; struct WeaponPosition diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 02188cad3..8de8727f1 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1032,7 +1032,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor double alen = sqrt(angx*angx + angy*angy); viewpoint.HWAngles.Pitch = RAD2DEG((float)asin(angy / alen)); - viewpoint.HWAngles.Roll.Degrees = viewpoint.Angles.Roll.Degrees; // copied for convenience. + viewpoint.HWAngles.Roll.Degrees = (float)viewpoint.Angles.Roll.Degrees; // copied for convenience. // ViewActor only gets set, if the camera actor should not be rendered if (actor->player && actor->player - players == consoleplayer && diff --git a/src/swrenderer/scene/r_3dfloors.h b/src/swrenderer/scene/r_3dfloors.h index 0237ca95e..a74e1fc53 100644 --- a/src/swrenderer/scene/r_3dfloors.h +++ b/src/swrenderer/scene/r_3dfloors.h @@ -1,7 +1,7 @@ #pragma once -#include "p_3dfloors.h" +#include "r_defs.h" #include EXTERN_CVAR(Int, r_3dfloors); diff --git a/wadsrc/static/shaders/glsl/main.vp b/wadsrc/static/shaders/glsl/main.vp index db57c67a4..1089cadcb 100644 --- a/wadsrc/static/shaders/glsl/main.vp +++ b/wadsrc/static/shaders/glsl/main.vp @@ -68,7 +68,7 @@ void main() #ifdef SPHEREMAP vec3 u = normalize(eyeCoordPos.xyz); - vec4 n = normalize(TextureMatrix * vec4(parmTexCoord.x, 0.0, parmTexCoord.y, 0.0)); // use texture matrix and coordinates for our normal. Since this is only used on walls, the normal's y coordinate is always 0. + vec4 n = normalize(NormalViewMatrix * vec4(parmTexCoord.x, 0.0, parmTexCoord.y, 0.0)); vec3 r = reflect(u, n.xyz); float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); vec2 sst = vec2(r.x/m + 0.5, r.y/m + 0.5); From 94861808438c593e9e231504c5ee4089be01f01c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 21 Jun 2018 21:36:12 +0200 Subject: [PATCH 34/58] - simplify the interface. --- src/gl/renderer/gl_renderer.cpp | 2 +- src/gl/scene/gl_scene.cpp | 2 +- src/gl/shaders/gl_shader.cpp | 27 ++++++++++++++------------- src/gl/shaders/gl_shader.h | 7 ++++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6bd2a6188..55009e2ce 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -471,7 +471,7 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer) matrices.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); matrices.mViewMatrix.loadIdentity(); matrices.CalcDependencies(); - GLRenderer->mShaderManager->ApplyMatrices(&matrices.mProjectionMatrix, &matrices.mViewMatrix, &matrices.mNormalViewMatrix, NORMAL_PASS); + GLRenderer->mShaderManager->ApplyMatrices(&matrices, NORMAL_PASS); glDisable(GL_DEPTH_TEST); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 75874aafe..10c67c809 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -75,7 +75,7 @@ EXTERN_CVAR (Bool, r_drawvoxels) void FDrawInfo::ApplyVPUniforms() { VPUniforms.CalcDependencies(); - GLRenderer->mShaderManager->ApplyMatrices(&VPUniforms.mProjectionMatrix, &VPUniforms.mViewMatrix, &VPUniforms.mNormalViewMatrix, NORMAL_PASS); + GLRenderer->mShaderManager->ApplyMatrices(&VPUniforms, NORMAL_PASS); } //----------------------------------------------------------------------------- diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 3641b8801..f50fb595a 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -34,6 +34,7 @@ #include "cmdlib.h" #include "hwrenderer/utility/hw_shaderpatcher.h" #include "hwrenderer/data/shaderuniforms.h" +#include "hwrenderer/scene/hw_viewpointuniforms.h" #include "gl_load/gl_interface.h" #include "gl/system/gl_debug.h" @@ -434,12 +435,12 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP // //========================================================================== -void FShader::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm) +void FShader::ApplyMatrices(HWViewpointUniforms *u) { Bind(); - glUniformMatrix4fv(projectionmatrix_index, 1, false, proj->get()); - glUniformMatrix4fv(viewmatrix_index, 1, false, view->get()); - glUniformMatrix4fv(normalviewmatrix_index, 1, false, norm->get()); + glUniformMatrix4fv(projectionmatrix_index, 1, false, u->mProjectionMatrix.get()); + glUniformMatrix4fv(viewmatrix_index, 1, false, u->mViewMatrix.get()); + glUniformMatrix4fv(normalviewmatrix_index, 1, false, u->mNormalViewMatrix.get()); } //========================================================================== @@ -539,10 +540,10 @@ FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passT return nullptr; } -void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm, EPassType passType) +void FShaderManager::ApplyMatrices(HWViewpointUniforms *u, EPassType passType) { if (passType < mPassShaders.Size()) - mPassShaders[passType]->ApplyMatrices(proj, view, norm); + mPassShaders[passType]->ApplyMatrices(u); if (mActiveShader) mActiveShader->Bind(); @@ -687,25 +688,25 @@ FShader *FShaderCollection::BindEffect(int effect) //========================================================================== EXTERN_CVAR(Int, gl_fuzztype) -void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm) +void FShaderCollection::ApplyMatrices(HWViewpointUniforms *u) { for (int i = 0; i < SHADER_NoTexture; i++) { - mMaterialShaders[i]->ApplyMatrices(proj, view, norm); - mMaterialShadersNAT[i]->ApplyMatrices(proj, view, norm); + mMaterialShaders[i]->ApplyMatrices(u); + mMaterialShadersNAT[i]->ApplyMatrices(u); } - mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(proj, view, norm); + mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(u); if (gl_fuzztype != 0) { - mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(proj, view, norm); + mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(u); } for (unsigned i = FIRST_USER_SHADER; i < mMaterialShaders.Size(); i++) { - mMaterialShaders[i]->ApplyMatrices(proj, view, norm); + mMaterialShaders[i]->ApplyMatrices(u); } for (int i = 0; i < MAX_EFFECTS; i++) { - mEffectShaders[i]->ApplyMatrices(proj, view, norm); + mEffectShaders[i]->ApplyMatrices(u); } } diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index a3ca7eb7c..10503f527 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -38,6 +38,7 @@ enum }; class FShaderCollection; +struct HWViewpointUniforms; //========================================================================== // @@ -306,7 +307,7 @@ public: bool Bind(); unsigned int GetHandle() const { return hShader; } - void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm); + void ApplyMatrices(HWViewpointUniforms *u); }; @@ -326,7 +327,7 @@ public: FShader *BindEffect(int effect, EPassType passType); FShader *Get(unsigned int eff, bool alphateston, EPassType passType); - void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm, EPassType passType); + void ApplyMatrices(HWViewpointUniforms *u, EPassType passType); private: FShader *mActiveShader = nullptr; @@ -348,7 +349,7 @@ public: FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType); int Find(const char *mame); FShader *BindEffect(int effect); - void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm); + void ApplyMatrices(HWViewpointUniforms *u); FShader *Get(unsigned int eff, bool alphateston) { From 9b4e8efcb9033be0d477d9e05fd5e84415438741 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 22 Jun 2018 15:40:28 +0300 Subject: [PATCH 35/58] - fixed potential access to freed memory on map loading MapData could destruct FResourceLump objects before accessing them Loading of map .wad from .pk3 file is example of this case https://forum.zdoom.org/viewtopic.php?t=60972 --- src/p_setup.h | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/p_setup.h b/src/p_setup.h index 6b961bfa7..b9c680632 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -36,6 +36,25 @@ struct MapData { private: + struct ResourceHolder + { + FResourceFile *data = nullptr; + + ~ResourceHolder() + { + delete data; + } + + ResourceHolder &operator=(FResourceFile *other) { data = other; return *this; } + FResourceFile *operator->() { return data; } + operator FResourceFile *() const { return data; } + }; + + // The order of members here is important + // Resource should be destructed after MapLumps as readers may share FResourceLump objects + // For example, this is the case when map .wad is loaded from .pk3 file + ResourceHolder resource; + struct MapLump { char Name[8] = { 0 }; @@ -48,13 +67,6 @@ public: bool isText = false; bool InWad = false; int lumpnum = -1; - FResourceFile * resource = nullptr; - - ~MapData() - { - if (resource != nullptr) delete resource; - resource = nullptr; - } /* void Seek(unsigned int lumpindex) From ceec12056a7868c71ce8a3a8922af2abd8fad556 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Wed, 20 Jun 2018 00:48:42 +0300 Subject: [PATCH 36/58] Upgrade libADLMIDI and libOPNMIDI Added ability to switch emulator and it's accuracy level ("enabling of 'run at PCM rate' reduces accuracy, and also reduces CPU usage") Added draft code for future external banks support (WOPL format for ADLMIDI and WOPN format for OPNMIDI) ADLMIDI 1.3.3 2018-06-19 * Fixed an inability to load another custom bank without of library re-initialization * Optimizing the MIDI banks management system for MultiBanks (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Fixed incorrect 4-op counter which is still catch 4-op instruments on 2-op banks * Fixed an incorrect processing of auto-flags * Fixed incorrect initial MIDI tempo when MIDI file doesn't includes the tempo event * Channel and Note Aftertouch features are now supported correctly! Aftertouch is the tremolo / vibrato, NOT A VOLUME! * Updated DosBox OPL3 emulator up to r4111 of official DosBox trunk (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * The automatical choosing of 4 operator channels count has been improved (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Added optional HQ resampler for Nuked OPL3 emulators which does usage of Zita-Resampler library (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) ADLMIDI 1.3.2 2018-04-24 * Added ability to disable MUS and XMI converters * Added ability to disable embedded MIDI sequencer to use library as RealTime synthesizer only or use any custom MIDI sequencer plugins. * Fixed blank instruments fallback in multi-bank support. When using non-zero bank, if instrument is blank, then, instrument will be taken from a root (I.e. zero bank). * Added support for real-time switching the emulator * Added support for CC-120 - "All sound off" on the MIDI channel * Changed logic of CC-74 Brightness to affect sound only between 0 and 64 like real XG synthesizers. Ability to turn on a full-ranged brightness (to use full 0...127 range) is kept. * Added support for different output sample formats (PCM8, PCM8U, PCM16, PCM16U, PCM32, PCM32U, Float32, and Float64) (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Reworked MIDI channels management to avoid any memory reallocations while music processing for a hard real time. (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) OPNMIDI 1.3.0 2018-06-19 * Optimizing the MIDI banks management system for MultiBanks (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Fixed incorrect initial MIDI tempo when MIDI file doesn't includes the tempo event * Fixed an incorrect processing of auto-flags * MAME YM2612 now results a more accurate sound as internal using of native sample rate makes more correct sound generation * Channel and Note Aftertouch features are now supported correctly! Aftertouch is the tremolo / vibrato, NOT A VOLUME! * Added optional HQ resampler for Nuked OPL3 emulators which does usage of Zita-Resampler library (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) OPNMIDI 1.2.0 2018-04-24 * Added ability to disable MUS and XMI converters * Added ability to disable embedded MIDI sequencer to use library as RealTime synthesizer only or use any custom MIDI sequencer plugins. * Fixed blank instruments fallback in multi-bank support. When using non-zero bank, if instrument is blank, then, instrument will be taken from a root (I.e. zero bank). * Added support for real-time switching the emulator * Added support for MAME YM2612 Emulator * Added support for CC-120 - "All sound off" on the MIDI channel * Changed logic of CC-74 Brightness to affect sound only between 0 and 64 like real XG synthesizers. Ability to turn on a full-ranged brightness (to use full 0...127 range) is kept. * Added support for different output sample formats (PCM8, PCM8U, PCM16, PCM16U, PCM32, PCM32U, Float32, and Float64) (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Reworked MIDI channels management to avoid any memory reallocations while music processing for a hard real time. (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) --- src/CMakeLists.txt | 21 +- src/sound/adlmidi/adldata.cpp | 10273 ++++++++-------- src/sound/adlmidi/adldata.hh | 58 +- src/sound/adlmidi/adlmidi.cpp | 468 +- src/sound/adlmidi/adlmidi.h | 184 +- src/sound/adlmidi/adlmidi.hpp | 21 +- src/sound/adlmidi/adlmidi_bankmap.h | 127 + src/sound/adlmidi/adlmidi_bankmap.tcc | 283 + src/sound/adlmidi/adlmidi_load.cpp | 432 +- src/sound/adlmidi/adlmidi_midiplay.cpp | 904 +- src/sound/adlmidi/adlmidi_opl3.cpp | 230 +- src/sound/adlmidi/adlmidi_private.cpp | 71 +- src/sound/adlmidi/adlmidi_private.hpp | 484 +- src/sound/adlmidi/adlmidi_ptr.hpp | 217 + src/sound/adlmidi/chips/dosbox/dbopl.cpp | 1618 +++ src/sound/adlmidi/chips/dosbox/dbopl.h | 284 + src/sound/adlmidi/chips/dosbox_opl3.cpp | 54 + src/sound/adlmidi/chips/dosbox_opl3.h | 23 + src/sound/adlmidi/chips/nuked/nukedopl3.c | 1391 +++ .../adlmidi/{ => chips/nuked}/nukedopl3.h | 46 +- .../nuked/nukedopl3_174.c} | 232 +- src/sound/adlmidi/chips/nuked/nukedopl3_174.h | 154 + src/sound/adlmidi/chips/nuked_opl3.cpp | 49 + src/sound/adlmidi/chips/nuked_opl3.h | 23 + src/sound/adlmidi/chips/nuked_opl3_v174.cpp | 49 + src/sound/adlmidi/chips/nuked_opl3_v174.h | 23 + src/sound/adlmidi/chips/opl_chip_base.h | 129 + src/sound/adlmidi/chips/opl_chip_base.tcc | 294 + src/sound/adlmidi/dbopl.cpp | 2045 --- src/sound/adlmidi/dbopl.h | 314 - src/sound/adlmidi/fraction.hpp | 9 + src/sound/adlmidi/wopl/wopl_file.c | 584 + src/sound/adlmidi/wopl/wopl_file.h | 290 + src/sound/i_musicinterns.h | 2 - .../mididevices/music_adlmidi_mididevice.cpp | 61 +- .../mididevices/music_opnmidi_mididevice.cpp | 60 +- .../gens/Ym2612_Emu.cpp} | 54 +- .../gens/Ym2612_Emu.h} | 22 +- src/sound/opnmidi/chips/gens_opn2.cpp | 52 + src/sound/opnmidi/chips/gens_opn2.h | 24 + src/sound/opnmidi/chips/mame/mame_ym2612fm.c | 2847 +++++ src/sound/opnmidi/chips/mame/mame_ym2612fm.h | 164 + src/sound/opnmidi/chips/mame/mamedef.h | 65 + src/sound/opnmidi/chips/mame_opn2.cpp | 54 + src/sound/opnmidi/chips/mame_opn2.h | 23 + src/sound/opnmidi/chips/nuked/ym3438.c | 1592 +++ src/sound/opnmidi/chips/nuked/ym3438.h | 246 + src/sound/opnmidi/chips/nuked_opn2.cpp | 49 + src/sound/opnmidi/chips/nuked_opn2.h | 25 + src/sound/opnmidi/chips/opn_chip_base.h | 110 + src/sound/opnmidi/chips/opn_chip_base.tcc | 268 + src/sound/opnmidi/fraction.hpp | 8 + src/sound/opnmidi/opnbank.h | 64 +- src/sound/opnmidi/opnmidi.cpp | 271 +- src/sound/opnmidi/opnmidi.h | 65 +- src/sound/opnmidi/opnmidi_bankmap.h | 127 + src/sound/opnmidi/opnmidi_bankmap.tcc | 283 + src/sound/opnmidi/opnmidi_load.cpp | 70 +- src/sound/opnmidi/opnmidi_midiplay.cpp | 643 +- src/sound/opnmidi/opnmidi_opn2.cpp | 133 +- src/sound/opnmidi/opnmidi_private.hpp | 463 +- src/sound/opnmidi/opnmidi_ptr.hpp | 217 + wadsrc/static/language.enu | 9 + wadsrc/static/menudef.txt | 30 +- wadsrc/static/xg.wopn | Bin 53214 -> 62080 bytes 65 files changed, 20498 insertions(+), 8987 deletions(-) create mode 100644 src/sound/adlmidi/adlmidi_bankmap.h create mode 100644 src/sound/adlmidi/adlmidi_bankmap.tcc create mode 100644 src/sound/adlmidi/adlmidi_ptr.hpp create mode 100644 src/sound/adlmidi/chips/dosbox/dbopl.cpp create mode 100644 src/sound/adlmidi/chips/dosbox/dbopl.h create mode 100644 src/sound/adlmidi/chips/dosbox_opl3.cpp create mode 100644 src/sound/adlmidi/chips/dosbox_opl3.h create mode 100644 src/sound/adlmidi/chips/nuked/nukedopl3.c rename src/sound/adlmidi/{ => chips/nuked}/nukedopl3.h (75%) rename src/sound/adlmidi/{nukedopl3.c => chips/nuked/nukedopl3_174.c} (83%) create mode 100644 src/sound/adlmidi/chips/nuked/nukedopl3_174.h create mode 100644 src/sound/adlmidi/chips/nuked_opl3.cpp create mode 100644 src/sound/adlmidi/chips/nuked_opl3.h create mode 100644 src/sound/adlmidi/chips/nuked_opl3_v174.cpp create mode 100644 src/sound/adlmidi/chips/nuked_opl3_v174.h create mode 100644 src/sound/adlmidi/chips/opl_chip_base.h create mode 100644 src/sound/adlmidi/chips/opl_chip_base.tcc delete mode 100644 src/sound/adlmidi/dbopl.cpp delete mode 100644 src/sound/adlmidi/dbopl.h create mode 100644 src/sound/adlmidi/wopl/wopl_file.c create mode 100644 src/sound/adlmidi/wopl/wopl_file.h rename src/sound/opnmidi/{Ym2612_ChipEmu.cpp => chips/gens/Ym2612_Emu.cpp} (95%) rename src/sound/opnmidi/{Ym2612_ChipEmu.h => chips/gens/Ym2612_Emu.h} (82%) create mode 100644 src/sound/opnmidi/chips/gens_opn2.cpp create mode 100644 src/sound/opnmidi/chips/gens_opn2.h create mode 100644 src/sound/opnmidi/chips/mame/mame_ym2612fm.c create mode 100644 src/sound/opnmidi/chips/mame/mame_ym2612fm.h create mode 100644 src/sound/opnmidi/chips/mame/mamedef.h create mode 100644 src/sound/opnmidi/chips/mame_opn2.cpp create mode 100644 src/sound/opnmidi/chips/mame_opn2.h create mode 100644 src/sound/opnmidi/chips/nuked/ym3438.c create mode 100644 src/sound/opnmidi/chips/nuked/ym3438.h create mode 100644 src/sound/opnmidi/chips/nuked_opn2.cpp create mode 100644 src/sound/opnmidi/chips/nuked_opn2.h create mode 100644 src/sound/opnmidi/chips/opn_chip_base.h create mode 100644 src/sound/opnmidi/chips/opn_chip_base.tcc create mode 100644 src/sound/opnmidi/opnmidi_bankmap.h create mode 100644 src/sound/opnmidi/opnmidi_bankmap.tcc create mode 100644 src/sound/opnmidi/opnmidi_ptr.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cea058af9..d101b480d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -638,8 +638,8 @@ add_definitions(-DOPNMIDI_USE_LEGACY_EMULATOR) add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER) add_definitions(-DOPNMIDI_DISABLE_MUS_SUPPORT -DOPNMIDI_DISABLE_XMI_SUPPORT -DOPNMIDI_DISABLE_MIDI_SEQUENCER) -# Disable ADLMIDI's MIDI Sequencer, MUS and XMI converters -add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER) +# Disable OPNMIDI's experimental yet emulator (using of it has some issues and missing notes in playback) +add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR) # Project files should be aware of the header files. We can GLOB these since # there's generally a new cpp for every header so this file will get changed @@ -855,14 +855,25 @@ set( FASTMATH_SOURCES sound/adlmidi/adlmidi_midiplay.cpp sound/adlmidi/adlmidi_opl3.cpp sound/adlmidi/adlmidi_private.cpp - sound/adlmidi/dbopl.cpp - sound/adlmidi/nukedopl3.c + sound/adlmidi/chips/dosbox/dbopl.cpp + sound/adlmidi/chips/dosbox_opl3.cpp + sound/adlmidi/chips/nuked/nukedopl3_174.c + sound/adlmidi/chips/nuked/nukedopl3.c + sound/adlmidi/chips/nuked_opl3.cpp + sound/adlmidi/chips/nuked_opl3_v174.cpp + sound/adlmidi/wopl/wopl_file.c + sound/opnmidi/chips/gens_opn2.cpp + sound/opnmidi/chips/gens/Ym2612_Emu.cpp + sound/opnmidi/chips/mame/mame_ym2612fm.c + sound/opnmidi/chips/mame_opn2.cpp + sound/opnmidi/chips/nuked_opn2.cpp + sound/opnmidi/chips/nuked/ym3438.c sound/opnmidi/opnmidi.cpp sound/opnmidi/opnmidi_load.cpp sound/opnmidi/opnmidi_midiplay.cpp sound/opnmidi/opnmidi_opn2.cpp sound/opnmidi/opnmidi_private.cpp - sound/opnmidi/Ym2612_ChipEmu.cpp + ) set (PCH_SOURCES diff --git a/src/sound/adlmidi/adldata.cpp b/src/sound/adlmidi/adldata.cpp index 1556f4d9c..7215d7c89 100644 --- a/src/sound/adlmidi/adldata.cpp +++ b/src/sound/adlmidi/adldata.cpp @@ -4,7 +4,7 @@ * FROM A NUMBER OF SOURCES, MOSTLY PC GAMES. * PREPROCESSED, CONVERTED, AND POSTPROCESSED OFF-SCREEN. */ -const adldata adl[4423] = +const adldata adl[4537] = { // ,---------+-------- Wave select settings // | ,-------ч-+------ Sustain/release rates // | | ,-----ч-ч-+---- Attack/decay rates @@ -4172,10 +4172,10 @@ const adldata adl[4423] = { 0x332ED12,0x1E7D211, 0x80,0x45, 0x2, +0 }, { 0x0F4E431,0x0F5F331, 0x97,0x86, 0x8, +0 }, { 0x3F0F701,0x1F8F900, 0x00,0x0D, 0xE, +0 }, - { 0x0F78111,0x3F7F054, 0x40,0x45, 0x8, +0 }, - { 0x0F78140,0x3F7F040, 0x40,0x01, 0xC, +14 }, - { 0x0F78111,0x2F7F054, 0x40,0x45, 0xA, +0 }, - { 0x0F78140,0x3F7F040, 0x40,0x05, 0xC, +14 }, + { 0x0F77111,0x3F7F011, 0x48,0x87, 0xA, +0 }, + { 0x0F78140,0x3F7F040, 0x86,0x00, 0xC, +14 }, + { 0x0F78140,0x3F7F040, 0x07,0x40, 0xC, +12 }, + { 0x0F78100,0x3F7F000, 0x86,0x03, 0xC, +14 }, { 0x6F78AE8,0x649B1F4, 0x03,0x0A, 0xA, +0 }, { 0x6F78AE8,0x649B1F4, 0x43,0x4B, 0xA, +0 }, { 0x0609533,0x4E5C131, 0x63,0x05, 0x0, +0 }, @@ -4205,34 +4205,34 @@ const adldata adl[4423] = { 0x2C79613,0x4E45411, 0xD7,0x08, 0xA, +0 }, { 0x023E133,0x0F2F131, 0xA2,0x09, 0xE, +0 }, { 0x023F132,0x0F2F131, 0x24,0x0A, 0xE, +0 }, - { 0x5C3C404,0x1B4B519, 0xA1,0x00, 0xC, -31 }, - { 0x17A9913,0x0B4F213, 0x0F,0x00, 0x8, -19 }, - { 0x223F832,0x4055421, 0x99,0x8A, 0xC, +0 }, + { 0x4C3C404,0x4B4B519, 0x21,0x05, 0x0, -31 }, + { 0x17A9913,0x0B4F213, 0x0F,0x00, 0x0, -19 }, + { 0x223F832,0x4056421, 0x99,0x8A, 0xC, +0 }, { 0x433CB32,0x5057561, 0x9B,0x8A, 0xA, +0 }, { 0x1029033,0x4044561, 0x5B,0x85, 0x4, +0 }, { 0x4109033,0x2044520, 0xA8,0x85, 0xA, +0 }, - { 0x2034170,0x0043671, 0x08,0x20, 0x9, +0 }, - { 0x1022171,0x0042671, 0x0C,0x17, 0xB, +0 }, - { 0x0055021,0x0F55022, 0x1C,0x0F, 0x7, +0 }, - { 0x0F54066,0x0F55024, 0x5A,0x0F, 0x7, -12 }, + { 0x2034170,0x0043671, 0x0B,0x20, 0xB, +0 }, + { 0x1024171,0x0043671, 0x0C,0x17, 0xB, +0 }, + { 0x005A061,0x0F55022, 0x69,0x06, 0x0, +0 }, + { 0x0008060,0x0F55021, 0x33,0x08, 0x0, +12 }, { 0x239B420,0x0076121, 0x50,0x05, 0x6, +0 }, { 0x139B462,0x00D7161, 0x91,0x14, 0x0, +0 }, - { 0x05470F1,0x07460B1, 0x5A,0x80, 0x0, +0 }, - { 0x054A0F1,0x07460B1, 0x5E,0x80, 0x0, +0 }, - { 0x2436110,0x114D211, 0x90,0x00, 0xC, +0 }, - { 0x1436192,0x145F312, 0x8F,0x00, 0xC, +0 }, + { 0x05470F1,0x07440B1, 0x69,0x80, 0x0, +0 }, + { 0x054A0F1,0x07430B1, 0x5E,0x80, 0x0, +0 }, + { 0x2436110,0x714D211, 0xCD,0x00, 0xA, +0 }, + { 0x5436192,0x745F312, 0xCB,0x00, 0xA, +0 }, { 0x0147421,0x0077521, 0x94,0x04, 0xE, +0 }, { 0x0178461,0x008AF28, 0x10,0xA6, 0xC, +0 }, { 0x0235271,0x0198161, 0x1E,0x08, 0xE, +0 }, { 0x0235361,0x0196161, 0x1D,0x03, 0xE, +0 }, { 0x0155331,0x0378261, 0x94,0x00, 0xA, +0 }, { 0x118543A,0x5177472, 0x1E,0x00, 0x4, -12 }, - { 0x0364121,0x02B7221, 0x21,0x08, 0xC, +0 }, - { 0x026F021,0x0056121, 0x26,0x03, 0xC, +0 }, + { 0x0365121,0x0257221, 0x1E,0x08, 0x0, +0 }, + { 0x2844521,0x20592A0, 0x23,0x03, 0x0, +0 }, { 0x0578321,0x117C021, 0x19,0x03, 0xC, +0 }, { 0x2E77530,0x307F520, 0x10,0x08, 0x8, +0 }, - { 0x036F121,0x337F121, 0x92,0x08, 0xE, +0 }, - { 0x0368121,0x037F121, 0x92,0x08, 0xE, +0 }, + { 0x036F121,0x337F121, 0x95,0x08, 0xE, +0 }, + { 0x0368121,0x037F121, 0x95,0x08, 0xE, +0 }, { 0x0A66121,0x0976121, 0x9B,0x08, 0xE, +0 }, { 0x5237731,0x1F65012, 0x4B,0x00, 0xA, +0 }, { 0x0137732,0x0F65011, 0xC7,0x0A, 0xA, +0 }, @@ -4241,11 +4241,11 @@ const adldata adl[4423] = { 0x10B8020,0x11B6330, 0x87,0x00, 0x8, +12 }, { 0x1235031,0x0077C24, 0xC0,0x08, 0x2, +0 }, { 0x045D933,0x4076C35, 0xD0,0x26, 0x4, +0 }, - { 0x2077830,0x2076331, 0x9F,0x00, 0xA, +0 }, + { 0x6077831,0x2076331, 0x1E,0x00, 0x6, +0 }, { 0x0199031,0x01B6134, 0x95,0x80, 0xA, +0 }, { 0x0177532,0x0174531, 0x93,0x03, 0xC, +0 }, { 0x0277530,0x0174536, 0x14,0x9C, 0xE, +12 }, - { 0x08D6EF1,0x02A3571, 0xC0,0x00, 0xE, +0 }, + { 0x08B8EF1,0x0285571, 0xC0,0x00, 0xE, +0 }, { 0x08860A1,0x01A6561, 0x5C,0x00, 0x8, +0 }, { 0x2176522,0x0277421, 0x5A,0x00, 0x6, +0 }, { 0x1267532,0x0166531, 0x8D,0x05, 0x4, +0 }, @@ -4267,12 +4267,12 @@ const adldata adl[4423] = { 0x32B7420,0x12BF134, 0x46,0x00, 0x8, +0 }, { 0x5029072,0x0069061, 0x96,0x0C, 0x8, +0 }, { 0x1019031,0x0069061, 0x1A,0x0C, 0x6, +0 }, - { 0x245C224,0x2550133, 0x81,0x80, 0xB, -36 }, - { 0x2459224,0x2556133, 0x81,0x80, 0xB, -36 }, + { 0x245C224,0x2550133, 0x81,0x80, 0x9, -36 }, + { 0x2459224,0x2556133, 0x81,0x80, 0x9, -36 }, { 0x132ED10,0x3E7D010, 0x87,0x0D, 0x6, +12 }, { 0x132ED30,0x3E7D010, 0x87,0x12, 0x6, +12 }, - { 0x033513A,0x013C121, 0xA4,0x06, 0x2, +0 }, - { 0x273F325,0x0228231, 0x20,0x06, 0x4, +0 }, + { 0x073513A,0x013C121, 0xA4,0x0A, 0x2, +0 }, + { 0x273F325,0x0228231, 0x20,0x0A, 0x4, +0 }, { 0x0031131,0x0054361, 0xD4,0x08, 0x4, +0 }, { 0x20311B0,0x00543E1, 0xD9,0x08, 0x4, +0 }, { 0x245A121,0x126A121, 0x98,0x05, 0xC, +0 }, @@ -4300,8 +4300,8 @@ const adldata adl[4423] = { 0x5522363,0x0131331, 0x1A,0x8D, 0x7, +0 }, { 0x0B67061,0x0928032, 0x9C,0x11, 0xA, +0 }, { 0x0057F21,0x0038F62, 0x9C,0x11, 0xA, +0 }, - { 0x0025511,0x1748201, 0x94,0x06, 0xE, +0 }, - { 0x2045501,0x2445501, 0x15,0x0D, 0xA, +0 }, + { 0x0625331,0x1648221, 0x94,0x06, 0xE, +0 }, + { 0x2645321,0x2445521, 0x15,0x0D, 0xA, +0 }, { 0x0B37121,0x5F48221, 0x16,0x08, 0x2, +0 }, { 0x2B37102,0x5F48221, 0x90,0x08, 0x6, +0 }, { 0x1127533,0x4F4F211, 0x58,0x03, 0x6, +0 }, @@ -4355,24 +4355,36 @@ const adldata adl[4423] = { 0x048FA00,0x008F900, 0x00,0x00, 0x6, +12 }, { 0x287F702,0x678F802, 0x80,0x88, 0xE, +12 }, { 0x2F7F602,0x0F8F802, 0x00,0x88, 0xE, +12 }, + { 0x008F700,0x007F609, 0x00,0x00, 0xD, -24 }, + { 0x0F1F105,0x0078407, 0x00,0x08, 0xC, -12 }, { 0x05476C1,0x30892C5, 0x80,0x08, 0x0, +0 }, { 0x05477C1,0x30892C5, 0x00,0x08, 0xA, -2 }, - { 0x005C604,0x005C604, 0x08,0x00, 0x1, +0 }, - { 0x509F902,0x057AB07, 0x03,0x07, 0xC, +12 }, - { 0x254F307,0x307F905, 0x04,0x08, 0x6, -5 }, - { 0x254F307,0x207F905, 0x04,0x08, 0x8, +0 }, - { 0x509F912,0x057AB07, 0x03,0x07, 0xC, +12 }, + { 0x007C604,0x007C604, 0x08,0x08, 0x1, +0 }, + { 0x201F302,0x057AB09, 0x03,0x07, 0xC, +12 }, + { 0x058F30B,0x308F90D, 0x04,0x08, 0x6, +0 }, + { 0x255F308,0x308F909, 0x04,0x08, 0x8, +4 }, + { 0x006C604,0x007C604, 0x08,0x08, 0x1, +0 }, + { 0x201F312,0x057AB09, 0x03,0x07, 0xC, +12 }, { 0x254D307,0x3288905, 0x04,0x03, 0xA, -5 }, - { 0x509F902,0x057AB07, 0x03,0x07, 0x0, +12 }, - { 0x210F509,0x605FE05, 0x8A,0x8A, 0xE, +12 }, - { 0x400F509,0x605FE05, 0x07,0x8A, 0xA, +12 }, + { 0x0015500,0x007C716, 0x0C,0x00, 0x0, +0 }, + { 0x201F312,0x057AB09, 0x00,0x07, 0xC, +12 }, + { 0x0015500,0x007C718, 0x0C,0x00, 0x0, +0 }, + { 0x001F312,0x047BB05, 0x03,0x07, 0xC, +12 }, + { 0x0015500,0x007C71B, 0x0C,0x00, 0x0, +0 }, + { 0x201F312,0x047BB09, 0x03,0x07, 0xC, +12 }, + { 0x291F108,0x333F401, 0x00,0x00, 0x8, +12 }, + { 0x291F108,0x333F501, 0x00,0x00, 0x8, +12 }, + { 0x0015500,0x007C71F, 0x0C,0x00, 0x0, +0 }, + { 0x300F50C,0x605FE05, 0x07,0x8A, 0x0, +12 }, + { 0x310F508,0x604FE05, 0x86,0x8A, 0x0, +11 }, { 0x2E1F11E,0x3F3F318, 0x04,0x00, 0x8, +0 }, { 0x2777603,0x3679601, 0x87,0x08, 0x6, +12 }, { 0x277C643,0x3679601, 0x87,0x08, 0xE, +12 }, { 0x366F905,0x099F701, 0x00,0x00, 0xC, +12 }, + { 0x291F108,0x334F401, 0x00,0x00, 0x8, +12 }, { 0x431A000,0x085B41A, 0x81,0x05, 0xA, +12 }, { 0x459F640,0x185B418, 0x00,0x20, 0xB, +12 }, - { 0x212FD08,0x305FD03, 0x01,0x03, 0x8, +12 }, + { 0x300F50C,0x605FE04, 0x07,0x8A, 0x0, +12 }, { 0x2A8F9E3,0x0779643, 0x1E,0x08, 0x2, +6 }, { 0x0A5F7E8,0x0D89949, 0xDE,0x00, 0x0, +0 }, { 0x2A8F9E3,0x0779643, 0x1E,0x00, 0xE, +12 }, @@ -4439,4568 +4451,4925 @@ const adldata adl[4423] = { 0x023F331,0x09C4333, 0x45,0x25, 0x6, -12 }, { 0x04CA700,0x04FC600, 0x00,0x2B, 0x0, -12 }, { 0x0B5F704,0x002010C, 0x00,0x00, 0x8, +21 }, + { 0x050F113,0x076D201, 0x50,0x40, 0x6, +0 }, + { 0x050F113,0x076D201, 0x50,0x00, 0x6, +0 }, + { 0x054F113,0x076D201, 0x53,0x00, 0x6, +0 }, + { 0x054F113,0x076D201, 0x50,0x00, 0x6, +0 }, + { 0x0FFF92C,0x0FFC1A1, 0xD4,0x00, 0x0, +0 }, + { 0x050F101,0x07CD301, 0x4F,0x00, 0x6, +0 }, + { 0x030A131,0x074C216, 0x81,0x80, 0x8, +0 }, + { 0x0FFF201,0x0F8F101, 0x11,0x00, 0xA, +0 }, + { 0x011FAD6,0x0FCF161, 0x4D,0x00, 0x8, +0 }, + { 0x011FA16,0x0F1F1E1, 0x4D,0x00, 0x8, +0 }, + { 0x011FAD6,0x0F5F561, 0x4D,0x00, 0x8, +0 }, + { 0x015DA45,0x0F6F361, 0x4E,0x80, 0x0, +0 }, + { 0x0F0FE04,0x0B5F6C2, 0x00,0x00, 0xE, -12 }, + { 0x004FE11,0x0BDF211, 0x11,0x00, 0x8, +0 }, + { 0x00FFF24,0x00FFF21, 0x80,0x80, 0x1, -12 }, + { 0x0FFF92C,0x0FFC0A1, 0xD4,0x00, 0x0, -12 }, + { 0x0E5F8E2,0x00EC0E1, 0xCA,0x00, 0x8, +0 }, + { 0x0FD5524,0x02D5031, 0x54,0x00, 0xE, +0 }, + { 0x0C8F253,0x0C5F211, 0x16,0x40, 0x4, +0 }, + { 0x0C8F253,0x0C5F211, 0x20,0x00, 0x4, +0 }, + { 0x0FFF111,0x3FFF054, 0x43,0x00, 0x8, +0 }, + { 0x0FFF111,0x3FFF054, 0x43,0x40, 0x8, +0 }, + { 0x0F0F0CA,0x06859EC, 0x4E,0x00, 0xC, +0 }, + { 0x02CD321,0x02CC321, 0x15,0x80, 0xA, +0 }, + { 0x0F2D401,0x08AC421, 0x18,0x80, 0xA, +0 }, + { 0x07AB400,0x07CC301, 0x1D,0x00, 0x0, +12 }, + { 0x07E7330,0x09E8021, 0x16,0x00, 0xE, +12 }, + { 0x004FE11,0x0BDF211, 0x0A,0x00, 0x8, +0 }, + { 0x0035171,0x0175461, 0x20,0x00, 0xE, +0 }, + { 0x0035171,0x0175461, 0x1E,0x00, 0xE, +0 }, + { 0x0035171,0x0175423, 0x1C,0x00, 0xE, +0 }, + { 0x04CA800,0x04FD600, 0x0B,0x00, 0x0, +12 }, + { 0x075F502,0x0F3F201, 0x29,0x80, 0x0, +0 }, + { 0x0530900,0x094F702, 0x40,0x00, 0xE, -12 }, + { 0x01432F1,0x016F1E1, 0x18,0x00, 0x0, +0 }, + { 0x01432F1,0x01631E1, 0x18,0x00, 0x0, +0 }, + { 0x01132F1,0x014F1E1, 0x18,0x00, 0x0, +0 }, + { 0x0154011,0x03831F1, 0x92,0x00, 0x8, +0 }, + { 0x0948411,0x0F4F4E4, 0x03,0x40, 0x8, -12 }, + { 0x0577361,0x017A021, 0x19,0x00, 0xC, +0 }, + { 0x0585361,0x018A021, 0x19,0x00, 0xC, +0 }, + { 0x0565361,0x016A021, 0x19,0x00, 0xC, +0 }, + { 0x0035171,0x0675421, 0x1C,0x00, 0xE, +0 }, + { 0x0576361,0x017A021, 0x1C,0x00, 0xC, +0 }, + { 0x0176E70,0x00E6B22, 0x8D,0x00, 0x2, +12 }, + { 0x00E7170,0x00E7823, 0x16,0x07, 0xE, +12 }, + { 0x0178731,0x00E8B22, 0x45,0x00, 0x2, +0 }, + { 0x0195132,0x0396061, 0x9A,0x80, 0xC, +0 }, + { 0x02495A2,0x02A60E2, 0x1D,0x80, 0x2, -12 }, + { 0x0AFD6A1,0x02A60E2, 0x13,0x80, 0x2, +0 }, + { 0x02498A2,0x02A60E2, 0x1D,0x80, 0x2, -12 }, + { 0x04FD6A1,0x02A60E2, 0x13,0x80, 0x2, +0 }, + { 0x0BF7721,0x02A60A1, 0x19,0x80, 0x6, +0 }, + { 0x0E5F8E2,0x00E70E1, 0xCA,0x00, 0x8, +0 }, + { 0x30FF221,0x018F221, 0x1D,0x00, 0x0, +0 }, + { 0x0FFF041,0x0FFF001, 0x11,0x00, 0xA, +0 }, + { 0x0BDF101,0x39FF102, 0xCE,0x80, 0x0, +0 }, + { 0x0FFF141,0x0FFF001, 0x0E,0x09, 0xA, +0 }, + { 0x0867261,0x01450E1, 0xA7,0x80, 0x2, +0 }, + { 0x049F430,0x033F410, 0x90,0x00, 0xC, +12 }, + { 0x0F0F0CA,0x06459CC, 0x4E,0x00, 0xC, +0 }, + { 0x0152011,0x0F831F1, 0x43,0x00, 0x8, +0 }, + { 0x0152011,0x0F831F1, 0x92,0x00, 0x8, +0 }, + { 0x010FF34,0x004FF03, 0x91,0x00, 0xA, +0 }, + { 0x002A4B0,0x04240D7, 0x84,0x80, 0x0, +0 }, + { 0x032B6B3,0x031D1B0, 0x4A,0x00, 0xE, +12 }, + { 0x0978211,0x0F3F0E4, 0x03,0x40, 0x8, +0 }, + { 0x002A4B4,0x04240D7, 0x87,0x80, 0x6, +0 }, + { 0x0F0A133,0x0F37115, 0x85,0x80, 0x8, +0 }, + { 0x053F101,0x074F211, 0x4F,0x00, 0x6, +0 }, + { 0x0E8F80B,0x0F4C301, 0xCA,0x00, 0x0, +0 }, + { 0x0FFF001,0x0F8F001, 0x11,0x00, 0xA, +0 }, + { 0x0EE7130,0x01E8823, 0x16,0x00, 0xE, +0 }, + { 0x025DA09,0x015F101, 0x4E,0x00, 0xA, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x00, 0xE, +12 }, + { 0x0F33900,0x005FF00, 0x3F,0x00, 0x0, +12 }, + { 0x0FFF832,0x0F8F501, 0x44,0x00, 0xE, +0 }, + { 0x0F0F007,0x0DC5C00, 0x00,0x00, 0xE, +12 }, + { 0x002A4B0,0x04240D7, 0xC4,0x89, 0x0, +0 }, + { 0x1111EF0,0x11121E2, 0x00,0xC0, 0x8, -12 }, + { 0x0EFE800,0x0FFA500, 0x0D,0x00, 0x6, +12 }, + { 0x077F005,0x0EDFA00, 0x00,0x00, 0xE, +12 }, + { 0x0F0F006,0x0F7F700, 0x00,0x00, 0xE, +12 }, + { 0x1FFF005,0x0B9F800, 0x00,0x00, 0xE, +0 }, + { 0x0F33900,0x005FF00, 0x3F,0x00, 0x0, +0 }, + { 0x077F005,0x0FBFA00, 0x00,0x00, 0xE, +0 }, + { 0x077F005,0x0EAFA00, 0x00,0x00, 0xE, +12 }, + { 0x0FFF005,0x0FFF600, 0x00,0x06, 0xE, +0 }, + { 0x0C0F006,0x034C6CF, 0x0E,0x00, 0xE, +0 }, + { 0x360F207,0x352F212, 0x0A,0x0C, 0x0, +0 }, + { 0x360F207,0x352F212, 0x0A,0x0B, 0x0, +0 }, + { 0x0F0F406,0x0F78700, 0x00,0x0D, 0xE, +0 }, + { 0x1FFF005,0x0B9F800, 0x00,0x00, 0x8, +0 }, + { 0x0F0F000,0x0F5F500, 0x00,0x09, 0xA, +0 }, + { 0x0590900,0x097F700, 0x40,0x00, 0x0, +24 }, + { 0x052F301,0x194F700, 0x40,0x00, 0x0, +12 }, + { 0x0530907,0x096F605, 0x40,0x00, 0xE, +0 }, + { 0x070F005,0x0E57A00, 0x00,0x10, 0xE, +12 }, + { 0x070F005,0x0E59A00, 0x00,0x10, 0xE, +12 }, + { 0x070F005,0x0E55A00, 0x00,0x10, 0xE, +12 }, + { 0x07BF003,0x07BF502, 0x8A,0x80, 0x8, +0 }, + { 0x07BF003,0x07BF402, 0x8A,0x80, 0x8, +0 }, }; -const struct adlinsdata adlins[4549] = +const struct adlinsdata adlins[4804] = { - { 0, 0, 0, 0, 1660, 1660,0.000000 }, - { 1, 1, 0, 0, 1746, 1746,0.000000 }, - { 2, 2, 0, 0, 1980, 1980,0.000000 }, - { 3, 3, 0, 0, 1553, 1553,0.000000 }, - { 4, 4, 0, 0, 1233, 1233,0.000000 }, - { 5, 5, 0, 0, 1980, 1980,0.000000 }, - { 6, 6, 0, 0, 1100, 1100,0.000000 }, - { 7, 7, 0, 0, 1233, 1233,0.000000 }, - { 8, 8, 0, 0, 940, 940,0.000000 }, - { 9, 9, 0, 0, 1100, 1100,0.000000 }, - { 10, 10, 0, 0, 460, 460,0.000000 }, - { 11, 11, 0, 0, 1740, 1740,0.000000 }, - { 12, 12, 0, 0, 66, 66,0.000000 }, - { 13, 13, 0, 0, 140, 140,0.000000 }, - { 14, 14, 0, 0, 940, 940,0.000000 }, - { 15, 15, 0, 0, 266, 266,0.000000 }, - { 16, 16, 0, 0, 40000, 33,0.000000 }, - { 17, 17, 0, 0, 40000, 6,0.000000 }, - { 18, 18, 0, 0, 40000, 13,0.000000 }, - { 19, 19, 0, 0, 40000, 193,0.000000 }, - { 20, 20, 0, 0, 40000, 193,0.000000 }, - { 21, 21, 0, 0, 40000, 6,0.000000 }, - { 22, 22, 0, 0, 40000, 66,0.000000 }, - { 23, 23, 0, 0, 40000, 66,0.000000 }, - { 24, 24, 0, 0, 1026, 1026,0.000000 }, - { 25, 25, 0, 0, 1826, 1826,0.000000 }, - { 26, 26, 0, 0, 1813, 1813,0.000000 }, - { 27, 27, 0, 0, 1080, 1080,0.000000 }, - { 28, 28, 0, 0, 40000, 0,0.000000 }, - { 29, 29, 0, 0, 40000, 13,0.000000 }, - { 30, 30, 0, 0, 40000, 13,0.000000 }, - { 31, 31, 0, 0, 4200, 4200,0.000000 }, - { 32, 32, 0, 0, 940, 940,0.000000 }, - { 33, 33, 0, 0, 40000, 20,0.000000 }, - { 34, 34, 0, 0, 1746, 1746,0.000000 }, - { 35, 35, 0, 0, 40000, 0,0.000000 }, - { 36, 36, 0, 0, 2400, 2400,0.000000 }, - { 37, 37, 0, 0, 4166, 4166,0.000000 }, - { 38, 38, 0, 0, 1740, 1740,0.000000 }, - { 39, 39, 0, 0, 40000, 26,0.000000 }, - { 40, 40, 0, 0, 40000, 86,0.000000 }, - { 41, 41, 0, 0, 40000, 6,0.000000 }, - { 42, 42, 0, 0, 40000, 133,0.000000 }, - { 43, 43, 0, 0, 40000, 126,0.000000 }, - { 44, 44, 0, 0, 333, 333,0.000000 }, - { 45, 45, 0, 0, 940, 940,0.000000 }, - { 46, 46, 0, 0, 1046, 1046,0.000000 }, - { 47, 47, 0, 0, 40000, 33,0.000000 }, - { 48, 48, 0, 0, 1766, 13,0.000000 }, - { 49, 49, 0, 0, 40000, 220,0.000000 }, - { 50, 50, 0, 0, 40000, 153,0.000000 }, - { 51, 51, 0, 0, 40000, 146,0.000000 }, - { 52, 52, 0, 0, 2300, 2300,0.000000 }, - { 53, 53, 0, 0, 40000, 140,0.000000 }, - { 54, 54, 0, 0, 233, 233,0.000000 }, - { 55, 55, 0, 0, 40000, 6,0.000000 }, - { 56, 56, 0, 0, 40000, 6,0.000000 }, - { 57, 57, 0, 0, 40000, 6,0.000000 }, - { 58, 58, 0, 0, 40000, 6,0.000000 }, - { 59, 59, 0, 0, 40000, 6,0.000000 }, - { 60, 60, 0, 0, 40000, 0,0.000000 }, - { 61, 61, 0, 0, 40000, 26,0.000000 }, - { 62, 62, 0, 0, 40000, 0,0.000000 }, - { 63, 63, 0, 0, 40000, 6,0.000000 }, - { 64, 64, 0, 0, 40000, 20,0.000000 }, - { 65, 65, 0, 0, 40000, 20,0.000000 }, - { 66, 66, 0, 0, 40000, 0,0.000000 }, - { 67, 67, 0, 0, 40000, 6,0.000000 }, - { 68, 68, 0, 0, 3940, 3940,0.000000 }, - { 69, 69, 0, 0, 40000, 6,0.000000 }, - { 70, 70, 0, 0, 40000, 60,0.000000 }, - { 71, 71, 0, 0, 40000, 6,0.000000 }, - { 72, 72, 0, 0, 40000, 6,0.000000 }, - { 73, 73, 0, 0, 40000, 13,0.000000 }, - { 74, 74, 0, 0, 40000, 0,0.000000 }, - { 75, 75, 0, 0, 40000, 73,0.000000 }, - { 76, 76, 0, 0, 40000, 53,0.000000 }, - { 77, 77, 0, 0, 40000, 53,0.000000 }, - { 78, 78, 0, 0, 40000, 53,0.000000 }, - { 79, 79, 0, 0, 40000, 0,0.000000 }, - { 80, 80, 0, 0, 40000, 0,0.000000 }, - { 81, 81, 0, 0, 40000, 33,0.000000 }, - { 82, 82, 0, 0, 40000, 6,0.000000 }, - { 83, 83, 0, 0, 40000, 6,0.000000 }, - { 84, 84, 0, 0, 40000, 73,0.000000 }, - { 85, 85, 0, 0, 40000, 133,0.000000 }, - { 86, 86, 0, 0, 40000, 20,0.000000 }, - { 87, 87, 0, 0, 40000, 233,0.000000 }, - { 88, 88, 0, 0, 40000, 413,0.000000 }, - { 89, 89, 0, 0, 1660, 86,0.000000 }, - { 90, 90, 0, 0, 40000, 553,0.000000 }, - { 91, 91, 0, 0, 40000, 100,0.000000 }, - { 92, 92, 0, 0, 613, 13,0.000000 }, - { 93, 93, 0, 0, 40000, 126,0.000000 }, - { 94, 94, 0, 0, 40000, 53,0.000000 }, - { 95, 95, 0, 0, 993, 993,0.000000 }, - { 96, 96, 0, 0, 40000, 513,0.000000 }, - { 97, 97, 0, 0, 280, 280,0.000000 }, - { 98, 98, 0, 0, 40000, 73,0.000000 }, - { 99, 99, 0, 0, 2113, 2113,0.000000 }, - { 100, 100, 0, 0, 40000, 886,0.000000 }, - { 101, 101, 0, 0, 40000, 773,0.000000 }, - { 102, 102, 0, 0, 40000, 426,0.000000 }, - { 103, 103, 0, 0, 2413, 2413,0.000000 }, - { 104, 104, 0, 0, 1126, 1126,0.000000 }, - { 105, 105, 0, 0, 1046, 1046,0.000000 }, - { 106, 106, 0, 0, 866, 866,0.000000 }, - { 107, 107, 0, 0, 280, 280,0.000000 }, - { 108, 108, 0, 0, 40000, 6,0.000000 }, - { 109, 109, 0, 0, 40000, 113,0.000000 }, - { 110, 110, 0, 0, 40000, 6,0.000000 }, - { 111, 111, 0, 0, 1126, 1126,0.000000 }, - { 112, 112, 0, 0, 140, 140,0.000000 }, - { 113, 113, 0, 0, 246, 246,0.000000 }, - { 114, 114, 0, 0, 20, 20,0.000000 }, - { 115, 115, 0, 0, 146, 146,0.000000 }, - { 116, 116, 0, 0, 146, 146,0.000000 }, - { 117, 117, 0, 0, 340, 340,0.000000 }, - { 118, 118, 0, 0, 2280, 2280,0.000000 }, - { 119, 119, 0, 0, 286, 286,0.000000 }, - { 120, 120, 0, 0, 586, 586,0.000000 }, - { 121, 121, 0, 0, 4486, 4486,0.000000 }, - { 122, 122, 0, 0, 133, 133,0.000000 }, - { 123, 123, 0, 0, 186, 186,0.000000 }, - { 124, 124, 0, 0, 146, 146,0.000000 }, - { 125, 125, 0, 0, 40000, 113,0.000000 }, - { 126, 126, 0, 0, 140, 140,0.000000 }, - { 127, 127, 35, 0, 26, 26,0.000000 }, - { 128, 128, 52, 0, 20, 20,0.000000 }, - { 129, 129, 48, 0, 73, 73,0.000000 }, - { 130, 130, 58, 0, 40, 40,0.000000 }, - { 129, 129, 60, 0, 80, 80,0.000000 }, - { 131, 131, 47, 0, 106, 106,0.000000 }, - { 132, 132, 43, 0, 20, 20,0.000000 }, - { 131, 131, 49, 0, 146, 146,0.000000 }, - { 133, 133, 43, 0, 26, 26,0.000000 }, - { 131, 131, 51, 0, 160, 160,0.000000 }, - { 134, 134, 43, 0, 160, 160,0.000000 }, - { 131, 131, 54, 0, 140, 140,0.000000 }, - { 131, 131, 57, 0, 153, 153,0.000000 }, - { 135, 135, 72, 0, 600, 600,0.000000 }, - { 131, 131, 60, 0, 160, 160,0.000000 }, - { 136, 136, 76, 0, 513, 513,0.000000 }, - { 137, 137, 84, 0, 393, 393,0.000000 }, - { 138, 138, 36, 0, 400, 400,0.000000 }, - { 139, 139, 65, 0, 86, 86,0.000000 }, - { 140, 140, 84, 0, 406, 406,0.000000 }, - { 141, 141, 83, 0, 60, 60,0.000000 }, - { 135, 135, 84, 0, 406, 406,0.000000 }, - { 142, 142, 24, 0, 46, 46,0.000000 }, - { 136, 136, 77, 0, 500, 500,0.000000 }, - { 143, 143, 60, 0, 40, 40,0.000000 }, - { 144, 144, 65, 0, 40, 40,0.000000 }, - { 145, 145, 59, 0, 13, 13,0.000000 }, - { 146, 146, 51, 0, 46, 46,0.000000 }, - { 147, 147, 45, 0, 40, 40,0.000000 }, - { 148, 148, 71, 0, 140, 140,0.000000 }, - { 149, 149, 60, 0, 160, 160,0.000000 }, - { 150, 150, 58, 0, 153, 153,0.000000 }, - { 151, 151, 53, 0, 153, 153,0.000000 }, - { 152, 152, 64, 0, 86, 86,0.000000 }, - { 153, 153, 71, 0, 13, 13,0.000000 }, - { 154, 154, 61, 0, 313, 313,0.000000 }, - { 155, 155, 61, 0, 606, 606,0.000000 }, - { 156, 156, 44, 0, 80, 80,0.000000 }, - { 157, 157, 40, 0, 320, 320,0.000000 }, - { 158, 158, 69, 0, 20, 20,0.000000 }, - { 159, 159, 68, 0, 20, 20,0.000000 }, - { 160, 160, 63, 0, 33, 33,0.000000 }, - { 161, 161, 74, 0, 93, 93,0.000000 }, - { 162, 162, 60, 0, 353, 353,0.000000 }, - { 163, 163, 80, 0, 60, 60,0.000000 }, - { 164, 164, 64, 0, 873, 873,0.000000 }, - { 165, 165, 72, 0, 33, 33,0.000000 }, - { 166, 166, 73, 0, 286, 286,0.000000 }, - { 167, 167, 70, 0, 133, 133,0.000000 }, - { 168, 168, 68, 0, 20, 20,0.000000 }, - { 169, 169, 48, 0, 40, 40,0.000000 }, - { 131, 131, 53, 0, 126, 126,0.000000 }, - { 170, 170, 0, 0, 1020, 1020,0.000000 }, - { 171, 171, 0, 0, 40000, 0,0.000000 }, - { 172, 173, 0, 0, 1740, 1740,0.000000 }, - { 174, 175, 0, 0, 1260, 1260,0.000000 }, - { 176, 177, 0, 0, 1100, 1100,0.000000 }, - { 178, 178, 0, 0, 1100, 1100,0.000000 }, - { 179, 180, 0, 0, 726, 726,0.000000 }, - { 181, 181, 0, 0, 460, 460,0.000000 }, - { 182, 182, 0, 0, 246, 246,0.000000 }, - { 183, 184, 0, 0, 120, 120,0.000000 }, - { 185, 186, 0, 0, 840, 840,0.000000 }, - { 187, 187, 0, 0, 293, 293,0.000000 }, - { 188, 189, 0, 0, 1320, 1320,0.000000 }, - { 190, 191, 0, 0, 646, 6,0.000000 }, - { 192, 193, 0, 0, 40000, 6,0.000000 }, - { 194, 194, 0, 0, 40000, 6,0.000000 }, - { 195, 196, 0, 0, 40000, 0,0.000000 }, - { 197, 198, 0, 0, 40000, 20,0.000000 }, - { 199, 200, 0, 0, 2573, 6,0.000000 }, - { 201, 202, 0, 0, 953, 953,0.000000 }, - { 203, 204, 0, 0, 1740, 1740,0.000000 }, - { 205, 206, 0, 0, 1900, 1900,0.000000 }, - { 207, 208, 0, 0, 1986, 1986,0.000000 }, - { 209, 210, 0, 0, 760, 760,0.000000 }, - { 211, 212, 0, 0, 40000, 6,0.000000 }, - { 213, 213, 0, 0, 1613, 0,0.000000 }, - { 214, 215, 0, 0, 3260, 3260,0.000000 }, - { 216, 217, 0, 0, 360, 360,0.000000 }, - { 218, 219, 0, 0, 1020, 1020,0.000000 }, - { 220, 221, 0, 0, 2126, 2126,0.000000 }, - { 222, 223, 0, 0, 300, 13,0.000000 }, - { 224, 224, 0, 0, 1740, 1740,0.000000 }, - { 225, 226, 0, 0, 1720, 1720,0.000000 }, - { 227, 227, 0, 0, 146, 146,0.000000 }, - { 228, 228, 0, 0, 186, 40,0.000000 }, - { 229, 230, 0, 0, 40000, 46,0.000000 }, - { 231, 232, 0, 0, 40000, 66,0.000000 }, - { 233, 234, 0, 0, 40000, 266,0.000000 }, - { 235, 236, 0, 0, 160, 160,0.000000 }, - { 235, 237, 0, 0, 386, 386,0.000000 }, - { 46, 238, 0, 0, 1046, 1046,0.000000 }, - { 239, 240, 0, 0, 40000, 66,0.000000 }, - { 241, 242, 0, 0, 720, 46,0.000000 }, - { 243, 243, 0, 0, 40000, 33,0.000000 }, - { 244, 244, 0, 0, 40000, 6,0.000000 }, - { 245, 245, 0, 0, 40000, 13,0.000000 }, - { 246, 247, 0, 0, 466, 466,0.000000 }, - { 248, 249, 0, 0, 1213, 13,0.000000 }, - { 250, 250, 0, 0, 60, 6,0.000000 }, - { 251, 251, 0, 0, 40000, 66,0.000000 }, - { 252, 253, 0, 0, 40000, 6,0.000000 }, - { 254, 255, 0, 0, 40000, 26,0.000000 }, - { 256, 257, 0, 0, 166, 0,0.000000 }, - { 258, 259, 0, 0, 40000, 6,0.000000 }, - { 260, 261, 0, 0, 40000, 20,0.000000 }, - { 262, 263, 0, 0, 40000, 26,0.000000 }, - { 264, 265, 0, 0, 40000, 40,0.000000 }, - { 266, 267, 0, 0, 40000, 6,0.000000 }, - { 268, 269, 0, 0, 40000, 20,0.000000 }, - { 270, 271, 0, 0, 40000, 13,0.000000 }, - { 272, 273, 0, 0, 40000, 60,0.000000 }, - { 274, 275, 0, 0, 40000, 33,0.000000 }, - { 276, 276, 0, 0, 40000, 20,0.000000 }, - { 277, 278, 0, 0, 40000, 73,0.000000 }, - { 279, 280, 0, 0, 40000, 33,0.000000 }, - { 281, 282, 0, 0, 40000, 73,0.000000 }, - { 283, 283, 0, 0, 40000, 13,0.000000 }, - { 284, 285, 0, 0, 40000, 6,0.000000 }, - { 286, 287, 0, 0, 40000, 20,0.000000 }, - { 288, 288, 0, 0, 40000, 33,0.000000 }, - { 289, 290, 0, 0, 40000, 6,0.000000 }, - { 291, 292, 0, 0, 40000, 73,0.000000 }, - { 293, 294, 0, 0, 40000, 106,0.000000 }, - { 295, 296, 0, 0, 40000, 180,0.000000 }, - { 88, 297, 0, 0, 40000, 500,0.000000 }, - { 298, 299, 0, 0, 40000, 26,0.000000 }, - { 300, 301, 0, 0, 40000, 553,0.000000 }, - { 302, 302, 0, 0, 40000, 126,0.000000 }, - { 303, 303, 0, 0, 40000, 53,0.000000 }, - { 304, 304, 0, 0, 1793, 1793,0.000000 }, - { 305, 306, 0, 0, 40000, 300,0.000000 }, - { 307, 307, 0, 0, 1900, 1900,0.000000 }, - { 308, 308, 0, 0, 146, 146,0.000000 }, - { 309, 309, 0, 0, 40000, 580,0.000000 }, - { 310, 310, 0, 0, 40000, 480,0.000000 }, - { 311, 312, 0, 0, 2273, 2273,0.000000 }, - { 313, 314, 0, 0, 1140, 1140,0.000000 }, - { 315, 316, 0, 0, 560, 560,0.000000 }, - { 317, 318, 0, 0, 1080, 1080,0.000000 }, - { 107, 319, 0, 0, 280, 280,0.000000 }, - { 108, 320, 0, 0, 40000, 6,0.000000 }, - { 109, 321, 0, 0, 40000, 46,0.000000 }, - { 322, 323, 0, 0, 40000, 6,0.000000 }, - { 324, 325, 0, 0, 146, 146,0.000000 }, - { 326, 327, 0, 0, 246, 246,0.000000 }, - { 328, 328, 0, 0, 26, 26,0.000000 }, - { 329, 329, 0, 0, 140, 140,0.000000 }, - { 330, 331, 0, 0, 40, 40,0.000000 }, - { 332, 332, 0, 0, 2313, 2313,0.000000 }, - { 333, 333, 0, 0, 86, 86,0.000000 }, - { 334, 334, 0, 0, 3440, 3440,0.000000 }, - { 335, 335, 0, 0, 146, 146,0.000000 }, - { 336, 336, 0, 0, 2406, 2406,0.000000 }, - { 337, 337, 0, 0, 40000, 146,0.000000 }, - { 338, 339, 0, 0, 40000, 113,0.000000 }, - { 340, 341, 0, 0, 40000, 0,0.000000 }, - { 342, 342, 35, 0, 446, 446,0.000000 }, - { 343, 343, 0, 0, 20, 20,0.000000 }, - { 344, 344, 35, 0, 86, 86,0.000000 }, - { 345, 345, 35, 0, 26, 26,0.000000 }, - { 346, 346, 50, 0, 153, 153,0.000000 }, - { 347, 347, 18, 0, 53, 53,0.000000 }, - { 348, 348, 72, 0, 53, 53,0.000000 }, - { 349, 349, 74, 0, 33, 33,0.000000 }, - { 350, 350, 35, 0, 66, 66,0.000000 }, - { 351, 351, 16, 0, 86, 86,0.000000 }, - { 352, 352, 0, 0, 1513, 13,0.000000 }, - { 353, 353, 38, 0, 53, 53,0.000000 }, - { 354, 354, 38, 0, 100, 100,0.000000 }, - { 355, 355, 31, 0, 33, 33,0.000000 }, - { 355, 355, 35, 0, 33, 33,0.000000 }, - { 355, 355, 38, 0, 193, 193,0.000000 }, - { 355, 355, 41, 0, 166, 166,0.000000 }, - { 355, 355, 45, 0, 126, 126,0.000000 }, - { 355, 355, 50, 0, 146, 146,0.000000 }, - { 356, 356, 36, 0, 100, 100,0.000000 }, - { 357, 357, 36, 0, 26, 26,0.000000 }, - { 358, 358, 48, 0, 73, 73,0.000000 }, - { 358, 358, 36, 0, 100, 100,0.000000 }, - { 359, 359, 36, 0, 20, 20,0.000000 }, - { 360, 360, 0, 0, 33, 33,0.000000 }, - { 361, 361, 61, 0, 60, 60,0.000000 }, - { 362, 362, 96, 0, 233, 233,0.000000 }, - { 363, 363, 38, 0, 60, 60,0.000000 }, - { 127, 127, 16, 0, 66, 66,0.000000 }, - { 364, 365, 18, 0, 13, 13,0.000000 }, - { 366, 366, 30, 0, 106, 106,0.000000 }, - { 367, 368, 35, 0, 73, 73,0.000000 }, - { 129, 129, 0, 0, 73, 73,0.000000 }, - { 369, 369, 0, 0, 66, 66,0.000000 }, - { 370, 370, 88, 0, 86, 86,0.000000 }, - { 371, 371, 88, 0, 60, 60,0.000000 }, - { 372, 372, 79, 0, 380, 380,0.000000 }, - { 135, 135, 14, 0, 860, 860,0.000000 }, - { 373, 373, 46, 0, 313, 313,0.000000 }, - { 374, 375,129, 0, 1613, 60,0.000000 }, - { 376, 376, 58, 0, 140, 140,0.000000 }, - { 377, 377,164, 0, 106, 106,0.000000 }, - { 378, 378,142, 0, 2300, 2300,0.000000 }, - { 379, 379, 9, 0, 40, 40,0.000000 }, - { 380, 381, 35, 0, 3306, 0,0.000000 }, - { 382, 382, 28, 0, 26, 26,0.000000 }, - { 383, 383, 46, 0, 320, 320,0.000000 }, - { 384, 384, 60, 0, 93, 93,0.000000 }, - { 384, 384, 54, 0, 100, 100,0.000000 }, - { 385, 385, 72, 0, 60, 60,0.000000 }, - { 385, 385, 67, 0, 60, 60,0.000000 }, - { 385, 385, 60, 0, 53, 53,0.000000 }, - { 386, 386, 1, 0, 46, 46,0.000000 }, - { 387, 387, 77, 0, 133, 133,0.000000 }, - { 387, 387, 72, 0, 133, 133,0.000000 }, - { 388, 388, 90, 0, 46, 46,0.000000 }, - { 389, 389, 39, 0, 180, 180,0.000000 }, - { 390, 390, 36, 0, 506, 506,0.000000 }, - { 391, 392, 35, 0, 20, 20,0.000000 }, - { 391, 393, 35, 0, 20, 20,0.000000 }, - { 394, 394, 60, 0, 40, 40,0.000000 }, - { 328, 328, 7, 0, 26, 26,0.000000 }, - { 395, 395, 90, 0, 80, 80,0.000000 }, - { 396, 396, 90, 0, 306, 306,0.000000 }, - { 397, 397, 35, 0, 106, 106,0.000000 }, - { 398, 399, 5, 0, 506, 506,0.000000 }, - { 400, 400,103, 0, 220, 220,0.000000 }, - { 401, 401, 3, 0, 6, 6,0.000000 }, - { 169, 169, 1, 0, 40, 40,0.000000 }, - { 131, 131, 0, 0, 40, 40,0.000000 }, - { 402, 402, 36, 0, 180, 180,0.000000 }, - { 403, 403, 60, 0, 40000, 0,0.000000 }, - { 404, 404, 37, 0, 60, 60,0.000000 }, - { 405, 405, 36, 0, 46, 46,0.000000 }, - { 406, 406, 32, 0, 40, 40,0.000000 }, - { 407, 407, 50, 0, 306, 306,0.000000 }, - { 408, 408, 50, 0, 93, 93,0.000000 }, - { 409, 409, 83, 0, 26, 26,0.000000 }, - { 410, 410, 72, 0, 93, 93,0.000000 }, - { 148, 148, 59, 0, 153, 153,0.000000 }, - { 411, 411, 64, 0, 40, 40,0.000000 }, - { 411, 411, 60, 0, 40, 40,0.000000 }, - { 412, 412, 72, 0, 26, 26,0.000000 }, - { 412, 412, 62, 0, 33, 33,0.000000 }, - { 413, 413, 83, 0, 273, 273,0.000000 }, - { 414, 414, 0, 0, 40000, 13,0.000000 }, - { 415, 415, 0, 0, 1126, 1126,0.000000 }, - { 416, 416, 0, 0, 40000, 20,0.000000 }, - { 417, 417, 0, 0, 2940, 20,0.000000 }, - { 418, 418, 0, 0, 2233, 2233,0.000000 }, - { 419, 419, 0, 0, 1233, 1233,0.000000 }, - { 420, 420, 0, 0, 940, 940,0.000000 }, - { 421, 421, 0, 0, 1580, 1580,0.000000 }, - { 422, 422, 0, 0, 1740, 1740,0.000000 }, - { 423, 423, 0, 0, 140, 140,0.000000 }, - { 424, 424, 0, 0, 940, 940,0.000000 }, - { 425, 425, 0, 0, 1080, 1080,0.000000 }, - { 426, 426, 0, 0, 2086, 2086,0.000000 }, - { 427, 427, 0, 0, 40000, 26,0.000000 }, - { 428, 428, 60, 0, 40000, 0,0.000000 }, - { 429, 429, 73, 0, 473, 473,0.000000 }, - { 429, 429, 74, 0, 473, 473,0.000000 }, - { 429, 429, 80, 0, 473, 473,0.000000 }, - { 429, 429, 84, 0, 473, 473,0.000000 }, - { 429, 429, 92, 0, 400, 400,0.000000 }, - { 430, 430, 81, 0, 280, 280,0.000000 }, - { 430, 430, 83, 0, 280, 280,0.000000 }, - { 430, 430, 95, 0, 220, 220,0.000000 }, - { 431, 431, 35, 0, 46, 46,0.000000 }, - { 432, 432, 60, 0, 40, 40,0.000000 }, - { 357, 357, 59, 0, 13, 13,0.000000 }, - { 432, 432, 44, 0, 40, 40,0.000000 }, - { 433, 433, 41, 0, 166, 166,0.000000 }, - { 434, 434, 97, 0, 26, 26,0.000000 }, - { 433, 433, 44, 0, 140, 140,0.000000 }, - { 433, 433, 48, 0, 153, 153,0.000000 }, - { 435, 435, 96, 0, 233, 233,0.000000 }, - { 433, 433, 51, 0, 160, 160,0.000000 }, - { 433, 433, 54, 0, 160, 160,0.000000 }, - { 436, 436, 40, 0, 386, 386,0.000000 }, - { 433, 433, 57, 0, 113, 113,0.000000 }, - { 437, 437, 58, 0, 120, 120,0.000000 }, - { 438, 438, 97, 0, 73, 73,0.000000 }, - { 439, 439, 50, 0, 60, 60,0.000000 }, - { 437, 437, 60, 0, 153, 153,0.000000 }, - { 440, 440, 53, 0, 53, 53,0.000000 }, - { 441, 441, 46, 0, 46, 46,0.000000 }, - { 440, 440, 57, 0, 53, 53,0.000000 }, - { 442, 442, 42, 0, 206, 206,0.000000 }, - { 442, 442, 37, 0, 206, 206,0.000000 }, - { 443, 443, 41, 0, 193, 193,0.000000 }, - { 443, 443, 37, 0, 193, 193,0.000000 }, - { 444, 444, 77, 0, 46, 46,0.000000 }, - { 444, 444, 72, 0, 46, 46,0.000000 }, - { 445, 445, 70, 0, 60, 60,0.000000 }, - { 445, 445, 90, 0, 60, 60,0.000000 }, - { 446, 446, 46, 0, 46, 46,0.000000 }, - { 447, 447, 48, 0, 246, 246,0.000000 }, - { 448, 448, 85, 0, 20, 20,0.000000 }, - { 449, 449, 66, 0, 60, 60,0.000000 }, - { 449, 449, 61, 0, 60, 60,0.000000 }, - { 450, 450, 41, 0, 106, 106,0.000000 }, - { 451, 451, 41, 0, 140, 140,0.000000 }, - { 452, 452, 81, 0, 66, 66,0.000000 }, - { 400, 400, 81, 0, 266, 266,0.000000 }, - { 400, 400, 76, 0, 266, 266,0.000000 }, - { 359, 359, 60, 0, 13, 13,0.000000 }, - { 453, 453, 53, 0, 120, 120,0.000000 }, - { 454, 454, 0, 0, 40000, 0,0.000000 }, - { 455, 455, 0, 0, 40, 40,0.000000 }, - { 456, 456, 0, 0, 1080, 1080,0.000000 }, - { 457, 457, 0, 0, 126, 126,0.000000 }, - { 458, 458, 0, 0, 40000, 26,0.000000 }, - { 459, 459, 0, 0, 40000, 6,0.000000 }, - { 460, 460, 0, 0, 700, 700,0.000000 }, - { 461, 461, 0, 0, 280, 280,0.000000 }, - { 462, 462, 0, 0, 360, 360,0.000000 }, - { 463, 463, 0, 0, 40000, 20,0.000000 }, - { 464, 464, 0, 0, 1466, 13,0.000000 }, - { 465, 465, 0, 0, 40000, 33,0.000000 }, - { 466, 466, 0, 0, 40000, 33,0.000000 }, - { 467, 467, 0, 0, 1953, 1953,0.000000 }, - { 468, 468, 0, 0, 1580, 1580,0.000000 }, - { 469, 469, 0, 0, 1340, 1340,0.000000 }, - { 470, 470, 0, 0, 40000, 0,0.000000 }, - { 471, 471, 0, 0, 40000, 53,0.000000 }, - { 472, 472, 0, 0, 1386, 1386,0.000000 }, - { 473, 473, 0, 0, 560, 13,0.000000 }, - { 474, 474, 0, 0, 40000, 66,0.000000 }, - { 475, 475, 0, 0, 593, 593,0.000000 }, - { 476, 476, 0, 0, 40000, 2446,0.000000 }, - { 477, 477, 0, 0, 60, 60,0.000000 }, - { 478, 478, 0, 0, 220, 220,0.000000 }, - { 479, 479, 0, 0, 153, 153,0.000000 }, - { 480, 480, 0, 0, 20, 20,0.000000 }, - { 481, 481, 0, 0, 40000, 0,0.000000 }, - { 482, 482, 0, 0, 753, 753,0.000000 }, - { 483, 483, 0, 0, 100, 100,0.000000 }, - { 484, 484, 0, 0, 40000, 73,0.000000 }, - { 485, 485, 0, 0, 2600, 320,0.000000 }, - { 486, 486, 0, 0, 293, 293,0.000000 }, - { 487, 487, 0, 0, 1400, 1400,0.000000 }, - { 488, 488, 0, 0, 2073, 2073,0.000000 }, - { 489, 489, 0, 0, 80, 80,0.000000 }, - { 490, 490, 0, 0, 40000, 33,0.000000 }, - { 491, 491, 50, 0, 446, 446,0.000000 }, - { 492, 492, 37, 0, 46, 46,0.000000 }, - { 493, 493, 39, 0, 180, 180,0.000000 }, - { 494, 494, 39, 0, 33, 33,0.000000 }, - { 495, 495, 86, 0, 1800, 1800,0.000000 }, - { 496, 496, 43, 0, 33, 33,0.000000 }, - { 127, 127, 24, 0, 46, 46,0.000000 }, - { 127, 127, 29, 0, 33, 33,0.000000 }, - { 497, 497, 50, 0, 153, 153,0.000000 }, - { 498, 498, 30, 0, 100, 100,0.000000 }, - { 498, 498, 33, 0, 413, 413,0.000000 }, - { 498, 498, 38, 0, 1633, 1633,0.000000 }, - { 498, 498, 42, 0, 26, 26,0.000000 }, - { 499, 499, 24, 0, 46, 46,0.000000 }, - { 499, 499, 27, 0, 66, 66,0.000000 }, - { 499, 499, 29, 0, 66, 66,0.000000 }, - { 499, 499, 32, 0, 53, 53,0.000000 }, - { 500, 500, 32, 0, 13, 13,0.000000 }, - { 501, 501, 53, 0, 86, 86,0.000000 }, - { 501, 501, 57, 0, 86, 86,0.000000 }, - { 502, 502, 60, 0, 80, 80,0.000000 }, - { 503, 503, 55, 0, 160, 160,0.000000 }, - { 486, 486, 85, 0, 266, 266,0.000000 }, - { 504, 504, 90, 0, 453, 453,0.000000 }, - { 505, 505, 84, 0, 73, 73,0.000000 }, - { 506, 506, 48, 0, 160, 160,0.000000 }, - { 507, 507, 48, 0, 40, 40,0.000000 }, - { 132, 132, 72, 0, 13, 13,0.000000 }, - { 508, 508, 72, 0, 13, 13,0.000000 }, - { 509, 509, 72, 0, 13, 13,0.000000 }, - { 510, 510, 63, 0, 533, 533,0.000000 }, - { 510, 510, 65, 0, 526, 526,0.000000 }, - { 511, 511, 79, 0, 506, 506,0.000000 }, - { 512, 512, 38, 0, 113, 113,0.000000 }, - { 513, 513, 94, 0, 100, 100,0.000000 }, - { 514, 514, 87, 0, 113, 113,0.000000 }, - { 514, 514, 94, 0, 100, 100,0.000000 }, - { 515, 515, 80, 0, 66, 66,0.000000 }, - { 516, 516, 47, 0, 140, 140,0.000000 }, - { 517, 517, 61, 0, 80, 80,0.000000 }, - { 517, 517, 68, 0, 66, 66,0.000000 }, - { 518, 518, 61, 0, 160, 160,0.000000 }, - { 518, 518, 68, 0, 126, 126,0.000000 }, - { 499, 499, 60, 0, 46, 46,0.000000 }, - { 519, 519, 60, 0, 60, 60,0.000000 }, - { 520, 520, 36, 0, 53, 53,0.000000 }, - { 520, 520, 60, 0, 40, 40,0.000000 }, - { 521, 521, 60, 0, 40, 40,0.000000 }, - { 522, 522, 68, 0, 20, 20,0.000000 }, - { 523, 523, 71, 0, 33, 33,0.000000 }, - { 523, 523, 72, 0, 33, 33,0.000000 }, - { 524, 524,101, 0, 320, 320,0.000000 }, - { 525, 525, 36, 0, 1680, 1680,0.000000 }, - { 526, 526, 25, 0, 40000, 2286,0.000000 }, - { 527, 527, 37, 0, 400, 400,0.000000 }, - { 528, 528, 36, 0, 146, 146,0.000000 }, - { 528, 528, 41, 0, 166, 166,0.000000 }, - { 529, 529, 84, 0, 73, 73,0.000000 }, - { 530, 530, 54, 0, 1326, 1326,0.000000 }, - { 481, 481, 48, 0, 40000, 0,0.000000 }, - { 531, 531, 0, 0, 2266, 2266,0.000000 }, - { 532, 532, 0, 0, 833, 833,0.000000 }, - { 533, 533, 0, 0, 246, 13,0.000000 }, - { 534, 534, 0, 0, 40000, 6,0.000000 }, - { 535, 535, 0, 0, 4140, 26,0.000000 }, - { 536, 536, 0, 0, 3886, 13,0.000000 }, - { 537, 537, 0, 0, 4886, 4886,0.000000 }, - { 538, 538, 0, 0, 1780, 1780,0.000000 }, - { 539, 539, 0, 0, 4140, 26,0.000000 }, - { 540, 540, 0, 0, 40000, 6,0.000000 }, - { 541, 541, 0, 0, 3946, 13,0.000000 }, - { 542, 542, 0, 0, 40, 40,0.000000 }, - { 543, 543, 0, 0, 40000, 13,0.000000 }, - { 544, 544, 0, 0, 4426, 4426,0.000000 }, - { 545, 545, 0, 0, 40000, 493,0.000000 }, - { 546, 546, 0, 0, 40000, 846,0.000000 }, - { 547, 547, 0, 0, 40000, 53,0.000000 }, - { 135, 135, 49, 0, 1146, 1146,0.000000 }, - { 548, 548, 35, 0, 40, 40,0.000000 }, - { 549, 549, 41, 0, 6, 6,0.000000 }, - { 366, 366, 38, 0, 106, 106,0.000000 }, - { 550, 550, 39, 0, 26, 26,0.000000 }, - { 551, 551, 49, 0, 46, 46,0.000000 }, - { 408, 408, 59, 0, 93, 93,0.000000 }, - { 552, 552, 24, 0, 26, 26,0.000000 }, - { 552, 552, 27, 0, 40, 20,0.000000 }, - { 552, 552, 29, 0, 20, 20,0.000000 }, - { 552, 552, 32, 0, 40, 13,0.000000 }, - { 553, 553, 84, 0, 113, 113,0.000000 }, - { 512, 512, 79, 0, 73, 73,0.000000 }, - { 554, 554, 61, 0, 173, 173,0.000000 }, - { 554, 554, 68, 0, 133, 133,0.000000 }, - { 555, 555, 36, 0, 33, 33,0.000000 }, - { 555, 555, 60, 0, 20, 20,0.000000 }, - { 556, 556, 36, 0, 180, 180,0.000000 }, - { 115, 115, 37, 0, 46, 46,0.000000 }, - { 557, 557, 0, 0, 1066, 1066,0.000000 }, - { 558, 558, 0, 0, 273, 273,0.000000 }, - { 559, 559, 0, 0, 186, 186,0.000000 }, - { 560, 560, 0, 0, 966, 966,0.000000 }, - { 561, 561, 0, 0, 80, 80,0.000000 }, - { 562, 562, 0, 0, 326, 326,0.000000 }, - { 563, 563, 0, 0, 40000, 33,0.000000 }, - { 564, 564, 0, 0, 380, 380,0.000000 }, - { 565, 565, 0, 0, 40, 40,0.000000 }, - { 566, 566, 0, 0, 4586, 4586,0.000000 }, - { 567, 567, 0, 0, 2680, 2680,0.000000 }, - { 568, 568, 0, 0, 40000, 6,0.000000 }, - { 569, 569, 0, 0, 80, 80,0.000000 }, - { 570, 570, 0, 0, 1206, 1206,0.000000 }, - { 571, 571, 0, 0, 593, 593,0.000000 }, - { 572, 572, 0, 0, 680, 680,0.000000 }, - { 356, 356, 0, 0, 246, 246,0.000000 }, - { 573, 573, 0, 0, 40000, 20,0.000000 }, - { 574, 574, 0, 0, 40000, 20,0.000000 }, - { 575, 575, 0, 0, 40000, 73,0.000000 }, - { 576, 576, 0, 0, 40000, 53,0.000000 }, - { 577, 577, 0, 0, 40000, 33,0.000000 }, - { 578, 578, 0, 0, 40000, 140,0.000000 }, - { 579, 579, 0, 0, 40000, 93,0.000000 }, - { 580, 580, 0, 0, 40000, 126,0.000000 }, - { 581, 581, 0, 0, 40000, 26,0.000000 }, - { 582, 582, 0, 0, 40000, 53,0.000000 }, - { 583, 583, 0, 0, 513, 513,0.000000 }, - { 584, 584, 0, 0, 40000, 66,0.000000 }, - { 585, 585, 0, 0, 313, 313,0.000000 }, - { 516, 516, 0, 0, 140, 140,0.000000 }, - { 586, 586, 0, 0, 40000, 0,0.000000 }, - { 587, 587, 0, 0, 40000, 0,0.000000 }, - { 588, 588, 0, 0, 40000, 6,0.000000 }, - { 498, 498, 26, 0, 86, 86,0.000000 }, - { 494, 494, 35, 0, 53, 53,0.000000 }, - { 350, 350, 41, 0, 80, 80,0.000000 }, - { 353, 353, 48, 0, 40, 40,0.000000 }, - { 354, 354, 67, 0, 73, 73,0.000000 }, - { 502, 502, 24, 0, 100, 100,0.000000 }, - { 346, 346, 36, 0, 46, 46,0.000000 }, - { 346, 346, 38, 0, 193, 193,0.000000 }, - { 346, 346, 40, 0, 140, 140,0.000000 }, - { 346, 346, 42, 0, 173, 173,0.000000 }, - { 346, 346, 44, 0, 140, 140,0.000000 }, - { 510, 510, 55, 0, 453, 453,0.000000 }, - { 346, 346, 46, 0, 146, 146,0.000000 }, - { 136, 136, 80, 0, 486, 486,0.000000 }, - { 486, 486, 24, 0, 346, 346,0.000000 }, - { 153, 153, 50, 0, 13, 13,0.000000 }, - { 346, 346, 24, 0, 66, 66,0.000000 }, - { 516, 516, 31, 0, 186, 186,0.000000 }, - { 498, 498, 35, 0, 40, 40,0.000000 }, - { 517, 517, 60, 0, 80, 80,0.000000 }, - { 530, 530, 36, 0, 1760, 1760,0.000000 }, - { 530, 530, 48, 0, 1360, 6,0.000000 }, - { 589, 589, 0, 0, 1660, 1660,0.000000 }, - { 139, 139, 76, 0, 73, 73,0.000000 }, - { 156, 156, 48, 0, 80, 80,0.000000 }, - { 157, 157, 48, 0, 226, 226,0.000000 }, - { 165, 165, 69, 0, 40, 40,0.000000 }, - { 167, 167, 75, 0, 133, 133,0.000000 }, - { 590, 590, 0, 0, 1226, 1226,0.000000 }, - { 591, 591, 0, 0, 2193, 2193,0.000000 }, - { 592, 592, 0, 0, 1900, 1900,0.000000 }, - { 593, 593, 0, 0, 626, 626,0.000000 }, - { 594, 594, 0, 0, 1213, 1213,0.000000 }, - { 595, 595, 0, 0, 1740, 1740,0.000000 }, - { 596, 596, 0, 0, 193, 193,0.000000 }, - { 597, 597, 0, 0, 300, 300,0.000000 }, - { 598, 598, 0, 0, 40000, 6,0.000000 }, - { 599, 599, 0, 0, 40000, 480,0.000000 }, - { 600, 600, 0, 0, 40000, 20,0.000000 }, - { 601, 601, 0, 0, 653, 653,0.000000 }, - { 602, 602, 0, 0, 126, 126,0.000000 }, - { 603, 603, 0, 0, 1080, 1080,0.000000 }, - { 604, 604, 0, 0, 4200, 4200,0.000000 }, - { 605, 605, 0, 0, 1020, 1020,0.000000 }, - { 606, 606, 0, 0, 593, 13,0.000000 }, - { 607, 607, 0, 0, 80, 60,0.000000 }, - { 608, 608, 0, 0, 40000, 113,0.000000 }, - { 609, 609, 0, 0, 120, 120,0.000000 }, - { 610, 610, 0, 0, 1580, 1580,0.000000 }, - { 611, 611, 0, 0, 460, 460,0.000000 }, - { 612, 612, 0, 0, 40000, 53,0.000000 }, - { 613, 613, 0, 0, 2320, 13,0.000000 }, - { 614, 614, 0, 0, 80, 60,0.000000 }, - { 615, 615, 0, 0, 80, 60,0.000000 }, - { 616, 616, 0, 0, 1020, 1020,0.000000 }, - { 617, 617, 0, 0, 146, 146,0.000000 }, - { 618, 618, 0, 0, 740, 740,0.000000 }, - { 619, 619, 0, 0, 146, 146,0.000000 }, - { 620, 620, 0, 0, 40000, 6,0.000000 }, - { 621, 621, 0, 0, 913, 913,0.000000 }, - { 622, 622, 0, 0, 2280, 2280,0.000000 }, - { 623, 623, 0, 0, 166, 6,0.000000 }, - { 624, 624, 0, 0, 40000, 0,0.000000 }, - { 625, 625, 0, 0, 40000, 0,0.000000 }, - { 626, 626, 0, 0, 40000, 0,0.000000 }, - { 627, 627, 0, 0, 1046, 1046,0.000000 }, - { 628, 628, 0, 0, 40000, 6,0.000000 }, - { 629, 629, 0, 0, 40000, 0,0.000000 }, - { 630, 630, 0, 0, 1953, 1953,0.000000 }, - { 631, 631, 0, 0, 40000, 106,0.000000 }, - { 632, 632, 0, 0, 533, 13,0.000000 }, - { 633, 633, 0, 0, 2273, 2273,0.000000 }, - { 634, 634, 0, 0, 646, 646,0.000000 }, - { 635, 635, 0, 0, 166, 166,0.000000 }, - { 636, 636, 0, 0, 326, 326,0.000000 }, - { 637, 637, 0, 0, 40000, 26,0.000000 }, - { 638, 638, 0, 0, 246, 246,0.000000 }, - { 346, 346, 0, 0, 140, 140,0.000000 }, - { 639, 639, 0, 0, 1226, 1226,0.000000 }, - { 404, 404, 0, 0, 146, 146,0.000000 }, - { 506, 506, 0, 0, 153, 153,0.000000 }, - { 639, 639, 60, 0, 886, 886,0.000000 }, - { 639, 639, 79, 0, 446, 446,0.000000 }, - { 640, 640, 65, 0, 800, 800,0.000000 }, - { 486, 486, 31, 0, 353, 353,0.000000 }, - { 486, 486, 36, 0, 360, 360,0.000000 }, - { 640, 640, 72, 0, 613, 613,0.000000 }, - { 136, 136, 79, 0, 493, 493,0.000000 }, - { 148, 148, 57, 0, 160, 160,0.000000 }, - { 150, 150, 53, 0, 153, 153,0.000000 }, - { 641, 641, 84, 0, 106, 106,0.000000 }, - { 520, 520, 66, 0, 40, 40,0.000000 }, - { 642, 642, 31, 0, 166, 13,0.000000 }, - { 642, 642, 29, 0, 60, 26,0.000000 }, - { 356, 356, 31, 0, 93, 93,0.000000 }, - { 356, 356, 19, 0, 220, 220,0.000000 }, - { 643, 643, 31, 0, 40000, 6,0.000000 }, - { 643, 643, 29, 0, 40000, 6,0.000000 }, - { 644, 644, 31, 0, 560, 560,0.000000 }, - { 644, 644, 35, 0, 573, 573,0.000000 }, - { 644, 644, 40, 0, 526, 526,0.000000 }, - { 644, 644, 47, 0, 540, 540,0.000000 }, - { 516, 516, 32, 0, 186, 186,0.000000 }, - { 516, 516, 43, 0, 146, 146,0.000000 }, - { 495, 495, 26, 0, 60, 60,0.000000 }, - { 495, 495, 44, 0, 380, 380,0.000000 }, - { 496, 496, 26, 0, 26, 26,0.000000 }, - { 496, 496, 51, 0, 40, 40,0.000000 }, - { 496, 496, 39, 0, 33, 33,0.000000 }, - { 495, 495, 30, 0, 46, 46,0.000000 }, - { 645, 645, 44, 0, 486, 486,0.000000 }, - { 645, 645, 43, 0, 473, 473,0.000000 }, - { 646, 646, 0, 0, 653, 653,0.000000 }, - { 647, 647, 0, 0, 2280, 2280,0.000000 }, - { 648, 648, 0, 0, 526, 526,0.000000 }, - { 649, 649, 0, 0, 40000, 40,0.000000 }, - { 650, 650, 0, 0, 40, 40,0.000000 }, - { 651, 651, 0, 0, 933, 933,0.000000 }, - { 652, 652, 0, 0, 40000, 300,0.000000 }, - { 653, 653, 0, 0, 280, 280,0.000000 }, - { 654, 654, 0, 0, 40000, 6,0.000000 }, - { 655, 655, 0, 0, 40000, 0,0.000000 }, - { 656, 656, 0, 0, 40000, 6,0.000000 }, - { 657, 657, 0, 0, 280, 280,0.000000 }, - { 658, 658, 0, 0, 513, 513,0.000000 }, - { 659, 659, 0, 0, 40000, 6,0.000000 }, - { 660, 660, 0, 0, 600, 600,0.000000 }, - { 661, 661, 0, 0, 646, 646,0.000000 }, - { 662, 662, 0, 0, 1166, 1166,0.000000 }, - { 663, 663, 0, 0, 40, 40,0.000000 }, - { 664, 664, 0, 0, 2073, 2073,0.000000 }, - { 665, 665, 0, 0, 40000, 106,0.000000 }, - { 666, 666, 0, 0, 40000, 146,0.000000 }, - { 667, 667, 0, 0, 40000, 126,0.000000 }, - { 668, 668, 0, 0, 280, 280,0.000000 }, - { 669, 669, 0, 0, 40000, 73,0.000000 }, - { 670, 670, 0, 0, 393, 393,0.000000 }, - { 671, 671, 0, 0, 760, 760,0.000000 }, - { 672, 672, 0, 0, 40000, 233,0.000000 }, - { 673, 673, 0, 0, 253, 253,0.000000 }, - { 674, 674, 0, 0, 186, 186,0.000000 }, - { 675, 675, 0, 0, 40000, 46,0.000000 }, - { 676, 676, 0, 0, 4773, 4773,0.000000 }, - { 677, 677, 0, 0, 2186, 2186,0.000000 }, - { 678, 678, 0, 0, 313, 313,0.000000 }, - { 679, 679, 0, 0, 1226, 1226,0.000000 }, - { 680, 680, 0, 0, 526, 13,0.000000 }, - { 681, 681, 0, 0, 40000, 0,0.000000 }, - { 682, 682, 0, 0, 40, 46,0.000000 }, - { 683, 683, 0, 0, 146, 146,0.000000 }, - { 684, 684, 0, 0, 2193, 2193,0.000000 }, - { 685, 685, 0, 0, 40000, 220,0.000000 }, - { 686, 686, 0, 0, 40000, 560,0.000000 }, - { 687, 687, 0, 0, 86, 86,0.000000 }, - { 688, 688, 0, 0, 40000, 1326,0.000000 }, - { 689, 689, 0, 0, 1166, 1166,0.000000 }, - { 690, 690, 0, 0, 593, 593,0.000000 }, - { 691, 691, 0, 0, 1580, 1580,0.000000 }, - { 692, 692, 0, 0, 513, 513,0.000000 }, - { 693, 693, 0, 0, 333, 333,0.000000 }, - { 694, 694, 0, 0, 1200, 1200,0.000000 }, - { 695, 695, 0, 0, 40000, 6,0.000000 }, - { 645, 645, 0, 0, 486, 486,0.000000 }, - { 696, 696, 0, 0, 40000, 6,0.000000 }, - { 697, 697, 0, 0, 333, 333,0.000000 }, - { 698, 698, 0, 0, 120, 120,0.000000 }, - { 699, 699, 0, 0, 40000, 6,0.000000 }, - { 700, 700, 0, 0, 13, 13,0.000000 }, - { 701, 701, 0, 0, 40000, 53,0.000000 }, - { 702, 702, 0, 0, 806, 806,0.000000 }, - { 703, 703, 0, 0, 40000, 6,0.000000 }, - { 704, 704, 0, 0, 40000, 6,0.000000 }, - { 705, 705, 0, 0, 40000, 0,0.000000 }, - { 706, 706, 0, 0, 40000, 6,0.000000 }, - { 707, 707, 0, 0, 40000, 166,0.000000 }, - { 708, 708, 0, 0, 40000, 26,0.000000 }, - { 709, 709, 0, 0, 40000, 146,0.000000 }, - { 710, 710, 0, 0, 1233, 6,0.000000 }, - { 711, 711, 0, 0, 146, 146,0.000000 }, - { 712, 712, 0, 0, 40000, 6,0.000000 }, - { 713, 713, 0, 0, 40000, 66,0.000000 }, - { 714, 714, 0, 0, 40000, 393,0.000000 }, - { 715, 715, 0, 0, 40000, 93,0.000000 }, - { 716, 716, 0, 0, 620, 620,0.000000 }, - { 717, 717, 0, 0, 40000, 6,0.000000 }, - { 718, 718, 0, 0, 493, 493,0.000000 }, - { 719, 719, 0, 0, 280, 280,0.000000 }, - { 720, 720, 0, 0, 760, 760,0.000000 }, - { 721, 721, 0, 0, 160, 160,0.000000 }, - { 722, 722, 0, 0, 40000, 106,0.000000 }, - { 723, 723, 0, 0, 166, 166,0.000000 }, - { 724, 724, 0, 0, 40000, 6,0.000000 }, - { 725, 725, 0, 0, 126, 126,0.000000 }, - { 726, 726, 0, 0, 593, 593,0.000000 }, - { 727, 727, 0, 0, 513, 513,0.000000 }, - { 728, 728, 0, 0, 246, 246,0.000000 }, - { 507, 507, 0, 0, 40, 40,0.000000 }, - { 512, 512, 0, 0, 93, 93,0.000000 }, - { 729, 729, 0, 0, 2406, 2406,0.000000 }, - { 730, 730, 0, 0, 2400, 2400,0.000000 }, - { 731, 731, 0, 0, 1153, 1153,0.000000 }, - { 732, 732, 0, 0, 40000, 1113,0.000000 }, - { 733, 733, 0, 0, 40000, 613,0.000000 }, - { 734, 734, 0, 0, 4453, 4453,0.000000 }, - { 735, 735, 0, 0, 160, 160,0.000000 }, - { 736, 736, 0, 0, 80, 80,0.000000 }, - { 737, 737, 0, 0, 1513, 13,0.000000 }, - { 738, 738, 38, 0, 33, 33,0.000000 }, - { 739, 739, 44, 0, 40, 40,0.000000 }, - { 500, 500, 58, 0, 13, 13,0.000000 }, - { 740, 740, 24, 0, 160, 160,0.000000 }, - { 741, 741, 60, 0, 133, 133,0.000000 }, - { 736, 736, 44, 0, 80, 80,0.000000 }, - { 742, 742, 25, 0, 146, 146,0.000000 }, - { 743, 743, 60, 0, 33, 33,0.000000 }, - { 742, 742, 30, 0, 146, 146,0.000000 }, - { 377, 377, 60, 0, 93, 93,0.000000 }, - { 742, 742, 33, 0, 173, 173,0.000000 }, - { 744, 744, 60, 0, 53, 53,0.000000 }, - { 742, 742, 35, 0, 186, 186,0.000000 }, - { 742, 742, 37, 0, 200, 200,0.000000 }, - { 745, 745, 0, 0, 313, 313,0.000000 }, - { 742, 742, 40, 0, 206, 206,0.000000 }, - { 746, 746,102, 0, 300, 300,0.000000 }, - { 747, 747, 80, 0, 433, 433,0.000000 }, - { 377, 377, 0, 0, 86, 86,0.000000 }, - { 748, 748, 56, 0, 13, 13,0.000000 }, - { 749, 749, 0, 0, 140, 140,0.000000 }, - { 746, 746,100, 0, 280, 280,0.000000 }, - { 750, 750, 40, 0, 200, 200,0.000000 }, - { 750, 750, 35, 0, 200, 200,0.000000 }, - { 751, 751, 29, 0, 53, 53,0.000000 }, - { 750, 750, 29, 0, 200, 200,0.000000 }, - { 750, 750, 22, 0, 206, 206,0.000000 }, - { 500, 500, 0, 0, 13, 13,0.000000 }, - { 752, 752, 0, 0, 46, 46,0.000000 }, - { 753, 753, 84, 0, 86, 86,0.000000 }, - { 754, 754, 84, 0, 506, 506,0.000000 }, - { 755, 755, 0, 0, 160, 160,0.000000 }, - { 755, 755, 71, 0, 133, 133,0.000000 }, - { 755, 755, 53, 0, 160, 160,0.000000 }, - { 755, 755, 48, 0, 160, 160,0.000000 }, - { 756, 756, 95, 0, 200, 200,0.000000 }, - { 757, 757, 95, 0, 613, 613,0.000000 }, - { 758, 758, 0, 0, 866, 866,0.000000 }, - { 759, 759, 0, 0, 933, 933,0.000000 }, - { 760, 760, 0, 0, 2393, 2393,0.000000 }, - { 761, 761, 0, 0, 760, 760,0.000000 }, - { 762, 762, 0, 0, 1740, 1740,0.000000 }, - { 763, 763, 0, 0, 1846, 1846,0.000000 }, - { 764, 764, 0, 0, 2106, 2106,0.000000 }, - { 765, 765, 0, 0, 1193, 1193,0.000000 }, - { 766, 766, 0, 0, 1613, 1613,0.000000 }, - { 767, 767, 0, 0, 673, 673,0.000000 }, - { 768, 768, 0, 0, 280, 280,0.000000 }, - { 769, 769, 0, 0, 1740, 1740,0.000000 }, - { 770, 770, 0, 0, 193, 193,0.000000 }, - { 771, 771, 0, 0, 140, 140,0.000000 }, - { 772, 772, 0, 0, 933, 933,0.000000 }, - { 773, 773, 0, 0, 333, 333,0.000000 }, - { 774, 774, 0, 0, 40000, 6,0.000000 }, - { 775, 775, 0, 0, 40000, 6,0.000000 }, - { 776, 776, 0, 0, 613, 13,0.000000 }, - { 777, 777, 0, 0, 40000, 80,0.000000 }, - { 778, 778, 0, 0, 40000, 26,0.000000 }, - { 779, 779, 0, 0, 40000, 20,0.000000 }, - { 780, 780, 0, 0, 40000, 20,0.000000 }, - { 781, 781, 0, 0, 40000, 66,0.000000 }, - { 782, 782, 0, 0, 940, 940,0.000000 }, - { 783, 783, 0, 0, 566, 566,0.000000 }, - { 784, 784, 0, 0, 1846, 1846,0.000000 }, - { 785, 785, 0, 0, 1120, 1120,0.000000 }, - { 786, 786, 0, 0, 300, 300,0.000000 }, - { 787, 787, 0, 0, 3000, 3000,0.000000 }, - { 788, 788, 0, 0, 3773, 3773,0.000000 }, - { 789, 789, 0, 0, 620, 620,0.000000 }, - { 790, 790, 0, 0, 846, 846,0.000000 }, - { 791, 791, 0, 0, 4846, 4846,0.000000 }, - { 792, 792, 0, 0, 1133, 1133,0.000000 }, - { 793, 793, 0, 0, 1446, 1446,0.000000 }, - { 794, 794, 0, 0, 566, 566,0.000000 }, - { 795, 795, 0, 0, 600, 600,0.000000 }, - { 796, 796, 0, 0, 1180, 1180,0.000000 }, - { 797, 797, 0, 0, 893, 893,0.000000 }, - { 798, 798, 0, 0, 40000, 20,0.000000 }, - { 799, 799, 0, 0, 40000, 20,0.000000 }, - { 800, 800, 0, 0, 166, 40,0.000000 }, - { 801, 801, 0, 0, 2286, 2286,0.000000 }, - { 802, 802, 0, 0, 40000, 86,0.000000 }, - { 803, 803, 0, 0, 120, 120,0.000000 }, - { 804, 804, 0, 0, 440, 440,0.000000 }, - { 805, 805, 0, 0, 286, 286,0.000000 }, - { 806, 806, 0, 0, 40000, 86,0.000000 }, - { 807, 807, 0, 0, 5473, 120,0.000000 }, - { 808, 808, 0, 0, 100, 13,0.000000 }, - { 809, 809, 0, 0, 40000, 53,0.000000 }, - { 810, 810, 0, 0, 1640, 6,0.000000 }, - { 811, 811, 0, 0, 40000, 53,0.000000 }, - { 812, 812, 0, 0, 1026, 13,0.000000 }, - { 813, 813, 0, 0, 186, 186,0.000000 }, - { 814, 814, 0, 0, 40, 40,0.000000 }, - { 815, 815, 0, 0, 40000, 20,0.000000 }, - { 816, 816, 0, 0, 40000, 20,0.000000 }, - { 817, 817, 0, 0, 873, 873,0.000000 }, - { 818, 818, 0, 0, 4440, 4440,0.000000 }, - { 819, 819, 0, 0, 273, 6,0.000000 }, - { 820, 820, 0, 0, 40000, 53,0.000000 }, - { 821, 821, 0, 0, 40000, 106,0.000000 }, - { 822, 822, 0, 0, 40000, 6,0.000000 }, - { 823, 823, 0, 0, 40000, 0,0.000000 }, - { 824, 824, 0, 0, 40000, 13,0.000000 }, - { 825, 825, 0, 0, 40000, 33,0.000000 }, - { 826, 826, 0, 0, 40000, 20,0.000000 }, - { 827, 827, 0, 0, 40000, 6,0.000000 }, - { 828, 828, 0, 0, 40000, 6,0.000000 }, - { 829, 829, 0, 0, 40000, 20,0.000000 }, - { 830, 830, 0, 0, 40000, 20,0.000000 }, - { 831, 831, 0, 0, 40000, 6,0.000000 }, - { 832, 832, 0, 0, 40000, 13,0.000000 }, - { 833, 833, 0, 0, 40000, 20,0.000000 }, - { 834, 834, 0, 0, 40000, 20,0.000000 }, - { 835, 835, 0, 0, 40000, 20,0.000000 }, - { 836, 836, 0, 0, 40000, 33,0.000000 }, - { 837, 837, 0, 0, 40000, 53,0.000000 }, - { 838, 838, 0, 0, 40000, 6,0.000000 }, - { 839, 839, 0, 0, 1153, 1153,0.000000 }, - { 840, 840, 0, 0, 40000, 20,0.000000 }, - { 841, 841, 0, 0, 20, 20,0.000000 }, - { 842, 842, 0, 0, 2040, 2040,0.000000 }, - { 843, 843, 0, 0, 40000, 6,0.000000 }, - { 844, 844, 0, 0, 40000, 13,0.000000 }, - { 845, 845, 0, 0, 1153, 1153,0.000000 }, - { 846, 846, 0, 0, 1526, 80,0.000000 }, - { 847, 847, 0, 0, 40000, 180,0.000000 }, - { 848, 848, 0, 0, 40000, 86,0.000000 }, - { 849, 849, 0, 0, 40000, 26,0.000000 }, - { 850, 850, 0, 0, 40000, 53,0.000000 }, - { 851, 851, 0, 0, 2886, 2886,0.000000 }, - { 852, 852, 0, 0, 40000, 73,0.000000 }, - { 853, 853, 0, 0, 40000, 126,0.000000 }, - { 854, 854, 0, 0, 1706, 1706,0.000000 }, - { 855, 855, 0, 0, 40000, 213,0.000000 }, - { 856, 856, 0, 0, 920, 920,0.000000 }, - { 857, 857, 0, 0, 993, 993,0.000000 }, - { 858, 858, 0, 0, 1613, 1613,0.000000 }, - { 859, 859, 0, 0, 1000, 106,0.000000 }, - { 860, 860, 0, 0, 40000, 20,0.000000 }, - { 861, 861, 0, 0, 40000, 213,0.000000 }, - { 862, 862, 0, 0, 766, 766,0.000000 }, - { 863, 863, 0, 0, 153, 153,0.000000 }, - { 864, 864, 0, 0, 626, 626,0.000000 }, - { 865, 865, 0, 0, 506, 506,0.000000 }, - { 866, 866, 0, 0, 300, 300,0.000000 }, - { 867, 867, 0, 0, 40000, 33,0.000000 }, - { 868, 868, 0, 0, 40000, 33,0.000000 }, - { 869, 869, 0, 0, 40000, 20,0.000000 }, - { 870, 870, 0, 0, 2106, 2106,0.000000 }, - { 871, 871, 0, 0, 93, 93,0.000000 }, - { 872, 872, 0, 0, 213, 213,0.000000 }, - { 361, 361, 0, 0, 80, 80,0.000000 }, - { 873, 873, 0, 0, 100, 100,0.000000 }, - { 874, 874, 0, 0, 80, 80,0.000000 }, - { 875, 875, 0, 0, 300, 300,0.000000 }, - { 876, 876, 0, 0, 3186, 3186,0.000000 }, - { 877, 877, 0, 0, 226, 226,0.000000 }, - { 878, 878, 0, 0, 53, 53,0.000000 }, - { 879, 879, 0, 0, 2833, 2833,0.000000 }, - { 880, 880, 0, 0, 113, 113,0.000000 }, - { 881, 881, 0, 0, 40000, 40,0.000000 }, - { 882, 882, 0, 0, 40000, 300,0.000000 }, - { 883, 883, 0, 0, 2793, 2793,0.000000 }, - { 884, 884, 36, 0, 73, 73,0.000000 }, - { 885, 885, 48, 0, 60, 60,0.000000 }, - { 885, 885, 36, 0, 73, 73,0.000000 }, - { 886, 886, 36, 0, 20, 20,0.000000 }, - { 887, 887, 32, 0, 46, 46,0.000000 }, - { 767, 767, 96, 0, 580, 580,0.000000 }, - { 888, 888, 30, 0, 53, 53,0.000000 }, - { 889, 889, 35, 0, 106, 106,0.000000 }, - { 890, 890, 60, 0, 86, 86,0.000000 }, - { 884, 884, 59, 0, 40, 40,0.000000 }, - { 890, 890, 44, 0, 80, 80,0.000000 }, - { 891, 891, 41, 0, 166, 166,0.000000 }, - { 892, 892, 47, 0, 40, 40,0.000000 }, - { 891, 891, 44, 0, 140, 140,0.000000 }, - { 891, 891, 48, 0, 153, 153,0.000000 }, - { 893, 893, 62, 0, 606, 606,0.000000 }, - { 891, 891, 51, 0, 160, 160,0.000000 }, - { 891, 891, 54, 0, 160, 160,0.000000 }, - { 894, 894, 40, 0, 386, 386,0.000000 }, - { 891, 891, 57, 0, 113, 113,0.000000 }, - { 895, 895, 97, 0, 73, 73,0.000000 }, - { 896, 896, 50, 0, 86, 86,0.000000 }, - { 376, 376, 60, 0, 153, 153,0.000000 }, - { 897, 897, 53, 0, 33, 33,0.000000 }, - { 898, 898, 46, 0, 53, 53,0.000000 }, - { 897, 897, 57, 0, 33, 33,0.000000 }, - { 899, 899, 42, 0, 193, 193,0.000000 }, - { 899, 899, 37, 0, 200, 200,0.000000 }, - { 900, 900, 41, 0, 200, 200,0.000000 }, - { 900, 900, 37, 0, 193, 193,0.000000 }, - { 871, 871, 77, 0, 46, 46,0.000000 }, - { 871, 871, 72, 0, 40, 40,0.000000 }, - { 388, 388, 70, 0, 46, 46,0.000000 }, - { 901, 901, 39, 0, 173, 173,0.000000 }, - { 902, 902, 36, 0, 1006, 1006,0.000000 }, - { 903, 903, 46, 0, 33, 33,0.000000 }, - { 904, 904, 48, 0, 680, 680,0.000000 }, - { 905, 905, 85, 0, 33, 33,0.000000 }, - { 361, 361, 66, 0, 60, 60,0.000000 }, - { 906, 906, 41, 0, 106, 106,0.000000 }, - { 907, 907, 41, 0, 93, 93,0.000000 }, - { 908, 908, 81, 0, 33, 33,0.000000 }, - { 400, 400, 10, 0, 313, 313,0.000000 }, - { 886, 886, 60, 0, 13, 13,0.000000 }, - { 873, 873, 53, 0, 166, 166,0.000000 }, - { 909, 909, 0, 0, 566, 566,0.000000 }, - { 910, 910, 0, 0, 1313, 1313,0.000000 }, - { 911, 911, 0, 0, 760, 760,0.000000 }, - { 912, 912, 0, 0, 760, 760,0.000000 }, - { 913, 913, 0, 0, 1740, 1740,0.000000 }, - { 914, 914, 0, 0, 1900, 1900,0.000000 }, - { 915, 915, 0, 0, 1173, 1173,0.000000 }, - { 916, 916, 0, 0, 1233, 1233,0.000000 }, - { 917, 917, 0, 0, 493, 493,0.000000 }, - { 362, 362, 0, 0, 246, 246,0.000000 }, - { 918, 918, 0, 0, 226, 226,0.000000 }, - { 919, 919, 0, 0, 1740, 1740,0.000000 }, - { 920, 920, 0, 0, 246, 246,0.000000 }, - { 921, 921, 0, 0, 120, 120,0.000000 }, - { 922, 922, 0, 0, 606, 606,0.000000 }, - { 923, 923, 0, 0, 333, 333,0.000000 }, - { 924, 924, 0, 0, 40000, 6,0.000000 }, - { 925, 925, 0, 0, 40000, 6,0.000000 }, - { 926, 926, 0, 0, 13, 13,0.000000 }, - { 927, 927, 0, 0, 40000, 66,0.000000 }, - { 928, 928, 0, 0, 40000, 26,0.000000 }, - { 929, 929, 0, 0, 40000, 20,0.000000 }, - { 930, 930, 0, 0, 40000, 20,0.000000 }, - { 931, 931, 0, 0, 40000, 33,0.000000 }, - { 932, 932, 0, 0, 866, 866,0.000000 }, - { 933, 933, 0, 0, 566, 566,0.000000 }, - { 934, 934, 0, 0, 1900, 1900,0.000000 }, - { 935, 935, 0, 0, 1146, 1146,0.000000 }, - { 936, 936, 0, 0, 606, 606,0.000000 }, - { 937, 937, 0, 0, 4673, 4673,0.000000 }, - { 938, 938, 0, 0, 3126, 3126,0.000000 }, - { 939, 939, 0, 0, 2406, 2406,0.000000 }, - { 940, 940, 0, 0, 440, 440,0.000000 }, - { 941, 941, 0, 0, 3200, 3200,0.000000 }, - { 942, 942, 0, 0, 1000, 1000,0.000000 }, - { 943, 943, 0, 0, 593, 593,0.000000 }, - { 944, 944, 0, 0, 546, 546,0.000000 }, - { 945, 945, 0, 0, 620, 620,0.000000 }, - { 946, 946, 0, 0, 1206, 1206,0.000000 }, - { 947, 947, 0, 0, 680, 680,0.000000 }, - { 948, 948, 0, 0, 40000, 20,0.000000 }, - { 949, 949, 0, 0, 40000, 20,0.000000 }, - { 950, 950, 0, 0, 4793, 33,0.000000 }, - { 951, 951, 0, 0, 1773, 1773,0.000000 }, - { 952, 952, 0, 0, 40000, 86,0.000000 }, - { 953, 953, 0, 0, 173, 173,0.000000 }, - { 954, 954, 0, 0, 406, 406,0.000000 }, - { 955, 955, 0, 0, 280, 280,0.000000 }, - { 956, 956, 0, 0, 5453, 33,0.000000 }, - { 957, 957, 0, 0, 2466, 100,0.000000 }, - { 958, 958, 0, 0, 973, 973,0.000000 }, - { 959, 959, 0, 0, 40000, 53,0.000000 }, - { 960, 960, 0, 0, 40000, 33,0.000000 }, - { 961, 961, 0, 0, 1026, 13,0.000000 }, - { 962, 962, 0, 0, 166, 166,0.000000 }, - { 963, 963, 0, 0, 806, 806,0.000000 }, - { 964, 964, 0, 0, 40000, 20,0.000000 }, - { 965, 965, 0, 0, 60, 20,0.000000 }, - { 966, 966, 0, 0, 1020, 1020,0.000000 }, - { 967, 967, 0, 0, 3426, 3426,0.000000 }, - { 968, 968, 0, 0, 40000, 20,0.000000 }, - { 969, 969, 0, 0, 40000, 20,0.000000 }, - { 970, 970, 0, 0, 40000, 106,0.000000 }, - { 971, 971, 0, 0, 40000, 6,0.000000 }, - { 972, 972, 0, 0, 40000, 0,0.000000 }, - { 973, 973, 0, 0, 2280, 2280,0.000000 }, - { 974, 974, 0, 0, 40000, 20,0.000000 }, - { 975, 975, 0, 0, 40000, 6,0.000000 }, - { 976, 976, 0, 0, 40000, 6,0.000000 }, - { 977, 977, 0, 0, 40000, 0,0.000000 }, - { 978, 978, 0, 0, 40000, 6,0.000000 }, - { 979, 979, 0, 0, 40000, 33,0.000000 }, - { 980, 980, 0, 0, 40000, 0,0.000000 }, - { 981, 981, 0, 0, 40000, 13,0.000000 }, - { 982, 982, 0, 0, 40000, 20,0.000000 }, - { 983, 983, 0, 0, 40000, 33,0.000000 }, - { 984, 984, 0, 0, 40000, 20,0.000000 }, - { 985, 985, 0, 0, 40000, 33,0.000000 }, - { 986, 986, 0, 0, 40000, 20,0.000000 }, - { 987, 987, 0, 0, 40000, 6,0.000000 }, - { 988, 988, 0, 0, 1133, 1133,0.000000 }, - { 989, 989, 0, 0, 40000, 20,0.000000 }, - { 990, 990, 0, 0, 1340, 1340,0.000000 }, - { 991, 991, 0, 0, 3533, 3533,0.000000 }, - { 992, 992, 0, 0, 40000, 6,0.000000 }, - { 993, 993, 0, 0, 40000, 26,0.000000 }, - { 994, 994, 0, 0, 4140, 4140,0.000000 }, - { 995, 995, 0, 0, 1206, 1206,0.000000 }, - { 996, 996, 0, 0, 40000, 173,0.000000 }, - { 997, 997, 0, 0, 380, 80,0.000000 }, - { 998, 998, 0, 0, 40000, 26,0.000000 }, - { 999, 999, 0, 0, 40000, 20,0.000000 }, - {1000,1000, 0, 0, 2893, 2893,0.000000 }, - {1001,1001, 0, 0, 40000, 73,0.000000 }, - {1002,1002, 0, 0, 40000, 126,0.000000 }, - {1003,1003, 0, 0, 4373, 4373,0.000000 }, - {1004,1004, 0, 0, 40000, 240,0.000000 }, - {1005,1005, 0, 0, 993, 993,0.000000 }, - {1006,1006, 0, 0, 760, 760,0.000000 }, - {1007,1007, 0, 0, 1380, 1380,0.000000 }, - {1008,1008, 0, 0, 726, 53,0.000000 }, - {1009,1009, 0, 0, 40000, 33,0.000000 }, - {1010,1010, 0, 0, 40000, 213,0.000000 }, - {1011,1011, 0, 0, 1046, 1046,0.000000 }, - {1012,1012, 0, 0, 273, 273,0.000000 }, - {1013,1013, 0, 0, 626, 626,0.000000 }, - {1014,1014, 0, 0, 1166, 1166,0.000000 }, - {1015,1015, 0, 0, 153, 153,0.000000 }, - {1016,1016, 0, 0, 40000, 6,0.000000 }, - {1017,1017, 0, 0, 40000, 33,0.000000 }, - {1018,1018, 0, 0, 40000, 20,0.000000 }, - {1019,1019, 0, 0, 2106, 2106,0.000000 }, - { 444, 444, 0, 0, 86, 86,0.000000 }, - {1020,1020, 0, 0, 180, 180,0.000000 }, - { 449, 449, 0, 0, 80, 80,0.000000 }, - { 453, 453, 0, 0, 20, 20,0.000000 }, - {1021,1021, 0, 0, 20, 20,0.000000 }, - {1022,1022, 0, 0, 246, 246,0.000000 }, - {1023,1023, 0, 0, 3186, 3186,0.000000 }, - {1024,1024, 0, 0, 226, 226,0.000000 }, - {1025,1025, 0, 0, 186, 186,0.000000 }, - {1026,1026, 0, 0, 2833, 2833,0.000000 }, - {1027,1027, 0, 0, 2460, 2460,0.000000 }, - {1028,1028, 0, 0, 40000, 40,0.000000 }, - {1029,1029, 0, 0, 40000, 286,0.000000 }, - {1030,1030, 0, 0, 40000, 146,0.000000 }, - {1031,1031, 0, 0, 586, 586,0.000000 }, - {1032,1032, 32, 0, 46, 46,0.000000 }, - {1033,1033, 30, 0, 26, 26,0.000000 }, - {1034,1034, 96, 0, 40, 40,0.000000 }, - {1035,1035, 60, 0, 186, 186,0.000000 }, - {1036,1036, 0, 0, 1100, 1100,0.000000 }, - {1037,1037, 0, 0, 40000, 33,0.000000 }, - {1038,1038, 0, 0, 1740, 1740,0.000000 }, - {1039,1039, 0, 0, 1793, 1793,0.000000 }, - {1040,1040, 0, 0, 160, 160,0.000000 }, - {1041,1041, 0, 0, 1206, 1206,0.000000 }, - {1042,1042, 0, 0, 353, 353,0.000000 }, - {1043,1043, 0, 0, 40000, 6,0.000000 }, - {1044,1044, 0, 0, 40000, 0,0.000000 }, - {1045,1045, 0, 0, 40000, 20,0.000000 }, - {1046,1046, 0, 0, 1186, 1186,0.000000 }, - {1047,1047, 0, 0, 1093, 1093,0.000000 }, - {1048,1048, 0, 0, 620, 620,0.000000 }, - {1049,1049, 0, 0, 4440, 4440,0.000000 }, - {1050,1050, 0, 0, 4506, 4506,0.000000 }, - {1051,1051, 0, 0, 4673, 4673,0.000000 }, - {1052,1052, 0, 0, 2386, 2386,0.000000 }, - {1053,1053, 0, 0, 40000, 146,0.000000 }, - {1054,1054, 0, 0, 1180, 1180,0.000000 }, - {1055,1055, 0, 0, 320, 320,0.000000 }, - {1056,1056, 0, 0, 633, 633,0.000000 }, - {1057,1057, 0, 0, 80, 80,0.000000 }, - {1058,1058, 0, 0, 1180, 1180,0.000000 }, - {1059,1059, 0, 0, 6120, 100,0.000000 }, - {1060,1060, 0, 0, 86, 86,0.000000 }, - {1061,1061, 0, 0, 206, 206,0.000000 }, - {1062,1062, 0, 0, 40000, 86,0.000000 }, - {1063,1063, 0, 0, 40000, 266,0.000000 }, - {1064,1064, 0, 0, 806, 806,0.000000 }, - {1065,1065, 0, 0, 40000, 20,0.000000 }, - {1066,1066, 0, 0, 40000, 53,0.000000 }, - {1067,1067, 0, 0, 40000, 53,0.000000 }, - {1068,1068, 0, 0, 40000, 26,0.000000 }, - {1069,1069, 0, 0, 40000, 20,0.000000 }, - {1070,1070, 0, 0, 40000, 20,0.000000 }, - {1071,1071, 0, 0, 40000, 6,0.000000 }, - {1072,1072, 0, 0, 40000, 26,0.000000 }, - {1073,1073, 0, 0, 40000, 13,0.000000 }, - {1074,1074, 0, 0, 40000, 60,0.000000 }, - {1075,1075, 0, 0, 213, 213,0.000000 }, - {1076,1076, 0, 0, 40000, 73,0.000000 }, - {1077,1077, 0, 0, 4513, 4513,0.000000 }, - {1078,1078, 0, 0, 313, 313,0.000000 }, - {1079,1079, 0, 0, 4273, 4273,0.000000 }, - {1080,1080, 0, 0, 40000, 26,0.000000 }, - {1081,1081, 0, 0, 3153, 3153,0.000000 }, - {1082,1082, 0, 0, 40000, 180,0.000000 }, - {1083,1083, 0, 0, 40000, 213,0.000000 }, - {1084,1084, 0, 0, 1740, 13,0.000000 }, - {1085,1085, 0, 0, 2853, 2853,0.000000 }, - {1086,1086, 0, 0, 40000, 213,0.000000 }, - {1087,1087, 0, 0, 3946, 3946,0.000000 }, - {1088,1088, 0, 0, 2253, 2253,0.000000 }, - {1089,1089, 0, 0, 40000, 260,0.000000 }, - {1090,1090, 0, 0, 166, 166,0.000000 }, - {1091,1091, 0, 0, 40000, 493,0.000000 }, - {1092,1092, 0, 0, 2126, 2126,0.000000 }, - {1093,1093, 0, 0, 40000, 480,0.000000 }, - {1094,1094, 0, 0, 40000, 213,0.000000 }, - {1095,1095, 0, 0, 146, 146,0.000000 }, - {1096,1096, 0, 0, 333, 333,0.000000 }, - {1097,1097, 0, 0, 466, 466,0.000000 }, - {1098,1098, 0, 0, 40000, 40,0.000000 }, - {1099,1099, 0, 0, 4926, 4926,0.000000 }, - {1100,1100, 0, 0, 5113, 5113,0.000000 }, - {1101,1101, 0, 0, 3060, 13,0.000000 }, - {1102,1102, 0, 0, 420, 420,0.000000 }, - {1103,1103, 0, 0, 300, 300,0.000000 }, - {1104,1104, 0, 0, 4426, 4426,0.000000 }, - {1105,1105, 0, 0, 5513, 5513,0.000000 }, - {1106,1106, 0, 0, 40000, 233,0.000000 }, - { 430, 430, 0, 0, 300, 300,0.000000 }, - {1107,1107, 35, 0, 73, 73,0.000000 }, - {1090,1090, 77, 0, 80, 80,0.000000 }, - {1090,1090, 72, 0, 80, 80,0.000000 }, - {1108,1108, 0, 0, 60, 60,0.000000 }, - {1109,1109, 0, 0, 1453, 1453,0.000000 }, - {1110,1111, 0, 1, 506, 506,0.000000 }, - {1112,1113, 0, 1, 1253, 1253,0.031250 }, - {1114,1114, 0, 0, 866, 866,0.000000 }, - {1115,1116, 0, 1, 1473, 1473,0.000000 }, - {1117,1117, 0, 0, 1600, 1600,0.000000 }, - {1118,1118, 0, 0, 680, 680,0.000000 }, - {1119,1119, 0, 0, 1133, 1133,0.000000 }, - {1120,1120, 0, 0, 1013, 1013,0.000000 }, - {1121,1121, 0, 0, 860, 860,0.000000 }, - {1122,1122, 0, 0, 1473, 1473,0.000000 }, - {1123,1123, 0, 0, 280, 280,0.000000 }, - {1124,1124, 0, 0, 53, 53,0.000000 }, - {1125,1125, 0, 0, 1260, 1260,0.000000 }, - {1126,1126, 0, 0, 906, 906,0.000000 }, - {1127,1127, 0, 0, 40000, 66,0.000000 }, - {1128,1128, 0, 0, 260, 13,0.000000 }, - {1129,1130, 0, 1, 13973, 13,0.156250 }, - {1131,1131, 0, 0, 40000, 173,0.000000 }, - {1132,1132, 0, 0, 40000, 53,0.000000 }, - {1133,1134, 0, 1, 40000, 20,-0.046875 }, - {1135,1135, 0, 0, 40000, 26,0.000000 }, - {1136,1137, 0, 1, 40000, 113,0.000025 }, - {1138,1138, 0, 0, 3366, 3366,0.000000 }, - {1139,1139, 0, 0, 400, 400,0.000000 }, - {1140,1140, 0, 0, 473, 473,0.000000 }, - {1141,1141, 0, 0, 840, 840,0.000000 }, - {1142,1142, 0, 0, 800, 800,0.000000 }, - {1143,1143, 0, 0, 2173, 0,0.000000 }, - {1144,1144, 0, 0, 40000, 0,0.000000 }, - {1145,1145, 0, 0, 1433, 1433,0.000000 }, - {1146,1146, 0, 0, 386, 386,0.000000 }, - {1147,1147, 0, 0, 1253, 1253,0.000000 }, - {1148,1148, 0, 0, 1606, 1606,0.000000 }, - {1149,1150, 0, 1, 1553, 1553,-0.031250 }, - {1151,1151, 0, 0, 40000, 6,0.000000 }, - {1152,1152, 0, 0, 1560, 1560,0.000000 }, - {1153,1153, 0, 0, 386, 386,0.000000 }, - {1154,1154, 0, 1, 40000, 20,-0.156250 }, - {1155,1155, 0, 0, 3886, 6,0.000000 }, - {1156,1156, 0, 0, 40000, 66,0.000000 }, - {1157,1157, 0, 0, 40000, 60,0.000000 }, - {1158,1158, 0, 0, 40000, 266,0.000000 }, - {1159,1160, 0, 1, 40000, 273,0.171875 }, - {1161,1161, 0, 0, 173, 173,0.000000 }, - {1162,1162, 0, 0, 993, 993,0.000000 }, - {1163,1163, 0, 0, 726, 726,0.000000 }, - {1164,1165, 0, 1, 40000, 206,-0.125000 }, - {1166,1167, 0, 1, 40000, 266,0.078125 }, - {1168,1168, 0, 1, 40000, 1073,-0.078125 }, - {1169,1170, 0, 1, 40000, 253,0.062500 }, - {1171,1172, 0, 1, 40000, 533,0.156250 }, - {1173,1173, 0, 0, 1813, 1813,0.000000 }, - {1174,1174, 0, 0, 40000, 200,0.000000 }, - {1175,1176, 0, 1, 360, 360,0.000000 }, - {1177,1177, 0, 0, 40000, 6,0.000000 }, - {1178,1178, 0, 0, 806, 6,0.000000 }, - {1179,1179, 0, 0, 1600, 1600,0.000000 }, - {1180,1180, 0, 0, 60, 60,0.000000 }, - {1181,1182, 0, 1, 3153, 3153,0.000025 }, - {1183,1184, 0, 1, 826, 6,0.046875 }, - {1185,1186, 0, 1, 3106, 3106,0.093750 }, - {1187,1188, 0, 1, 1020, 1020,0.093750 }, - {1189,1189, 0, 0, 40000, 33,0.000000 }, - {1190,1190, 0, 0, 40000, 20,0.000000 }, - {1191,1191, 0, 0, 40000, 0,0.000000 }, - {1192,1192, 0, 0, 266, 6,0.000000 }, - {1193,1193, 0, 0, 40000, 33,0.000000 }, - {1194,1194, 0, 0, 40000, 26,0.000000 }, - {1195,1195, 0, 0, 40000, 0,0.000000 }, - {1196,1196, 0, 0, 40000, 13,0.000000 }, - {1197,1197, 0, 0, 40000, 0,0.000000 }, - {1198,1198, 0, 0, 40000, 6,0.000000 }, - {1199,1199, 0, 0, 40000, 53,0.000000 }, - {1200,1200, 0, 0, 40000, 20,0.000000 }, - {1201,1201, 0, 0, 40000, 20,0.000000 }, - {1202,1202, 0, 0, 140, 140,0.000000 }, - {1203,1203, 0, 0, 40000, 93,0.000000 }, - {1204,1204, 0, 0, 40000, 6,0.000000 }, - {1205,1206, 0, 1, 40000, 86,0.031250 }, - {1207,1208, 0, 1, 40000, 100,-0.000025 }, - {1209,1209, 0, 0, 40000, 53,0.000000 }, - {1210,1211, 0, 1, 40000, 0,0.031250 }, - {1212,1212, 0, 0, 40000, 0,0.000000 }, - {1213,1214, 0, 1, 40000, 320,-0.093750 }, - {1215,1216, 0, 1, 1660, 80,-0.046875 }, - {1217,1217, 0, 0, 40000, 66,0.000000 }, - {1218,1219, 0, 1, 40000, 233,0.031250 }, - {1220,1220, 0, 1, 1960, 1960,0.031250 }, - {1221,1222, 0, 1, 17500, 46,0.171875 }, - {1223,1223, 0, 0, 2273, 2273,0.000000 }, - {1224,1224, 0, 0, 1933, 1933,0.000000 }, - {1225,1150, 0, 1, 3126, 13,-0.031250 }, - {1226,1226, 0, 0, 40000, 640,0.000000 }, - {1227,1227, 0, 0, 1080, 1080,0.000000 }, - {1228,1229, 0, 1, 40000, 33,0.125000 }, - {1230,1230, 0, 0, 1046, 1046,0.000000 }, - {1231,1232, 0, 1, 1553, 1553,-0.031250 }, - {1233,1234, 0, 1, 2066, 2066,-0.187500 }, - {1235,1235, 0, 0, 40000, 466,0.000000 }, - {1236,1236, 0, 0, 400, 400,0.000000 }, - {1237,1237, 0, 0, 2706, 2706,0.000000 }, - {1238,1238, 0, 0, 1220, 1220,0.000000 }, - {1239,1239, 0, 0, 446, 446,0.000000 }, - {1240,1241, 0, 1, 360, 360,0.000000 }, - {1242,1242, 0, 0, 4806, 4806,0.000000 }, - {1243,1243, 0, 0, 80, 80,0.000000 }, - {1244,1244, 0, 0, 40000, 80,0.000000 }, - {1245,1245, 0, 0, 40000, 86,0.000000 }, - {1246,1246, 0, 0, 40000, 40,0.000000 }, - {1247,1247, 0, 0, 1920, 1920,0.000000 }, - {1248,1248, 0, 0, 66, 66,0.000000 }, - {1249,1249, 0, 0, 40000, 233,0.000000 }, - {1250,1250, 0, 0, 113, 113,0.000000 }, - {1251,1251, 0, 0, 46, 46,0.000000 }, - {1252,1252, 0, 0, 140, 140,0.000000 }, - {1253,1253, 0, 0, 886, 886,0.000000 }, - {1254,1254, 0, 0, 1080, 1080,0.000000 }, - {1255,1255, 0, 0, 206, 206,0.000000 }, - {1256,1256, 0, 0, 280, 280,0.000000 }, - {1257,1257, 0, 0, 2873, 2873,0.000000 }, - {1258,1258, 0, 0, 313, 313,0.000000 }, - {1259,1259, 0, 0, 40000, 73,0.000000 }, - {1260,1260, 29, 0, 1320, 1320,0.000000 }, - {1261,1261, 65, 0, 40000, 1746,0.000000 }, - {1262,1262, 0, 0, 153, 153,0.000000 }, - {1263,1263, 25, 0, 33, 33,0.000000 }, - {1264,1264, 83, 0, 53, 53,0.000000 }, - {1265,1265, 32, 0, 40, 40,0.000000 }, - {1266,1266, 60, 0, 46, 46,0.000000 }, - {1267,1267, 36, 0, 46, 46,0.000000 }, - {1268,1268, 27, 0, 13, 13,0.000000 }, - {1269,1269, 31, 0, 266, 266,0.000000 }, - {1270,1270, 21, 0, 246, 246,0.000000 }, - {1270,1270, 26, 0, 253, 253,0.000000 }, - {1270,1270, 28, 0, 260, 260,0.000000 }, - {1271,1271, 60, 0, 280, 280,0.000000 }, - {1270,1270, 32, 0, 206, 206,0.000000 }, - {1272,1272, 60, 0, 260, 260,0.000000 }, - {1273,1273, 96, 0, 140, 140,0.000000 }, - {1274,1274, 72, 0, 133, 133,0.000000 }, - {1275,1275, 79, 0, 46, 46,0.000000 }, - {1276,1276, 69, 0, 126, 126,0.000000 }, - {1277,1277, 71, 0, 146, 146,0.000000 }, - {1278,1278, 22, 0, 566, 566,0.000000 }, - {1279,1279, 55, 0, 66, 66,0.000000 }, - {1279,1279, 48, 0, 80, 80,0.000000 }, - {1280,1280, 0, 0, 6, 6,0.000000 }, - {1281,1281, 49, 0, 6, 6,0.000000 }, - {1282,1282, 73, 0, 60, 60,0.000000 }, - {1282,1282, 68, 0, 60, 60,0.000000 }, - {1282,1282, 61, 0, 80, 80,0.000000 }, - {1283,1283, 0, 0, 6, 6,0.000000 }, - {1284,1284, 0, 0, 40000, 46,0.000000 }, - {1285,1285, 0, 0, 40000, 20,0.000000 }, - {1286,1286, 0, 0, 2380, 2380,0.000000 }, - {1287,1287, 0, 0, 700, 700,0.000000 }, - {1288,1289, 0, 1, 653, 6,0.000000 }, - {1290,1290, 0, 0, 80, 80,0.000000 }, - {1291,1292, 36, 1, 46, 46,0.000000 }, - {1293,1293, 69, 0, 126, 126,0.000000 }, - {1294,1294, 0, 0, 120, 120,0.000000 }, - {1295,1295, 0, 0, 40000, 0,0.000000 }, - {1296,1296, 0, 0, 1173, 1173,0.000000 }, - {1297,1297, 22, 0, 566, 566,0.000000 }, - {1298,1298, 0, 0, 40000, 0,0.000000 }, - {1299,1299, 0, 0, 993, 993,0.000000 }, - {1300,1300, 0, 0, 1900, 1900,0.000000 }, - {1301,1301, 0, 0, 1046, 1046,0.000000 }, - {1302,1302, 0, 0, 293, 293,0.000000 }, - {1303,1303, 0, 0, 40000, 33,0.000000 }, - {1304,1304, 0, 0, 40000, 6,0.000000 }, - {1305,1305, 0, 0, 40000, 20,0.000000 }, - {1306,1306, 0, 0, 40000, 193,0.000000 }, - {1307,1307, 0, 0, 40000, 153,0.000000 }, - {1308,1308, 0, 0, 40000, 6,0.000000 }, - {1309,1309, 0, 0, 40000, 46,0.000000 }, - {1310,1310, 0, 0, 40000, 73,0.000000 }, - {1311,1311, 0, 0, 920, 920,0.000000 }, - {1312,1312, 0, 0, 4580, 4580,0.000000 }, - {1313,1313, 0, 0, 1026, 1026,0.000000 }, - {1314,1314, 0, 0, 40000, 6,0.000000 }, - {1315,1315, 0, 0, 3746, 13,0.000000 }, - {1316,1316, 0, 0, 40000, 233,0.000000 }, - {1317,1317, 0, 0, 40000, 153,0.000000 }, - {1318,1318, 0, 0, 40000, 140,0.000000 }, - {1319,1319, 0, 0, 2620, 2620,0.000000 }, - {1320,1320, 0, 0, 40000, 140,0.000000 }, - {1321,1321, 0, 0, 233, 233,0.000000 }, - {1322,1322, 0, 0, 40000, 6,0.000000 }, - {1323,1323, 0, 0, 40000, 0,0.000000 }, - {1324,1324, 0, 0, 40000, 26,0.000000 }, - {1325,1325, 0, 0, 40000, 6,0.000000 }, - { 260, 260, 0, 0, 40000, 20,0.000000 }, - {1326,1326, 0, 0, 40000, 20,0.000000 }, - {1327,1327, 0, 0, 40000, 0,0.000000 }, - {1328,1328, 0, 0, 40000, 13,0.000000 }, - {1329,1329, 0, 0, 40000, 73,0.000000 }, - {1330,1330, 0, 0, 40000, 53,0.000000 }, - {1331,1331, 0, 0, 40000, 66,0.000000 }, - {1332,1332, 0, 0, 40000, 66,0.000000 }, - {1333,1333, 0, 0, 40000, 6,0.000000 }, - {1334,1334, 0, 0, 40000, 73,0.000000 }, - {1335,1335, 0, 0, 40000, 133,0.000000 }, - {1336,1336, 0, 0, 40000, 233,0.000000 }, - {1337,1337, 0, 0, 40000, 406,0.000000 }, - {1338,1338, 0, 0, 1766, 93,0.000000 }, - {1339,1339, 0, 0, 40000, 533,0.000000 }, - {1340,1340, 0, 0, 40000, 106,0.000000 }, - {1341,1341, 0, 0, 593, 13,0.000000 }, - {1342,1342, 0, 0, 940, 940,0.000000 }, - {1343,1343, 0, 0, 40000, 53,0.000000 }, - {1344,1344, 0, 0, 2006, 2006,0.000000 }, - {1345,1345, 0, 0, 40000, 933,0.000000 }, - {1346,1346, 0, 0, 973, 973,0.000000 }, - { 378, 378, 84, 0, 440, 440,0.000000 }, - {1347,1347, 24, 0, 46, 46,0.000000 }, - {1348,1348, 44, 0, 80, 80,0.000000 }, - {1349,1349, 40, 0, 306, 306,0.000000 }, - {1350,1350, 60, 0, 340, 340,0.000000 }, - {1351,1351, 0, 0, 866, 866,0.000000 }, - {1352,1352, 0, 0, 993, 993,0.000000 }, - {1353,1353, 0, 0, 633, 633,0.000000 }, - {1354,1354, 0, 0, 40000, 40,0.000000 }, - {1355,1355, 0, 0, 973, 973,0.000000 }, - {1356,1356, 0, 0, 40000, 126,0.000000 }, - {1357,1357, 0, 0, 166, 166,0.000000 }, - {1358,1358, 0, 0, 640, 640,0.000000 }, - {1359,1359, 0, 0, 100, 100,0.000000 }, - {1360,1360, 0, 0, 40000, 113,0.000000 }, - {1361,1361, 0, 0, 40000, 240,0.000000 }, - {1362,1362, 0, 0, 3793, 80,0.000000 }, - {1363,1363, 0, 0, 40000, 240,0.000000 }, - {1364,1364, 0, 0, 126, 126,0.000000 }, - {1365,1365, 0, 0, 40000, 20,0.000000 }, - {1366,1366, 0, 0, 40000, 26,0.000000 }, - {1367,1367, 0, 0, 2233, 6,0.000000 }, - {1368,1368, 0, 0, 40000, 53,0.000000 }, - {1369,1369, 0, 0, 40000, 33,0.000000 }, - {1370,1370, 0, 0, 40000, 13,0.000000 }, - {1371,1371, 0, 0, 40000, 13,0.000000 }, - {1372,1372, 0, 0, 40000, 26,0.000000 }, - {1373,1373, 0, 0, 40000, 6,0.000000 }, - {1374,1374, 0, 0, 40000, 13,0.000000 }, - {1375,1375, 0, 0, 4473, 13,0.000000 }, - {1376,1376, 0, 0, 146, 146,0.000000 }, - {1377,1377, 0, 0, 86, 86,0.000000 }, - {1378,1378, 35, 0, 73, 73,0.000000 }, - {1379,1379, 49, 0, 40, 40,0.000000 }, - {1377,1377, 48, 0, 80, 80,0.000000 }, - {1380,1380, 58, 0, 40, 40,0.000000 }, - {1377,1377, 60, 0, 86, 86,0.000000 }, - {1381,1381, 47, 0, 106, 106,0.000000 }, - {1382,1382, 60, 0, 20, 20,0.000000 }, - {1381,1381, 49, 0, 153, 153,0.000000 }, - {1383,1383, 72, 0, 80, 80,0.000000 }, - {1381,1381, 51, 0, 200, 200,0.000000 }, - {1384,1384, 84, 0, 273, 273,0.000000 }, - {1381,1381, 54, 0, 233, 233,0.000000 }, - {1381,1381, 57, 0, 260, 260,0.000000 }, - {1385,1385, 72, 0, 506, 506,0.000000 }, - {1381,1381, 60, 0, 306, 306,0.000000 }, - {1386,1386, 36, 0, 866, 866,0.000000 }, - {1387,1387, 93, 0, 66, 66,0.000000 }, - {1388,1388, 72, 0, 273, 273,0.000000 }, - {1389,1389, 84, 0, 426, 426,0.000000 }, - {1390,1390, 36, 0, 20, 20,0.000000 }, - {1391,1391, 64, 0, 86, 86,0.000000 }, - {1392,1392, 68, 0, 26, 26,0.000000 }, - {1393,1393, 0, 0, 1226, 1226,0.000000 }, - {1394,1394, 0, 0, 40000, 140,0.000000 }, - {1395,1395, 0, 0, 40000, 0,0.000000 }, - {1396,1396, 0, 0, 513, 513,0.000000 }, - {1397,1397, 0, 0, 40000, 126,0.000000 }, - {1398,1398, 0, 0, 320, 320,0.000000 }, - {1399,1399, 0, 0, 40000, 0,0.000000 }, - {1400,1400, 0, 0, 1766, 1766,0.000000 }, - {1401,1401, 0, 0, 2513, 2513,0.000000 }, - {1402,1402, 0, 0, 1300, 1300,0.000000 }, - {1403,1403, 0, 0, 46, 46,0.000000 }, - {1404,1404, 0, 0, 186, 6,0.000000 }, - {1405,1405, 0, 0, 40000, 6,0.000000 }, - {1406,1406, 0, 0, 40, 26,0.000000 }, - {1407,1407, 0, 0, 166, 166,0.000000 }, - {1408,1408, 0, 0, 2193, 2193,0.000000 }, - {1409,1409, 0, 0, 40000, 206,0.000000 }, - {1410,1410, 0, 0, 40000, 1460,0.000000 }, - {1411,1411, 0, 0, 166, 166,0.000000 }, - {1412,1412, 0, 0, 1380, 1380,0.000000 }, - {1413,1413, 0, 0, 533, 533,0.000000 }, - {1414,1414, 0, 0, 40000, 33,0.000000 }, - {1415,1415, 0, 0, 40000, 53,0.000000 }, - {1416,1416, 0, 0, 40000, 0,0.000000 }, - {1417,1417, 0, 0, 40000, 6,0.000000 }, - {1418,1418, 0, 0, 40000, 6,0.000000 }, - {1419,1419, 0, 0, 913, 913,0.000000 }, - {1420,1420, 0, 0, 1233, 6,0.000000 }, - {1421,1421, 0, 0, 126, 126,0.000000 }, - {1422,1422, 0, 0, 40000, 46,0.000000 }, - {1423,1423, 0, 0, 933, 933,0.000000 }, - {1424,1424, 0, 0, 1846, 1846,0.000000 }, - {1425,1425, 0, 0, 973, 973,0.000000 }, - {1426,1426, 0, 0, 160, 160,0.000000 }, - {1427,1427, 0, 0, 40000, 106,0.000000 }, - {1428,1428, 0, 0, 120, 120,0.000000 }, - {1429,1429, 0, 0, 40000, 6,0.000000 }, - {1430,1430, 0, 0, 93, 93,0.000000 }, - {1431,1431, 0, 0, 40, 40,0.000000 }, - {1432,1432, 0, 0, 2420, 2420,0.000000 }, - {1433,1433, 0, 0, 1153, 1153,0.000000 }, - {1434,1434, 0, 0, 2406, 2406,0.000000 }, - { 740, 740, 0, 0, 80, 80,0.000000 }, - {1435,1435, 0, 0, 40000, 3220,0.000000 }, - { 739, 739, 48, 0, 93, 93,0.000000 }, - { 500, 500, 55, 0, 13, 13,0.000000 }, - { 740, 740, 60, 0, 53, 53,0.000000 }, - { 500, 500, 41, 0, 13, 13,0.000000 }, - {1436,1436, 84, 0, 40, 40,0.000000 }, - {1437,1437, 84, 0, 73, 73,0.000000 }, - { 500, 500, 48, 0, 13, 13,0.000000 }, - {1438,1438, 15, 0, 86, 86,0.000000 }, - { 752, 752, 49, 0, 46, 46,0.000000 }, - {1438,1438, 16, 0, 93, 93,0.000000 }, - {1438,1438, 12, 0, 86, 86,0.000000 }, - { 740, 740, 55, 0, 53, 53,0.000000 }, - { 752, 752, 18, 0, 46, 46,0.000000 }, - { 752, 752, 15, 0, 46, 46,0.000000 }, - { 752, 752, 17, 0, 46, 46,0.000000 }, - {1439,1440, 0, 0, 1340, 1340,0.000000 }, - {1441,1442, 0, 0, 1113, 1113,0.000000 }, - {1443,1444, 0, 0, 2133, 2133,0.000000 }, - {1445,1446, 0, 0, 953, 953,0.000000 }, - {1447,1448, 0, 0, 100, 100,0.000000 }, - {1449,1450, 0, 0, 1313, 1313,0.000000 }, - { 181,1451, 0, 0, 473, 473,0.000000 }, - {1452,1453, 0, 0, 680, 680,0.000000 }, - {1454,1455, 0, 0, 260, 260,0.000000 }, - {1456,1457, 0, 0, 193, 193,0.000000 }, - {1458,1459, 0, 0, 1320, 1320,0.000000 }, - { 190,1460, 0, 0, 646, 6,0.000000 }, - { 192,1461, 0, 0, 40000, 6,0.000000 }, - {1462,1463, 0, 0, 40000, 113,0.000000 }, - {1464,1465, 0, 0, 40000, 133,0.000000 }, - {1466,1467, 0, 0, 40000, 6,0.000000 }, - {1468,1469, 0, 0, 40000, 0,0.000000 }, - { 35,1470, 0, 0, 1446, 1446,0.000000 }, - { 36,1471, 0, 0, 1606, 1606,0.000000 }, - {1472,1473, 0, 0, 1553, 1553,0.000000 }, - {1474,1475, 0, 0, 2133, 2133,0.000000 }, - { 39,1476, 0, 0, 40000, 26,0.000000 }, - {1477,1476, 0, 0, 40000, 46,0.000000 }, - {1478,1479, 0, 0, 40000, 240,0.000000 }, - { 50,1480, 0, 0, 40000, 153,0.000000 }, - {1481,1482, 0, 0, 40000, 46,0.000000 }, - {1483,1484, 0, 0, 2300, 2300,0.000000 }, - {1485,1486, 0, 0, 40000, 86,0.000000 }, - { 55,1487, 0, 0, 40000, 20,0.000000 }, - {1488,1489, 0, 0, 40000, 20,0.000000 }, - {1490,1491, 0, 0, 40000, 6,0.000000 }, - {1492,1493, 0, 0, 40000, 0,0.000000 }, - {1494,1495, 0, 0, 40000, 6,0.000000 }, - {1496,1497, 0, 0, 40000, 6,0.000000 }, - {1496,1498, 0, 0, 40000, 6,0.000000 }, - {1499,1500, 0, 0, 40000, 6,0.000000 }, - {1501,1502, 0, 0, 40000, 53,0.000000 }, - {1503,1504, 0, 0, 40000, 33,0.000000 }, - {1505,1506, 0, 0, 40000, 6,0.000000 }, - { 86,1507, 0, 0, 40000, 20,0.000000 }, - {1508,1509, 0, 0, 1766, 1766,0.000000 }, - {1510,1511, 0, 0, 2853, 13,0.000000 }, - {1512,1513, 0, 0, 593, 593,0.000000 }, - {1514,1515, 0, 0, 1126, 33,0.000000 }, - {1516,1517, 0, 0, 40000, 460,0.000000 }, - {1518,1519, 0, 0, 233, 233,0.000000 }, - {1520,1521, 0, 0, 40000, 213,0.000000 }, - {1522,1523, 0, 0, 1233, 1233,0.000000 }, - {1524,1525, 0, 0, 40000, 626,0.000000 }, - {1526,1527, 0, 0, 40000, 446,0.000000 }, - {1528,1529, 0, 0, 40000, 320,0.000000 }, - { 111,1530, 0, 0, 786, 786,0.000000 }, - {1531,1532, 0, 0, 20, 20,0.000000 }, - { 115,1533, 0, 0, 26, 26,0.000000 }, - {1534,1535, 0, 0, 166, 166,0.000000 }, - {1536,1537, 0, 0, 2280, 2280,0.000000 }, - {1538,1539, 0, 0, 280, 280,0.000000 }, - {1540, 339, 0, 0, 580, 580,0.000000 }, - {1541, 339, 0, 0, 4613, 4613,0.000000 }, - {1542,1543, 0, 0, 440, 440,0.000000 }, - {1544,1545, 0, 0, 153, 153,0.000000 }, - {1546,1547, 0, 0, 40000, 53,0.000000 }, - { 364, 365, 44, 0, 20, 20,0.000000 }, - { 129,1548, 48, 0, 66, 66,0.000000 }, - { 367, 368, 58, 0, 53, 53,0.000000 }, - { 129,1549, 60, 0, 73, 73,0.000000 }, - {1550,1551, 48, 0, 93, 93,0.000000 }, - { 132,1552, 43, 0, 13, 13,0.000000 }, - {1550,1551, 49, 0, 120, 120,0.000000 }, - {1553,1554, 43, 0, 26, 26,0.000000 }, - {1550,1551, 51, 0, 153, 153,0.000000 }, - { 134,1555, 43, 0, 146, 146,0.000000 }, - {1550,1551, 54, 0, 106, 106,0.000000 }, - {1550,1551, 57, 0, 100, 100,0.000000 }, - { 380, 381, 72, 0, 40000, 0,0.000000 }, - {1550,1551, 60, 0, 106, 106,0.000000 }, - {1556,1557, 70, 0, 246, 246,0.000000 }, - { 374, 375, 60, 0, 40000, 0,0.000000 }, - {1558,1559, 36, 0, 360, 360,0.000000 }, - {1560,1561, 65, 0, 113, 113,0.000000 }, - {1562,1563, 84, 0, 40000, 0,0.000000 }, - {1564,1565, 59, 0, 133, 133,0.000000 }, - {1566,1567, 84, 0, 40000, 0,0.000000 }, - {1568,1569, 35, 0, 53, 53,0.000000 }, - {1570,1571, 44, 0, 273, 273,0.000000 }, - {1572,1573, 67, 0, 66, 66,0.000000 }, - {1574,1575, 66, 0, 46, 46,0.000000 }, - { 145,1576, 59, 0, 13, 13,0.000000 }, - {1577,1578, 51, 0, 40, 40,0.000000 }, - {1579,1580, 45, 0, 40, 40,0.000000 }, - {1581,1582, 71, 0, 120, 120,0.000000 }, - { 149,1583, 60, 0, 180, 180,0.000000 }, - {1584,1585, 58, 0, 166, 166,0.000000 }, - {1586,1587, 53, 0, 140, 140,0.000000 }, - { 397,1588, 64, 0, 86, 86,0.000000 }, - {1589,1590, 71, 0, 13, 13,0.000000 }, - {1591,1592, 61, 0, 313, 313,0.000000 }, - {1593,1594, 61, 0, 513, 513,0.000000 }, - { 391, 392, 48, 0, 80, 80,0.000000 }, - { 391, 393, 48, 0, 80, 80,0.000000 }, - {1595,1596, 69, 0, 20, 20,0.000000 }, - { 159,1597, 68, 0, 20, 20,0.000000 }, - { 159,1597, 63, 0, 33, 33,0.000000 }, - {1598,1599, 74, 0, 333, 333,0.000000 }, - {1600,1601, 60, 0, 380, 380,0.000000 }, - {1602,1603, 80, 0, 60, 60,0.000000 }, - {1604,1605, 64, 0, 646, 646,0.000000 }, - {1606,1607, 69, 0, 33, 33,0.000000 }, - { 398, 399, 55, 0, 500, 500,0.000000 }, - {1608,1609, 75, 0, 346, 346,0.000000 }, - {1610,1611, 68, 0, 20, 20,0.000000 }, - {1612,1613, 48, 0, 46, 46,0.000000 }, - {1614,1615, 53, 0, 40, 40,0.000000 }, - {1616,1616, 0, 0, 40000, 873,0.000000 }, - {1617,1617, 0, 0, 40000, 446,0.000000 }, - {1618,1618, 0, 0, 1020, 1020,0.000000 }, - {1619,1619, 0, 0, 40000, 300,0.000000 }, - {1620,1620, 0, 0, 40000, 780,0.000000 }, - {1621,1621, 0, 0, 1400, 1400,0.000000 }, - {1622,1622, 0, 0, 40000, 480,0.000000 }, - {1623,1623, 0, 0, 40000, 566,0.000000 }, - {1624,1624, 0, 0, 3220, 3220,0.000000 }, - {1625,1625, 0, 0, 40000, 6,0.000000 }, - {1626,1626, 0, 0, 40000, 20,0.000000 }, - {1627,1627, 0, 0, 40000, 20,0.000000 }, - {1628,1628, 0, 0, 40000, 1180,0.000000 }, - {1629,1629, 0, 0, 40000, 20,0.000000 }, - {1630,1630, 0, 0, 40000, 6,0.000000 }, - {1631,1631, 0, 0, 40000, 6,0.000000 }, - {1632,1632, 0, 0, 40000, 280,0.000000 }, - {1633,1633, 0, 0, 40000, 6,0.000000 }, - {1634,1634, 0, 0, 40000, 6,0.000000 }, - {1635,1635, 0, 0, 40000, 6,0.000000 }, - {1636,1636, 0, 0, 340, 26,0.000000 }, - {1637,1637, 0, 0, 1000, 1000,0.000000 }, - {1638,1638, 0, 0, 40000, 200,0.000000 }, - {1639,1639, 0, 0, 460, 460,0.000000 }, - {1640,1640, 0, 0, 40000, 146,0.000000 }, - {1641,1641, 0, 0, 40000, 146,0.000000 }, - {1642,1642, 0, 0, 40000, 233,0.000000 }, - {1643,1643, 0, 0, 40000, 480,0.000000 }, - {1644,1644, 0, 0, 40000, 6,0.000000 }, - {1645,1645, 0, 0, 40000, 6,0.000000 }, - {1646,1646, 0, 0, 40000, 6,0.000000 }, - {1647,1647, 0, 0, 40000, 6,0.000000 }, - {1648,1648, 0, 0, 40000, 6,0.000000 }, - {1649,1649, 0, 0, 40000, 6,0.000000 }, - {1650,1650, 0, 0, 40000, 53,0.000000 }, - {1651,1651, 0, 0, 40000, 480,0.000000 }, - {1652,1652, 0, 0, 40000, 286,0.000000 }, - {1653,1653, 0, 0, 40000, 106,0.000000 }, - {1654,1654, 0, 0, 40000, 413,0.000000 }, - {1655,1655, 0, 0, 1213, 1213,0.000000 }, - {1656,1656, 0, 0, 513, 513,0.000000 }, - {1657,1657, 0, 0, 40000, 160,0.000000 }, - {1658,1658, 0, 0, 40000, 153,0.000000 }, - {1659,1659, 0, 0, 40000, 1973,0.000000 }, - {1660,1660, 0, 0, 40000, 3593,0.000000 }, - {1661,1661, 0, 0, 40000, 1120,0.000000 }, - {1662,1662, 0, 0, 620, 620,0.000000 }, - {1663,1663, 0, 0, 1740, 1740,0.000000 }, - {1664,1664, 0, 0, 940, 940,0.000000 }, - {1665,1665, 0, 0, 40000, 260,0.000000 }, - {1666,1666, 0, 0, 1900, 1900,0.000000 }, - {1667,1667, 0, 0, 40000, 426,0.000000 }, - {1668,1668, 12, 0, 66, 66,0.000000 }, - {1669,1669, 48, 0, 100, 100,0.000000 }, - { 736, 736, 52, 0, 80, 80,0.000000 }, - {1670,1670, 48, 0, 153, 153,0.000000 }, - {1670,1670, 36, 0, 146, 146,0.000000 }, - { 377, 377, 84, 0, 73, 73,0.000000 }, - { 730, 730, 95, 0, 600, 600,0.000000 }, - {1669,1669, 84, 0, 86, 86,0.000000 }, - { 755, 755, 20, 0, 200, 200,0.000000 }, - { 755, 755, 22, 0, 193, 193,0.000000 }, - { 755, 755, 24, 0, 200, 200,0.000000 }, - {1671,1671, 0, 0, 400, 400,0.000000 }, - {1672,1672, 0, 0, 460, 460,0.000000 }, - {1673,1673, 0, 0, 566, 566,0.000000 }, - {1674,1674, 0, 0, 40, 40,0.000000 }, - {1675,1675, 0, 0, 913, 913,0.000000 }, - {1676,1676, 0, 0, 940, 940,0.000000 }, - {1677,1677, 0, 0, 553, 553,0.000000 }, - {1678,1678, 0, 0, 653, 653,0.000000 }, - {1679,1679, 0, 0, 140, 140,0.000000 }, - {1680,1680, 0, 0, 440, 440,0.000000 }, - {1681,1681, 0, 0, 493, 493,0.000000 }, - {1682,1682, 0, 0, 40000, 213,0.000000 }, - {1683,1683, 0, 0, 246, 246,0.000000 }, - {1684,1684, 0, 0, 140, 140,0.000000 }, - {1685,1685, 0, 0, 880, 880,0.000000 }, - {1686,1686, 0, 0, 700, 700,0.000000 }, - {1687,1687, 0, 0, 40000, 0,0.000000 }, - {1688,1688, 0, 0, 40000, 6,0.000000 }, - {1689,1689, 0, 0, 86, 86,0.000000 }, - {1690,1690, 0, 0, 40000, 6,0.000000 }, - {1691,1691, 0, 0, 40000, 6,0.000000 }, - {1692,1692, 0, 0, 40000, 6,0.000000 }, - {1693,1693, 0, 0, 40000, 6,0.000000 }, - {1694,1694, 0, 0, 440, 440,0.000000 }, - {1695,1695, 0, 0, 440, 440,0.000000 }, - {1696,1696, 0, 0, 100, 0,0.000000 }, - {1697,1697, 0, 0, 1846, 1846,0.000000 }, - {1698,1698, 0, 0, 40, 40,0.000000 }, - {1699,1699, 0, 0, 6580, 6580,0.000000 }, - {1700,1700, 0, 0, 1846, 1846,0.000000 }, - {1701,1701, 0, 0, 1080, 1080,0.000000 }, - {1702,1702, 0, 0, 40000, 0,0.000000 }, - {1703,1703, 0, 0, 1020, 1020,0.000000 }, - {1704,1704, 0, 0, 40000, 53,0.000000 }, - {1705,1705, 0, 0, 40000, 0,0.000000 }, - {1706,1706, 0, 0, 40000, 0,0.000000 }, - {1707,1707, 0, 0, 40000, 53,0.000000 }, - {1708,1708, 0, 0, 40000, 0,0.000000 }, - {1709,1709, 0, 0, 40000, 26,0.000000 }, - {1710,1710, 0, 0, 626, 626,0.000000 }, - {1711,1711, 0, 0, 40000, 26,0.000000 }, - {1712,1712, 0, 0, 1226, 13,0.000000 }, - {1713,1713, 0, 0, 40000, 0,0.000000 }, - {1714,1714, 0, 0, 3980, 13,0.000000 }, - {1715,1715, 0, 0, 253, 253,0.000000 }, - {1716,1716, 0, 0, 940, 940,0.000000 }, - {1717,1717, 0, 0, 126, 126,0.000000 }, - {1718,1718, 0, 0, 3766, 13,0.000000 }, - {1719,1719, 0, 0, 660, 200,0.000000 }, - {1720,1720, 0, 0, 40000, 20,0.000000 }, - {1721,1721, 0, 0, 40000, 20,0.000000 }, - {1722,1722, 0, 0, 40000, 146,0.000000 }, - {1723,1723, 0, 0, 40000, 26,0.000000 }, - {1724,1724, 0, 0, 40000, 140,0.000000 }, - {1725,1725, 0, 0, 260, 260,0.000000 }, - {1726,1726, 0, 0, 40000, 0,0.000000 }, - {1727,1727, 0, 0, 126, 0,0.000000 }, - {1728,1728, 0, 0, 386, 6,0.000000 }, - {1729,1729, 0, 0, 40000, 33,0.000000 }, - {1730,1730, 0, 0, 126, 33,0.000000 }, - {1731,1731, 0, 0, 1580, 1580,0.000000 }, - {1732,1732, 0, 0, 40000, 6,0.000000 }, - {1733,1733, 0, 0, 40000, 6,0.000000 }, - {1734,1734, 0, 0, 40000, 6,0.000000 }, - {1735,1735, 0, 0, 40000, 13,0.000000 }, - {1736,1736, 0, 0, 40000, 13,0.000000 }, - {1737,1737, 0, 0, 40000, 20,0.000000 }, - {1738,1738, 0, 0, 40000, 13,0.000000 }, - {1739,1739, 0, 0, 40000, 13,0.000000 }, - {1740,1740, 0, 0, 40000, 73,0.000000 }, - {1741,1741, 0, 0, 40000, 6,0.000000 }, - {1742,1742, 0, 0, 40000, 6,0.000000 }, - {1743,1743, 0, 0, 40000, 20,0.000000 }, - {1744,1744, 0, 0, 993, 993,0.000000 }, - {1745,1745, 0, 0, 40000, 20,0.000000 }, - {1746,1746, 0, 0, 40000, 0,0.000000 }, - {1747,1747, 0, 0, 40000, 0,0.000000 }, - {1748,1748, 0, 0, 933, 6,0.000000 }, - {1749,1749, 0, 0, 40000, 20,0.000000 }, - {1750,1750, 0, 0, 40000, 6,0.000000 }, - {1751,1751, 0, 0, 40000, 13,0.000000 }, - {1752,1752, 0, 0, 40000, 26,0.000000 }, - {1753,1753, 0, 0, 40000, 0,0.000000 }, - {1754,1754, 0, 0, 1793, 1793,0.000000 }, - {1755,1755, 0, 0, 40000, 106,0.000000 }, - {1756,1756, 0, 0, 40000, 46,0.000000 }, - {1757,1757, 0, 0, 40000, 33,0.000000 }, - {1758,1758, 0, 0, 40000, 286,0.000000 }, - {1759,1759, 0, 0, 40000, 906,0.000000 }, - {1760,1760, 0, 0, 40000, 220,0.000000 }, - {1761,1761, 0, 0, 40000, 53,0.000000 }, - {1762,1762, 0, 0, 40000, 40,0.000000 }, - {1763,1763, 0, 0, 3446, 3446,0.000000 }, - {1764,1764, 0, 0, 860, 13,0.000000 }, - {1765,1765, 0, 0, 513, 513,0.000000 }, - {1766,1766, 0, 0, 1846, 1846,0.000000 }, - {1767,1767, 0, 0, 973, 973,0.000000 }, - {1768,1768, 0, 0, 40000, 366,0.000000 }, - {1769,1769, 0, 0, 1426, 1426,0.000000 }, - {1770,1770, 0, 0, 40000, 686,0.000000 }, - {1771,1771, 0, 0, 1060, 1060,0.000000 }, - {1772,1772, 0, 0, 13, 13,0.000000 }, - {1773,1773, 0, 0, 380, 380,0.000000 }, - {1774,1774, 0, 0, 146, 146,0.000000 }, - {1775,1775, 0, 0, 166, 166,0.000000 }, - {1776,1776, 0, 0, 40000, 6,0.000000 }, - {1777,1777, 0, 0, 40000, 0,0.000000 }, - {1778,1778, 0, 0, 40000, 66,0.000000 }, - {1779,1779, 0, 0, 1473, 1473,0.000000 }, - {1780,1780, 0, 0, 113, 113,0.000000 }, - {1781,1781, 0, 0, 353, 353,0.000000 }, - {1782,1782, 0, 0, 46, 46,0.000000 }, - {1783,1783, 0, 0, 86, 86,0.000000 }, - {1784,1784, 0, 0, 60, 60,0.000000 }, - {1785,1785, 0, 0, 2280, 2280,0.000000 }, - {1786,1786, 0, 0, 366, 366,0.000000 }, - {1787,1787, 0, 0, 1713, 1713,0.000000 }, - {1788,1788, 0, 0, 206, 206,0.000000 }, - {1789,1789, 0, 0, 40000, 6,0.000000 }, - {1790,1790, 0, 0, 40000, 0,0.000000 }, - {1791,1791, 0, 0, 40000, 553,0.000000 }, - {1792,1792, 0, 0, 160, 160,0.000000 }, - {1793,1793, 48, 0, 46, 46,0.000000 }, - {1794,1794, 48, 0, 13, 13,0.000000 }, - {1795,1795, 60, 0, 46, 46,0.000000 }, - {1796,1796, 60, 0, 33, 33,0.000000 }, - {1797,1797, 70, 0, 33, 33,0.000000 }, - {1798,1798, 51, 0, 160, 160,0.000000 }, - {1799,1799, 60, 0, 40, 40,0.000000 }, - {1798,1798, 54, 0, 140, 140,0.000000 }, - {1800,1800, 60, 0, 53, 53,0.000000 }, - {1798,1798, 56, 0, 140, 140,0.000000 }, - {1801,1801, 60, 0, 300, 300,0.000000 }, - {1798,1798, 61, 0, 166, 166,0.000000 }, - {1798,1798, 63, 0, 166, 166,0.000000 }, - {1802,1802, 48, 0, 300, 300,0.000000 }, - {1798,1798, 68, 0, 133, 133,0.000000 }, - {1803,1803, 60, 0, 393, 393,0.000000 }, - {1804,1804, 60, 0, 313, 313,0.000000 }, - {1805,1805, 66, 0, 100, 100,0.000000 }, - {1806,1806, 60, 0, 306, 306,0.000000 }, - { 379, 379, 59, 0, 40, 40,0.000000 }, - {1802,1802, 64, 0, 306, 306,0.000000 }, - {1807,1807, 48, 0, 566, 566,0.000000 }, - {1808,1808, 56, 0, 26, 26,0.000000 }, - {1809,1809, 53, 0, 73, 73,0.000000 }, - {1810,1810, 65, 0, 13, 13,0.000000 }, - {1811,1811, 49, 0, 86, 86,0.000000 }, - {1811,1811, 43, 0, 86, 86,0.000000 }, - { 386, 386, 65, 0, 86, 86,0.000000 }, - { 386, 386, 60, 0, 86, 86,0.000000 }, - {1812,1812, 70, 0, 80, 80,0.000000 }, - {1812,1812, 65, 0, 100, 100,0.000000 }, - {1813,1813, 60, 0, 40, 40,0.000000 }, - {1814,1814, 60, 0, 33, 33,0.000000 }, - {1815,1815, 56, 0, 93, 93,0.000000 }, - {1816,1816, 53, 0, 280, 280,0.000000 }, - {1817,1817, 60, 0, 60, 60,0.000000 }, - {1818,1818, 48, 0, 180, 180,0.000000 }, - {1819,1819, 69, 0, 20, 20,0.000000 }, - { 328, 328, 67, 0, 20, 20,0.000000 }, - { 328, 328, 62, 0, 20, 20,0.000000 }, - {1820,1820, 65, 0, 220, 220,0.000000 }, - {1821,1821, 60, 0, 220, 220,0.000000 }, - {1822,1822, 63, 0, 26, 26,0.000000 }, - {1823,1823, 63, 0, 606, 606,0.000000 }, - {1824,1824, 67, 0, 86, 86,0.000000 }, - {1825,1825, 60, 0, 300, 300,0.000000 }, - {1825,1825, 72, 0, 273, 273,0.000000 }, - { 401, 401, 62, 0, 6, 6,0.000000 }, - {1826,1826, 48, 0, 13, 13,0.000000 }, - {1827,1827, 53, 0, 213, 213,0.000000 }, - {1828,1828, 60, 0, 93, 93,0.000000 }, - {1829,1829, 60, 0, 40, 40,0.000000 }, - {1830,1830, 60, 0, 20, 20,0.000000 }, - {1831,1831, 60, 0, 46, 46,0.000000 }, - {1832,1832, 0, 0, 126, 0,0.000000 }, - {1833,1833, 0, 0, 1240, 1240,0.000000 }, - {1834,1834, 0, 0, 433, 433,0.000000 }, - {1835,1835, 0, 0, 40000, 0,0.000000 }, - {1836,1836, 0, 0, 40000, 153,0.000000 }, - {1837,1837, 0, 0, 146, 106,0.000000 }, - {1838,1838, 0, 0, 126, 126,0.000000 }, - {1839,1839, 0, 0, 1740, 1740,0.000000 }, - {1840,1840, 0, 0, 440, 13,0.000000 }, - {1841,1841, 0, 0, 546, 546,0.000000 }, - {1842,1842, 0, 0, 1420, 1420,0.000000 }, - { 525, 525, 0, 0, 1220, 1220,0.000000 }, - {1843,1843, 0, 0, 40000, 140,0.000000 }, - {1844,1844, 0, 0, 3873, 3873,0.000000 }, - {1845,1845, 0, 0, 546, 546,0.000000 }, - {1846,1846, 0, 0, 333, 333,0.000000 }, - {1847,1847, 0, 0, 4933, 4933,0.000000 }, - {1848,1848, 0, 0, 126, 0,0.000000 }, - {1849,1849, 0, 0, 380, 380,0.000000 }, - {1850,1850, 0, 0, 40000, 6,0.000000 }, - {1851,1851, 0, 0, 273, 273,0.000000 }, - {1852,1852, 0, 0, 233, 233,0.000000 }, - {1853,1853, 0, 0, 40000, 233,0.000000 }, - {1854,1854, 0, 0, 1466, 6,0.000000 }, - {1855,1855, 0, 0, 1380, 1380,0.000000 }, - {1856,1856, 0, 0, 40000, 106,0.000000 }, - {1857,1857, 0, 0, 40000, 133,0.000000 }, - {1858,1858, 0, 0, 913, 913,0.000000 }, - {1859,1859, 0, 0, 60, 60,0.000000 }, - {1860,1860, 0, 0, 40000, 6,0.000000 }, - {1861,1861, 0, 0, 866, 866,0.000000 }, - {1862,1862, 0, 0, 4726, 4726,0.000000 }, - {1863,1863, 0, 0, 40, 40,0.000000 }, - {1864,1864, 0, 0, 1793, 1793,0.000000 }, - {1865,1865, 0, 0, 246, 246,0.000000 }, - {1866,1866, 0, 0, 40000, 133,0.000000 }, - {1867,1867, 0, 0, 2006, 2006,0.000000 }, - {1868,1868, 0, 0, 140, 140,0.000000 }, - {1869,1869, 0, 0, 40000, 106,0.000000 }, - {1870,1870, 0, 0, 460, 460,0.000000 }, - {1871,1871, 0, 0, 146, 146,0.000000 }, - {1872,1872, 0, 0, 273, 273,0.000000 }, - {1873,1873, 0, 0, 40000, 46,0.000000 }, - {1874,1874, 41, 0, 120, 120,0.000000 }, - {1875,1875, 70, 0, 13, 13,0.000000 }, - {1876,1876, 60, 0, 40, 40,0.000000 }, - {1877,1877, 80, 0, 13, 13,0.000000 }, - {1878,1878, 84, 0, 20, 20,0.000000 }, - {1879,1879, 72, 0, 133, 133,0.000000 }, - {1880,1880, 84, 0, 266, 266,0.000000 }, - { 128, 128, 70, 0, 13, 13,0.000000 }, - { 132, 132, 60, 0, 13, 13,0.000000 }, - {1881,1882, 0, 0, 40000, 173,0.000000 }, - {1883,1883, 0, 0, 260, 260,0.000000 }, - {1884,1885, 0, 0, 40000, 6,0.000000 }, - {1886,1887, 0, 0, 40000, 20,0.000000 }, - {1888,1889, 0, 0, 40000, 6,0.000000 }, - {1890,1890, 0, 0, 1020, 53,0.000000 }, - {1891,1891, 0, 0, 40000, 100,0.000000 }, - {1892,1892, 0, 0, 786, 340,0.000000 }, - {1893,1893, 0, 0, 40000, 140,0.000000 }, - {1894,1894, 0, 0, 86, 86,0.000000 }, - {1895,1895, 0, 0, 80, 40,0.000000 }, - {1896,1896, 0, 0, 40000, 86,0.000000 }, - {1897,1897, 0, 0, 173, 173,0.000000 }, - {1898,1898, 0, 0, 886, 886,0.000000 }, - {1899,1899, 0, 0, 466, 466,0.000000 }, - {1900,1900, 0, 0, 40000, 106,0.000000 }, - {1901,1901, 0, 0, 40000, 66,0.000000 }, - {1902,1902, 0, 0, 40000, 13,0.000000 }, - {1903,1903, 0, 0, 60, 60,0.000000 }, - {1904,1904, 0, 0, 1226, 1226,0.000000 }, - {1905,1905, 0, 0, 280, 280,0.000000 }, - {1906,1906, 0, 0, 40000, 153,0.000000 }, - {1907,1907, 0, 0, 40000, 173,0.000000 }, - {1908,1908, 0, 0, 40000, 340,0.000000 }, - {1909,1909, 0, 0, 166, 166,0.000000 }, - { 501, 501, 0, 0, 86, 86,0.000000 }, - {1910,1910, 0, 0, 40000, 20,0.000000 }, - {1911,1911, 0, 0, 20, 20,0.000000 }, - {1912,1912, 0, 0, 600, 600,0.000000 }, - {1913,1913, 0, 0, 40000, 2393,0.000000 }, - {1914,1914, 0, 0, 166, 166,0.000000 }, - { 511, 511, 0, 0, 586, 586,0.000000 }, - {1915,1915, 0, 0, 73, 73,0.000000 }, - {1910,1910, 60, 0, 40000, 26,0.000000 }, - { 511, 511, 72, 0, 493, 493,0.000000 }, - {1915,1915, 84, 0, 60, 60,0.000000 }, - {1916,1916, 0, 0, 2080, 2080,0.000000 }, - {1917,1917, 0, 0, 593, 593,0.000000 }, - {1918,1918, 0, 0, 600, 600,0.000000 }, - {1919,1919, 0, 0, 940, 940,0.000000 }, - {1920,1920, 0, 0, 940, 940,0.000000 }, - {1921,1921, 0, 0, 940, 940,0.000000 }, - {1922,1922, 0, 0, 853, 853,0.000000 }, - {1923,1923, 0, 0, 893, 893,0.000000 }, - {1924,1924, 0, 0, 40000, 73,0.000000 }, - {1925,1925, 0, 0, 40000, 33,0.000000 }, - {1926,1926, 0, 0, 40000, 20,0.000000 }, - {1927,1927, 0, 0, 40000, 0,0.000000 }, - {1928,1928, 0, 0, 40000, 193,0.000000 }, - {1929,1929, 0, 0, 40000, 133,0.000000 }, - {1930,1930, 0, 0, 40000, 153,0.000000 }, - {1931,1931, 0, 0, 1146, 1146,0.000000 }, - {1932,1932, 0, 0, 960, 960,0.000000 }, - {1933,1933, 0, 0, 2406, 2406,0.000000 }, - {1934,1934, 0, 0, 2406, 2406,0.000000 }, - {1935,1935, 0, 0, 40000, 0,0.000000 }, - {1936,1936, 0, 0, 40000, 6,0.000000 }, - {1937,1937, 0, 0, 40000, 0,0.000000 }, - {1938,1938, 0, 0, 40000, 0,0.000000 }, - {1939,1939, 0, 0, 40000, 26,0.000000 }, - {1940,1940, 0, 0, 1426, 1426,0.000000 }, - {1941,1941, 0, 0, 40000, 766,0.000000 }, - {1942,1942, 0, 0, 40000, 453,0.000000 }, - {1943,1943, 0, 0, 40000, 486,0.000000 }, - {1944,1944, 0, 0, 40000, 106,0.000000 }, - {1945,1945, 0, 0, 2273, 2273,0.000000 }, - {1946,1946, 0, 0, 40000, 33,0.000000 }, - {1947,1947, 0, 0, 1226, 1226,0.000000 }, - {1948,1948, 0, 0, 40000, 6,0.000000 }, - {1949,1949, 0, 0, 40000, 1966,0.000000 }, - {1950,1950, 0, 0, 1153, 1153,0.000000 }, - {1951,1951, 0, 0, 40000, 6,0.000000 }, - {1952,1952, 0, 0, 1473, 1473,0.000000 }, - {1953,1953, 0, 0, 40000, 20,0.000000 }, - {1954,1954, 0, 0, 466, 33,0.000000 }, - {1955,1955, 0, 0, 833, 833,0.000000 }, - {1956,1956, 0, 0, 40000, 0,0.000000 }, - {1957,1957, 0, 0, 40000, 33,0.000000 }, - {1958,1958, 0, 0, 40000, 126,0.000000 }, - {1959,1959, 0, 0, 40000, 33,0.000000 }, - {1960,1960, 0, 0, 280, 280,0.000000 }, - {1961,1961, 0, 0, 40000, 266,0.000000 }, - {1962,1962, 0, 0, 40000, 46,0.000000 }, - {1963,1963, 0, 0, 40000, 266,0.000000 }, - {1964,1964, 0, 0, 40000, 0,0.000000 }, - {1965,1965, 0, 0, 40000, 113,0.000000 }, - {1966,1966, 0, 0, 800, 800,0.000000 }, - {1967,1967, 0, 0, 1206, 1206,0.000000 }, - {1968,1968, 0, 0, 1080, 1080,0.000000 }, - {1969,1969, 0, 0, 40000, 13,0.000000 }, - {1970,1970, 0, 0, 546, 546,0.000000 }, - {1971,1971, 0, 0, 40000, 6,0.000000 }, - {1972,1972, 0, 0, 40000, 20,0.000000 }, - {1973,1973, 0, 0, 1233, 20,0.000000 }, - {1974,1974, 0, 0, 40000, 0,0.000000 }, - {1975,1975, 0, 0, 40000, 0,0.000000 }, - {1976,1976, 0, 0, 466, 6,0.000000 }, - {1977,1977, 0, 0, 206, 206,0.000000 }, - {1978,1978, 0, 0, 40000, 6,0.000000 }, - {1979,1979, 0, 0, 40000, 6,0.000000 }, - {1980,1980, 0, 0, 40000, 6,0.000000 }, - {1981,1981, 0, 0, 40000, 6,0.000000 }, - {1982,1982, 0, 0, 40000, 0,0.000000 }, - {1983,1983, 0, 0, 40000, 0,0.000000 }, - {1984,1984, 0, 0, 40000, 0,0.000000 }, - {1985,1985, 0, 0, 40000, 180,0.000000 }, - {1986,1986, 0, 0, 40000, 53,0.000000 }, - {1987,1987, 0, 0, 40000, 53,0.000000 }, - {1988,1988, 0, 0, 40000, 6,0.000000 }, - {1989,1989, 0, 0, 40000, 6,0.000000 }, - {1990,1990, 0, 0, 100, 100,0.000000 }, - {1991,1991, 0, 0, 40000, 73,0.000000 }, - {1992,1992, 0, 0, 60, 60,0.000000 }, - {1993,1993, 0, 0, 40000, 0,0.000000 }, - {1994,1994, 0, 0, 40000, 0,0.000000 }, - {1995,1995, 0, 0, 40000, 0,0.000000 }, - {1996,1996, 0, 0, 40000, 6,0.000000 }, - {1997,1997, 0, 0, 40000, 0,0.000000 }, - {1998,1998, 0, 0, 40000, 0,0.000000 }, - {1999,1999, 0, 0, 40000, 0,0.000000 }, - {2000,2000, 0, 0, 40000, 233,0.000000 }, - {2001,2001, 0, 0, 40000, 233,0.000000 }, - {2002,2002, 0, 0, 620, 620,0.000000 }, - {2003,2003, 0, 0, 1740, 1740,0.000000 }, - {2004,2004, 0, 0, 1153, 1153,0.000000 }, - {2005,2005, 0, 0, 920, 920,0.000000 }, - {2006,2006, 0, 0, 153, 153,0.000000 }, - {2007,2007, 0, 0, 526, 526,0.000000 }, - {2008,2008, 0, 0, 40000, 6,0.000000 }, - {2009,2009, 0, 0, 40000, 40,0.000000 }, - {2010,2010, 0, 0, 40000, 53,0.000000 }, - {2011,2011, 0, 0, 40000, 160,0.000000 }, - {2012,2012, 0, 0, 40000, 6,0.000000 }, - {2013,2013, 0, 0, 1046, 1046,0.000000 }, - {2014,2014, 0, 0, 513, 513,0.000000 }, - {2015,2015, 0, 0, 40, 40,0.000000 }, - {2016,2016, 0, 0, 40, 40,0.000000 }, - {2017,2017, 0, 0, 93, 93,0.000000 }, - {2018,2018, 0, 0, 66, 66,0.000000 }, - {2019,2019, 0, 0, 13, 13,0.000000 }, - {2020,2020, 0, 0, 300, 300,0.000000 }, - {2021,2021, 0, 0, 40, 40,0.000000 }, - {2022,2022, 0, 0, 586, 586,0.000000 }, - {2023,2023, 0, 0, 260, 260,0.000000 }, - {2024,2024, 0, 0, 2406, 2406,0.000000 }, - {2025,2025, 0, 0, 126, 126,0.000000 }, - {2026,2026, 0, 0, 1220, 1220,0.000000 }, - {2027,2027, 0, 0, 386, 386,0.000000 }, - { 352, 352, 51, 0, 40000, 0,0.000000 }, - {2028,2028, 35, 0, 106, 106,0.000000 }, - {2028,2028, 36, 0, 93, 93,0.000000 }, - {2029,2029, 47, 0, 13, 13,0.000000 }, - {2030,2030, 38, 0, 106, 106,0.000000 }, - {2019,2019, 39, 0, 20, 20,0.000000 }, - {2031,2031, 45, 0, 80, 80,0.000000 }, - { 492, 492, 41, 0, 26, 26,0.000000 }, - {2032,2032, 42, 0, 33, 33,0.000000 }, - {2033,2033, 44, 0, 160, 160,0.000000 }, - { 492, 492, 48, 0, 40, 40,0.000000 }, - {2034,2034, 46, 0, 600, 600,0.000000 }, - { 492, 492, 53, 0, 26, 26,0.000000 }, - { 167, 167, 56, 0, 160, 160,0.000000 }, - {2035,2035, 61, 0, 93, 93,0.000000 }, - {2036,2036, 56, 0, 353, 353,0.000000 }, - {2037,2037, 60, 0, 40, 40,0.000000 }, - { 144, 144, 59, 0, 40, 40,0.000000 }, - {2038,2038, 59, 0, 13, 13,0.000000 }, - { 169, 169, 51, 0, 40, 40,0.000000 }, - { 169, 169, 45, 0, 40, 40,0.000000 }, - {2039,2039, 72, 0, 126, 126,0.000000 }, - {2040,2040, 60, 0, 153, 153,0.000000 }, - {2041,2041, 58, 0, 40, 40,0.000000 }, - {2042,2042, 53, 0, 40, 40,0.000000 }, - {2043,2043, 73, 0, 26, 26,0.000000 }, - { 158, 158, 75, 0, 20, 20,0.000000 }, - {2044,2044, 0, 0, 1226, 1226,0.000000 }, - {2045,2045, 0, 0, 493, 493,0.000000 }, - {2046,2046, 0, 0, 620, 620,0.000000 }, - {2047,2047, 0, 0, 280, 280,0.000000 }, - {2048,2048, 0, 0, 260, 260,0.000000 }, - {2049,2049, 0, 0, 40000, 1093,0.000000 }, - {2050,2050, 0, 0, 40000, 233,0.000000 }, - {2051,2051, 0, 0, 120, 120,0.000000 }, - {2052,2052, 0, 0, 40000, 926,0.000000 }, - {2053,2053, 0, 0, 40000, 926,0.000000 }, - {2054,2054, 0, 0, 40000, 233,0.000000 }, - {2055,2055, 0, 0, 40000, 280,0.000000 }, - {2056,2056, 0, 0, 40000, 6,0.000000 }, - {2057,2057, 0, 0, 40000, 153,0.000000 }, - {2058,2058, 0, 0, 286, 286,0.000000 }, - {2059,2059, 0, 0, 286, 286,0.000000 }, - {2060,2060, 0, 0, 40000, 0,0.000000 }, - {2061,2061, 0, 0, 680, 680,0.000000 }, - {2062,2062, 0, 0, 280, 280,0.000000 }, - {2063,2063, 0, 0, 40000, 200,0.000000 }, - {2064,2064, 0, 0, 40, 40,0.000000 }, - {2065,2065, 0, 0, 1580, 1580,0.000000 }, - {2066,2066, 0, 0, 140, 133,0.000000 }, - {2067,2067, 0, 0, 40000, 213,0.000000 }, - {2068,2068, 0, 0, 613, 613,0.000000 }, - {2069,2069, 0, 0, 293, 293,0.000000 }, - {2070,2070, 0, 0, 160, 160,0.000000 }, - {2071,2071, 0, 0, 146, 13,0.000000 }, - {2072,2072, 0, 0, 40000, 6,0.000000 }, - {2073,2073, 0, 0, 40000, 133,0.000000 }, - {2074,2074, 0, 0, 40000, 140,0.000000 }, - {2075,2075, 0, 0, 40000, 66,0.000000 }, - {2076,2076, 0, 0, 40000, 40,0.000000 }, - {2077,2077, 0, 0, 40000, 153,0.000000 }, - {2078,2078, 0, 0, 593, 593,0.000000 }, - {2079,2079, 0, 0, 40000, 40,0.000000 }, - {2080,2080, 0, 0, 4480, 4480,0.000000 }, - {2081,2081, 0, 0, 3600, 313,0.000000 }, - {2082,2082, 0, 0, 40000, 413,0.000000 }, - {2083,2083, 0, 0, 40000, 566,0.000000 }, - {2084,2084, 0, 0, 8420, 80,0.000000 }, - {2085,2085, 0, 0, 40000, 926,0.000000 }, - {2086,2086, 0, 0, 40000, 853,0.000000 }, - {2087,2087, 0, 0, 40000, 853,0.000000 }, - {2088,2088, 0, 0, 40000, 853,0.000000 }, - {2089,2089, 0, 0, 40000, 873,0.000000 }, - {2090,2090, 0, 0, 40000, 1780,0.000000 }, - {2091,2091, 0, 0, 40000, 1673,0.000000 }, - {2092,2092, 0, 0, 4026, 4026,0.000000 }, - {2093,2093, 0, 0, 1773, 153,0.000000 }, - {2094,2094, 0, 0, 1446, 1446,0.000000 }, - {2095,2095, 0, 0, 633, 633,0.000000 }, - {2096,2096, 0, 0, 333, 333,0.000000 }, - {2097,2097, 0, 0, 193, 193,0.000000 }, - {2098,2098, 0, 0, 86, 86,0.000000 }, - {2099,2099, 0, 0, 246, 246,0.000000 }, - {2100,2100, 0, 0, 40000, 6,0.000000 }, - {2101,2101, 0, 0, 40000, 6,0.000000 }, - {2102,2102, 0, 0, 313, 313,0.000000 }, - {2103,2103, 0, 0, 160, 160,0.000000 }, - {2104,2104, 0, 0, 46, 46,0.000000 }, - {2105,2105, 0, 0, 100, 100,0.000000 }, - {2106,2106, 0, 0, 2313, 2313,0.000000 }, - {2107,2107, 0, 0, 306, 306,0.000000 }, - {2108,2108, 0, 0, 40000, 2180,0.000000 }, - {2109,2109, 0, 0, 40000, 133,0.000000 }, - {2110,2110, 0, 0, 40000, 126,0.000000 }, - {2111,2111, 0, 0, 40000, 146,0.000000 }, - { 752, 752, 60, 0, 40, 40,0.000000 }, - { 755, 755, 12, 0, 160, 160,0.000000 }, - {2112,2112, 89, 0, 13, 13,0.000000 }, - {2113,2113, 89, 0, 206, 206,0.000000 }, - { 755, 755, 14, 0, 160, 160,0.000000 }, - { 755, 755, 16, 0, 160, 160,0.000000 }, - {2114,2114, 84, 0, 513, 513,0.000000 }, - { 755, 755, 19, 0, 160, 160,0.000000 }, - {2115,2115, 38, 0, 60, 60,0.000000 }, - {2116,2116, 36, 0, 20, 20,0.000000 }, - { 755, 755, 28, 0, 193, 193,0.000000 }, - { 755, 755, 26, 0, 200, 200,0.000000 }, - { 755, 755, 35, 0, 200, 200,0.000000 }, - { 755, 755, 30, 0, 193, 193,0.000000 }, - {2117,2117, 60, 0, 73, 73,0.000000 }, - {2104,2104, 60, 0, 40, 40,0.000000 }, - {2104,2104, 55, 0, 46, 46,0.000000 }, - { 730, 730, 94, 0, 626, 626,0.000000 }, - {2118,2118, 0, 0, 280, 280,0.000000 }, - {2119,2119, 0, 0, 1446, 1446,0.000000 }, - {2120,2120, 0, 0, 40000, 53,0.000000 }, - {2121,2121, 0, 0, 40000, 20,0.000000 }, - {2122,2122, 0, 0, 40000, 20,0.000000 }, - {2123,2123, 0, 0, 3106, 3106,0.000000 }, - {2124,2124, 0, 0, 40, 66,0.000000 }, - {2125,2125, 0, 0, 40000, 106,0.000000 }, - {2126,2126, 0, 0, 40000, 6,0.000000 }, - {2127,2127, 0, 0, 40000, 13,0.000000 }, - {2128,2128, 0, 0, 40000, 6,0.000000 }, - {2129,2129, 0, 0, 40000, 6,0.000000 }, - {2130,2130, 0, 0, 40000, 6,0.000000 }, - {2131,2131, 0, 0, 40000, 13,0.000000 }, - {2132,2132, 0, 0, 40000, 6,0.000000 }, - {2133,2133, 0, 0, 2060, 13,0.000000 }, - {2134,2134, 0, 0, 40000, 53,0.000000 }, - {2135,2135, 0, 0, 2886, 2886,0.000000 }, - {2136,2136, 0, 0, 40000, 126,0.000000 }, - {2137,2137, 0, 0, 5280, 100,0.000000 }, - {2138,2138, 0, 0, 140, 140,0.000000 }, - {2139,2139, 0, 0, 4553, 20,0.000000 }, - {2140,2140, 0, 0, 513, 513,0.000000 }, - {2141,2141, 60, 0, 40, 40,0.000000 }, - {2141,2141, 44, 0, 40, 40,0.000000 }, - {2142,2142, 47, 0, 40, 40,0.000000 }, - {2143,2143, 47, 0, 60, 60,0.000000 }, - {2144,2144, 62, 0, 606, 606,0.000000 }, - {2145,2145, 93, 0, 226, 226,0.000000 }, - {2146,2146, 50, 0, 80, 80,0.000000 }, - {2145,2145, 40, 0, 420, 420,0.000000 }, - {2147,2147, 60, 0, 20, 20,0.000000 }, - { 898, 898, 60, 0, 46, 46,0.000000 }, - {2147,2147, 57, 0, 20, 20,0.000000 }, - { 900, 900, 42, 0, 193, 193,0.000000 }, - { 900, 900, 38, 0, 193, 193,0.000000 }, - { 908, 908, 88, 0, 33, 33,0.000000 }, - {2148,2148, 0, 0, 1553, 1553,0.000000 }, - {2149,2149, 0, 0, 40000, 6,0.000000 }, - {2150,2150, 0, 0, 993, 993,0.000000 }, - {2151,2151, 0, 0, 40000, 26,0.000000 }, - {2152,2152, 0, 0, 29720, 80,0.000000 }, - {2153,2153, 0, 0, 40000, 306,0.000000 }, - {2154,2154, 0, 0, 700, 700,0.000000 }, - {2155,2155, 0, 0, 880, 880,0.000000 }, - {2156,2156, 0, 0, 40000, 6,0.000000 }, - {2157,2157, 0, 0, 1046, 1046,0.000000 }, - {2158,2158, 0, 0, 100, 100,0.000000 }, - {2159,2159, 0, 0, 40, 40,0.000000 }, - { 136, 136, 0, 0, 640, 640,0.000000 }, - { 168, 168, 0, 0, 40, 40,0.000000 }, - { 164, 164, 0, 0, 1220, 1220,0.000000 }, - { 167, 167, 0, 0, 160, 160,0.000000 }, - {2160,2160, 65, 0, 40, 40,0.000000 }, - {2161,2161, 21, 0, 200, 200,0.000000 }, - {2162, 173, 0, 0, 1513, 1513,0.000000 }, - {2163,2164, 0, 0, 653, 653,0.000000 }, - {2165,2166, 0, 0, 633, 633,0.000000 }, - {2167,2168, 0, 0, 913, 913,0.000000 }, - {2169,2170, 0, 0, 300, 300,0.000000 }, - {2171,2172, 0, 0, 1773, 1773,0.000000 }, - {2173,2174, 0, 0, 40000, 306,0.000000 }, - {2175,2174, 0, 0, 40000, 433,0.000000 }, - {2176, 299, 0, 0, 40000, 66,0.000000 }, - {2177,2178, 0, 0, 40000, 246,0.000000 }, - {2179,2180, 0, 0, 40000, 300,0.000000 }, - {2181,2182, 0, 0, 40000, 233,0.000000 }, - {2183,2184, 0, 0, 1740, 1740,0.000000 }, - { 127, 127, 65, 0, 66, 66,0.000000 }, - { 127, 127, 72, 0, 46, 46,0.000000 }, - { 364, 365, 52, 0, 20, 20,0.000000 }, - {2185,2186, 60, 0, 80, 80,0.000000 }, - {1550,1551, 47, 0, 100, 100,0.000000 }, - {1556,1557, 76, 0, 253, 253,0.000000 }, - { 374, 375, 84, 0, 40000, 0,0.000000 }, - {1564,1565, 83, 0, 73, 73,0.000000 }, - {1568,1569, 24, 0, 40, 40,0.000000 }, - {1556,1557, 77, 0, 253, 253,0.000000 }, - {1572,1573, 60, 0, 80, 80,0.000000 }, - {1574,1575, 65, 0, 46, 46,0.000000 }, - { 391, 392, 44, 0, 80, 80,0.000000 }, - { 391, 393, 40, 0, 40, 40,0.000000 }, - {1606,1607, 72, 0, 33, 33,0.000000 }, - { 398, 399, 73, 0, 400, 400,0.000000 }, - {1608,1609, 70, 0, 353, 353,0.000000 }, - {2187,2187, 0, 0, 40000, 140,0.000000 }, - {2188,2188, 0, 0, 40000, 153,0.000000 }, - {2189,2189, 0, 0, 453, 453,0.000000 }, - {2190,2190, 0, 0, 1300, 1300,0.000000 }, - {2191,2191, 0, 0, 46, 46,0.000000 }, - {2192,2192, 0, 0, 40000, 186,0.000000 }, - {2193,2193, 0, 0, 80, 80,0.000000 }, - {2194,2194, 0, 0, 40000, 6,0.000000 }, - {2195,2195, 0, 0, 186, 186,0.000000 }, - {2196,2196, 0, 0, 40000, 0,0.000000 }, - {2197,2197, 0, 0, 40000, 0,0.000000 }, - {2198,2198, 0, 0, 166, 0,0.000000 }, - {2199,2199, 0, 0, 40, 40,0.000000 }, - {2200,2200, 0, 0, 1740, 1740,0.000000 }, - { 528, 528, 0, 0, 140, 140,0.000000 }, - {2201,2201, 0, 0, 40000, 53,0.000000 }, - {2202,2202, 0, 0, 40, 40,0.000000 }, - {2203,2203, 0, 0, 80, 80,0.000000 }, - {2204,2204, 41, 0, 46, 46,0.000000 }, - {2205,2205, 84, 0, 40, 40,0.000000 }, - {2206,2206, 72, 0, 133, 133,0.000000 }, - { 741, 741, 48, 0, 133, 133,0.000000 }, - {2207,2207, 0, 0, 40, 40,0.000000 }, - {2208,2208, 0, 0, 940, 940,0.000000 }, - {2209,2209, 0, 0, 40000, 213,0.000000 }, - {2210,2210, 0, 0, 866, 866,0.000000 }, - {2211,2211, 0, 0, 40000, 0,0.000000 }, - {2212,2212, 0, 0, 40000, 6,0.000000 }, - {2213,2213, 0, 0, 1080, 1080,0.000000 }, - {2214,2214, 0, 0, 40000, 0,0.000000 }, - {2215,2215, 0, 0, 40000, 73,0.000000 }, - {2216,2216, 0, 0, 40000, 26,0.000000 }, - {2217,2217, 0, 0, 2360, 13,0.000000 }, - {2218,2218, 0, 0, 40000, 0,0.000000 }, - {2219,2219, 0, 0, 3126, 13,0.000000 }, - {2220,2220, 0, 0, 40000, 140,0.000000 }, - {2221,2221, 0, 0, 40000, 26,0.000000 }, - {2222,2222, 0, 0, 40000, 140,0.000000 }, - {2223,2223, 0, 0, 40000, 0,0.000000 }, - {2224,2224, 0, 0, 126, 0,0.000000 }, - {2225,2225, 0, 0, 40000, 6,0.000000 }, - {2226,2226, 0, 0, 40000, 33,0.000000 }, - {2227,2227, 0, 0, 40000, 6,0.000000 }, - {2228,2228, 0, 0, 40000, 6,0.000000 }, - {2229,2229, 0, 0, 40000, 0,0.000000 }, - {2230,2230, 0, 0, 40000, 6,0.000000 }, - {2231,2231, 0, 0, 40000, 20,0.000000 }, - {2232,2232, 0, 0, 40000, 6,0.000000 }, - {2233,2233, 0, 0, 40000, 26,0.000000 }, - {2234,2234, 0, 0, 40000, 266,0.000000 }, - {2235,2235, 0, 0, 40000, 40,0.000000 }, - {2236,2236, 0, 0, 3500, 3500,0.000000 }, - {2237,2237, 0, 0, 860, 20,0.000000 }, - {2238,2238, 0, 0, 513, 513,0.000000 }, - {2239,2239, 0, 0, 973, 973,0.000000 }, - {2240,2240, 0, 0, 1446, 1446,0.000000 }, - {2241,2241, 0, 0, 2280, 2280,0.000000 }, - {2242,2242, 0, 0, 1713, 1713,0.000000 }, - {2243,2243, 0, 0, 200, 200,0.000000 }, - {2244,2244, 0, 0, 40000, 6,0.000000 }, - {2245,2245, 0, 0, 40000, 0,0.000000 }, - {2246,2246, 0, 0, 140, 140,0.000000 }, - {2247,2247, 60, 0, 46, 46,0.000000 }, - {2248,2248, 60, 0, 306, 306,0.000000 }, - {2249,2249, 48, 0, 300, 300,0.000000 }, - {2250,2250, 60, 0, 360, 360,0.000000 }, - {2251,2251, 60, 0, 306, 306,0.000000 }, - {2252,2252, 66, 0, 93, 93,0.000000 }, - {2253,2253, 60, 0, 313, 313,0.000000 }, - {2249,2249, 64, 0, 300, 300,0.000000 }, - {2254,2254, 60, 0, 40, 40,0.000000 }, - {2255,2255, 60, 0, 26, 26,0.000000 }, - {2256,2256, 56, 0, 106, 106,0.000000 }, - {2257,2257, 53, 0, 313, 313,0.000000 }, - {2258,2258, 60, 0, 60, 60,0.000000 }, - {2259,2259, 48, 0, 180, 180,0.000000 }, - {2260,2260, 67, 0, 80, 80,0.000000 }, - {2261,2261, 60, 0, 320, 320,0.000000 }, - {2261,2261, 72, 0, 246, 246,0.000000 }, - {2262,2262, 60, 0, 40, 40,0.000000 }, - {2263,2263, 0, 0, 593, 593,0.000000 }, - {2264,2264, 24, 0, 13, 13,0.000000 }, - {2265,2265, 36, 0, 26, 26,0.000000 }, - { 343, 343, 36, 0, 26, 26,0.000000 }, - { 347, 347, 0, 0, 66, 66,0.000000 }, - { 347, 347, 12, 0, 53, 53,0.000000 }, - {2266,2266, 12, 0, 60, 60,0.000000 }, - {2267,2267, 24, 0, 20, 20,0.000000 }, - {2267,2267, 36, 0, 13, 13,0.000000 }, - {2268,2268, 0, 0, 66, 66,0.000000 }, - {2266,2266, 24, 0, 20, 20,0.000000 }, - {2269,2269, 88, 0, 920, 920,0.000000 }, - {2270,2270, 88, 0, 486, 486,0.000000 }, - {2271,2271, 13, 0, 100, 100,0.000000 }, - { 351, 351, 0, 0, 73, 73,0.000000 }, - {2271,2271, 15, 0, 113, 113,0.000000 }, - {2272,2272, 0, 0, 1226, 1226,0.000000 }, - {2273,2273, 0, 0, 2193, 2193,0.000000 }, - {2274,2274, 0, 0, 866, 866,0.000000 }, - {2275,2275, 0, 0, 886, 886,0.000000 }, - {2276,2276, 0, 0, 1900, 1900,0.000000 }, - {2277,2277, 0, 0, 266, 266,0.000000 }, - {2278,2278, 0, 0, 313, 313,0.000000 }, - {2279,2279, 0, 0, 40000, 1833,0.000000 }, - {2280,2280, 0, 0, 126, 126,0.000000 }, - {2281,2281, 0, 0, 4580, 4580,0.000000 }, - {2282,2282, 0, 0, 253, 253,0.000000 }, - {2283,2283, 0, 0, 120, 120,0.000000 }, - {2284,2284, 0, 0, 40000, 46,0.000000 }, - {2285,2285, 0, 0, 2320, 13,0.000000 }, - {2286,2286, 0, 0, 80, 60,0.000000 }, - {2287,2287, 0, 0, 400, 400,0.000000 }, - {2288,2288, 0, 0, 1953, 1953,0.000000 }, - {2289,2289, 0, 0, 1446, 1446,0.000000 }, - {2290,2290, 0, 0, 360, 360,0.000000 }, - {2291,2291, 0, 0, 40000, 6,0.000000 }, - {2292,2292, 0, 0, 40000, 6,0.000000 }, - {2293,2293, 0, 0, 2026, 2026,0.000000 }, - {2294,2294, 0, 0, 246, 13,0.000000 }, - {2295,2295, 0, 0, 233, 233,0.000000 }, - {2296,2296, 0, 0, 2600, 1333,0.000000 }, - {2297,2297, 79, 0, 20, 20,0.000000 }, - {2297,2297, 72, 0, 20, 20,0.000000 }, - {2298,2298, 72, 0, 13, 13,0.000000 }, - {2298,2298, 79, 0, 13, 13,0.000000 }, - { 554, 554, 60, 0, 160, 160,0.000000 }, - {2299,2299, 72, 0, 253, 253,0.000000 }, - {2300,2300, 84, 0, 106, 106,0.000000 }, - { 555, 555, 66, 0, 20, 20,0.000000 }, - {2301,2302, 35, 0, 20, 20,0.000000 }, - {2303,2304, 52, 0, 20, 20,0.000000 }, - {2305,1548, 48, 0, 66, 66,0.000000 }, - {1595,1595, 58, 0, 33, 33,0.000000 }, - {2305,1548, 60, 0, 73, 73,0.000000 }, - {2306,2307, 47, 0, 606, 606,0.000000 }, - {2306,2307, 43, 0, 313, 313,0.000000 }, - {2306,2307, 49, 0, 706, 706,0.000000 }, - {2306,2307, 51, 0, 840, 840,0.000000 }, - {2306,2307, 54, 0, 820, 820,0.000000 }, - {2306,2307, 57, 0, 780, 780,0.000000 }, - {2306,2307, 72, 0, 713, 713,0.000000 }, - {2306,2307, 60, 0, 786, 786,0.000000 }, - {2306,2307, 76, 0, 720, 720,0.000000 }, - {2306,2307, 84, 0, 720, 720,0.000000 }, - {2306,2307, 36, 0, 193, 193,0.000000 }, - {1560,2308, 65, 0, 113, 113,0.000000 }, - {2309,2310, 84, 0, 40000, 0,0.000000 }, - {1564,1564, 83, 0, 60, 60,0.000000 }, - { 380, 381, 84, 0, 40000, 0,0.000000 }, - {1568,1568, 24, 0, 40, 40,0.000000 }, - {2306,2307, 77, 0, 713, 713,0.000000 }, - {2311,2312, 60, 0, 86, 86,0.000000 }, - {2313,2314, 65, 0, 160, 160,0.000000 }, - {2315,2315, 59, 0, 13, 13,0.000000 }, - {2316,2316, 51, 0, 40, 40,0.000000 }, - {1612,1612, 45, 0, 46, 46,0.000000 }, - {2317,2317, 71, 0, 133, 133,0.000000 }, - {2318,2318, 60, 0, 153, 153,0.000000 }, - {2319,2319, 58, 0, 40, 40,0.000000 }, - {2320,2320, 53, 0, 40, 40,0.000000 }, - { 397, 397, 64, 0, 86, 86,0.000000 }, - {2321,2321, 71, 0, 13, 13,0.000000 }, - {2322,2322, 61, 0, 326, 326,0.000000 }, - {2323,2323, 61, 0, 640, 640,0.000000 }, - {2324, 392, 44, 0, 80, 80,0.000000 }, - {2324, 393, 40, 0, 40, 40,0.000000 }, - {1595,1595, 69, 0, 20, 20,0.000000 }, - {1595,1595, 68, 0, 20, 20,0.000000 }, - {1595,1595, 63, 0, 33, 33,0.000000 }, - {2325,2326, 74, 0, 200, 200,0.000000 }, - {2327,2328, 60, 0, 340, 340,0.000000 }, - {2329,2330, 80, 0, 113, 113,0.000000 }, - {2331,2332, 64, 0, 500, 500,0.000000 }, - { 397, 397, 72, 0, 73, 73,0.000000 }, - {2333,2334, 78, 0, 806, 806,0.000000 }, - {1608,1609, 82, 0, 353, 353,0.000000 }, - {2315,2315, 48, 0, 13, 13,0.000000 }, - {2316,2316, 53, 0, 40, 40,0.000000 }, - {2335,2335, 0, 0, 546, 546,0.000000 }, - {2336,2337, 0, 0, 546, 546,0.000000 }, - {2338,2339, 0, 0, 40000, 86,0.000000 }, - {2340,2340, 0, 0, 1580, 1580,0.000000 }, - {2341,2341, 0, 0, 1153, 1153,0.000000 }, - {2342,2342, 0, 0, 40000, 220,0.000000 }, - {2343,2343, 0, 0, 40000, 240,0.000000 }, - {2344,2345, 0, 0, 380, 380,0.000000 }, - {2346,2346, 0, 0, 40000, 6,0.000000 }, - {2347,2348, 0, 0, 40000, 20,0.000000 }, - {2349,2350, 0, 0, 40000, 6,0.000000 }, - {2351,2352, 0, 0, 40000, 6,0.000000 }, - {2353,2354, 0, 0, 40000, 20,0.000000 }, - {2355,2356, 0, 0, 40000, 213,0.000000 }, - {2357,2357, 14, 0, 26, 26,0.000000 }, - {2358,2358, 35, 0, 40, 40,0.000000 }, - {2357,2357, 19, 0, 26, 26,0.000000 }, - {2359,2359, 43, 0, 86, 86,0.000000 }, - {2360,2360, 41, 0, 60, 60,0.000000 }, - {2360,2360, 43, 0, 53, 53,0.000000 }, - {2360,2360, 45, 0, 60, 60,0.000000 }, - {2360,2360, 47, 0, 53, 53,0.000000 }, - {2361,2362, 0, 0, 1553, 1553,0.000000 }, - {2363,2363, 0, 0, 1213, 1213,0.000000 }, - {2364,2364, 0, 0, 2280, 2280,0.000000 }, - {2365,2365, 0, 0, 966, 966,0.000000 }, - {2366,2366, 0, 0, 500, 500,0.000000 }, - {2367,2367, 0, 0, 326, 326,0.000000 }, - {2368,2368, 0, 0, 1926, 1926,0.000000 }, - {2369,2369, 0, 0, 520, 520,0.000000 }, - {2370,2370, 0, 0, 993, 993,0.000000 }, - {2371,2371, 0, 0, 1740, 1740,0.000000 }, - {2372,2372, 0, 0, 2300, 80,0.000000 }, - {2373,2373, 0, 0, 513, 513,0.000000 }, - {2374,2374, 0, 0, 40, 40,0.000000 }, - {2375,2375, 0, 0, 1226, 1226,0.000000 }, - {2376,2376, 0, 0, 606, 606,0.000000 }, - {2377,2377, 0, 0, 40000, 6,0.000000 }, - {2378,2378, 0, 0, 993, 6,0.000000 }, - {2379,2379, 0, 0, 40000, 153,0.000000 }, - {2380,2380, 0, 0, 40000, 6,0.000000 }, - {2381,2381, 0, 0, 40000, 0,0.000000 }, - {2382,2382, 0, 0, 40000, 6,0.000000 }, - {2383,2383, 0, 0, 40000, 6,0.000000 }, - {2384,2384, 0, 0, 40000, 6,0.000000 }, - {2385,2385, 0, 0, 273, 273,0.000000 }, - {2386,2386, 0, 0, 1580, 1580,0.000000 }, - {2387,2387, 0, 0, 140, 140,0.000000 }, - { 402, 402, 0, 0, 146, 146,0.000000 }, - {2388,2388, 0, 0, 40000, 446,0.000000 }, - {2389,2389, 0, 0, 2006, 2006,0.000000 }, - {2390,2390, 0, 0, 353, 353,0.000000 }, - {2391,2391, 0, 0, 233, 233,0.000000 }, - {2392,2392, 0, 0, 226, 226,0.000000 }, - {2393,2393, 0, 0, 853, 853,0.000000 }, - {2394,2394, 0, 0, 100, 100,0.000000 }, - {2395,2395, 0, 0, 40000, 40,0.000000 }, - {2396,2397, 0, 0, 633, 20,0.000000 }, - {2398,2398, 0, 0, 40, 40,0.000000 }, - {2399,2400, 0, 0, 493, 493,0.000000 }, - {2401,2401, 0, 0, 40000, 86,0.000000 }, - {2402,2402, 0, 0, 40000, 0,0.000000 }, - {2403,2404, 0, 0, 40000, 66,0.000000 }, - {2405,2406, 0, 0, 186, 46,0.000000 }, - {2407,2408, 0, 0, 40000, 6,0.000000 }, - {2409,2409, 0, 0, 1153, 1153,0.000000 }, - {2410,2410, 0, 0, 40000, 0,0.000000 }, - {2411,2411, 0, 0, 86, 86,0.000000 }, - {2412,2413, 0, 0, 40000, 73,0.000000 }, - {2414,2414, 0, 0, 40000, 40,0.000000 }, - {2415,2416, 0, 0, 40000, 286,0.000000 }, - {2417,2418, 0, 0, 40000, 20,0.000000 }, - {2419,2420, 0, 0, 600, 600,0.000000 }, - {2421,2422, 0, 0, 253, 253,0.000000 }, - {2423,2424, 0, 0, 466, 466,0.000000 }, - {2425,2426, 0, 0, 40000, 6,0.000000 }, - {2427,2427, 33, 0, 53, 53,0.000000 }, - {2428,2429, 38, 0, 46, 46,0.000000 }, - {2430,2430, 38, 0, 13, 13,0.000000 }, - {2431,2431, 38, 0, 86, 86,0.000000 }, - {2432,2432, 40, 0, 6, 6,0.000000 }, - {2433,2434, 41, 0, 60, 60,0.000000 }, - {2435,2435, 0, 0, 20, 20,0.000000 }, - {2435,2435, 41, 0, 26, 26,0.000000 }, - {2360,2360, 48, 0, 53, 53,0.000000 }, - {2436,2436, 17, 0, 446, 446,0.000000 }, - {2360,2360, 50, 0, 46, 46,0.000000 }, - {2435,2435, 45, 0, 20, 20,0.000000 }, - {2437,2437,254, 0, 26, 26,0.000000 }, - {2438,2438, 60, 0, 80, 80,0.000000 }, - {2439,2439, 56, 0, 80, 80,0.000000 }, - {2440,2440, 60, 0, 40, 40,0.000000 }, - {2440,2440, 55, 0, 40, 40,0.000000 }, - {2441,2441, 63, 0, 86, 86,0.000000 }, - {2442,2442, 57, 0, 40, 40,0.000000 }, - {2443,2443, 0, 0, 40000, 106,0.000000 }, - {2444,2444, 0, 0, 246, 246,0.000000 }, - {2445,2445, 0, 0, 3133, 13,0.000000 }, - {2446,2446, 0, 0, 40000, 153,0.000000 }, - {2447,2447, 0, 0, 40000, 480,0.000000 }, - {2448,2448, 0, 0, 40000, 73,0.000000 }, - {2449,2449, 0, 0, 40000, 526,0.000000 }, - {2450,2450, 0, 0, 40000, 146,0.000000 }, - {2451,2451, 0, 0, 4520, 4520,0.000000 }, - {2452,2452, 0, 0, 280, 280,0.000000 }, - { 752, 752, 55, 0, 46, 46,0.000000 }, - {2453,2453, 0, 0, 646, 646,0.000000 }, - {2454,2454, 0, 0, 600, 600,0.000000 }, - {2455,2455, 0, 0, 86, 86,0.000000 }, - {2456,2456, 0, 0, 386, 386,0.000000 }, - {2457,2457, 0, 0, 40000, 0,0.000000 }, - {2458,2458, 0, 0, 1340, 1340,0.000000 }, - {2459,2459, 0, 0, 786, 786,0.000000 }, - {2460,2460, 0, 0, 680, 680,0.000000 }, - {2461,2461, 0, 0, 2113, 2113,0.000000 }, - {2462,2462, 0, 0, 3193, 13,0.000000 }, - {2463,2463, 0, 0, 726, 726,0.000000 }, - { 884, 884, 0, 0, 46, 46,0.000000 }, - { 884, 884, 28, 0, 100, 100,0.000000 }, - {2464,2464, 29, 0, 66, 66,0.000000 }, - { 886, 886, 31, 0, 20, 20,0.000000 }, - { 360, 360, 32, 0, 46, 46,0.000000 }, - { 361, 361, 33, 0, 100, 100,0.000000 }, - {2453,2453, 34, 0, 760, 760,0.000000 }, - { 888, 888, 29, 0, 46, 46,0.000000 }, - { 886, 886, 55, 0, 13, 13,0.000000 }, - { 890, 890, 48, 0, 73, 73,0.000000 }, - { 884, 884, 58, 0, 40, 40,0.000000 }, - {2465,2465, 45, 0, 46, 46,0.000000 }, - {2465,2465, 43, 0, 46, 46,0.000000 }, - {2466,2466, 73, 0, 520, 520,0.000000 }, - {2467,2467, 72, 0, 266, 266,0.000000 }, - {2468,2468, 76, 0, 160, 160,0.000000 }, - {2467,2467, 84, 0, 273, 273,0.000000 }, - {2468,2468, 36, 0, 166, 166,0.000000 }, - {2469,2469, 65, 0, 86, 86,0.000000 }, - {2470,2470, 83, 0, 73, 73,0.000000 }, - {2471,2471, 50, 0, 300, 300,0.000000 }, - {2468,2468, 77, 0, 133, 133,0.000000 }, - { 897, 897, 55, 0, 33, 33,0.000000 }, - {2472,2472, 60, 0, 46, 46,0.000000 }, - { 897, 897, 50, 0, 33, 33,0.000000 }, - {2473,2473, 42, 0, 206, 206,0.000000 }, - {2473,2473, 46, 0, 166, 166,0.000000 }, - {2474,2474, 71, 0, 133, 133,0.000000 }, - {2474,2474, 60, 0, 160, 160,0.000000 }, - {2455,2455, 58, 0, 66, 66,0.000000 }, - {2455,2455, 53, 0, 86, 86,0.000000 }, - {2475,2475, 91, 0, 40, 40,0.000000 }, - {2476,2476, 61, 0, 140, 140,0.000000 }, - {2477,2477, 61, 0, 786, 786,0.000000 }, - {2478,2478, 44, 0, 33, 33,0.000000 }, - {2479,2479, 40, 0, 840, 840,0.000000 }, - {2480,2480, 69, 0, 40, 40,0.000000 }, - { 361, 361, 68, 0, 40, 40,0.000000 }, - { 361, 361, 63, 0, 60, 60,0.000000 }, - {2481,2481, 74, 0, 73, 73,0.000000 }, - {2482,2482, 60, 0, 73, 73,0.000000 }, - { 908, 908, 80, 0, 33, 33,0.000000 }, - {2483,2483, 64, 0, 306, 306,0.000000 }, - {2483,2483, 73, 0, 260, 260,0.000000 }, - {2483,2483, 70, 0, 260, 260,0.000000 }, - { 886, 886, 68, 0, 6, 6,0.000000 }, - { 886, 886, 48, 0, 13, 13,0.000000 }, - {2484,2484, 0, 0, 440, 440,0.000000 }, - {2485,2485, 0, 0, 253, 253,0.000000 }, - {2486,2486, 0, 0, 306, 306,0.000000 }, - {2487,2487, 0, 0, 1213, 1213,0.000000 }, - {2488,2488, 0, 0, 626, 626,0.000000 }, - {2489,2489, 0, 0, 40000, 206,0.000000 }, - {2490,2490, 0, 0, 40000, 100,0.000000 }, - {2491,2491, 0, 0, 40000, 13,0.000000 }, - {2492,2492, 0, 0, 2273, 2273,0.000000 }, - {2493,2493, 0, 0, 1740, 1740,0.000000 }, - {2494,2494, 0, 0, 1953, 1953,0.000000 }, - {2495,2495, 0, 0, 1740, 1740,0.000000 }, - {2496,2496, 0, 0, 386, 386,0.000000 }, - {2497,2497, 0, 0, 233, 233,0.000000 }, - {2498,2498, 0, 0, 40000, 106,0.000000 }, - {2499,2499, 0, 0, 40000, 13,0.000000 }, - {2500,2500, 0, 0, 40000, 0,0.000000 }, - {2501,2501, 0, 0, 40000, 186,0.000000 }, - {2502,2502, 0, 0, 66, 66,0.000000 }, - {2503,2503, 0, 0, 46, 46,0.000000 }, - {2504,2504, 0, 0, 40000, 186,0.000000 }, - {2505,2505, 0, 0, 1106, 1106,0.000000 }, - {2506,2506, 0, 0, 40000, 186,0.000000 }, - {2507,2507, 0, 0, 40000, 100,0.000000 }, - {2508,2508, 0, 0, 40000, 206,0.000000 }, - {2509,2509, 0, 0, 40000, 60,0.000000 }, - {2510,2510, 0, 0, 40000, 106,0.000000 }, - {2511,2511, 0, 0, 60, 60,0.000000 }, - {2512,2512, 0, 0, 40, 40,0.000000 }, - {2513,2513, 0, 0, 1833, 26,0.000000 }, - {2514,2514, 0, 0, 806, 806,0.000000 }, - {2515,2515, 0, 0, 1106, 1106,0.000000 }, - {2516,2516, 0, 0, 40000, 6,0.000000 }, - {2517,2517, 0, 0, 40000, 180,0.000000 }, - {2518,2518, 0, 0, 40000, 340,0.000000 }, - {2519,2519, 0, 0, 1873, 6,0.000000 }, - {2520,2520, 0, 0, 2586, 2586,0.000000 }, - {2521,2521, 0, 0, 1740, 1740,0.000000 }, - {2522,2522, 0, 0, 3493, 13,0.000000 }, - {2523,2523, 0, 0, 40000, 266,0.000000 }, - {2524,2524, 0, 0, 6160, 33,0.000000 }, - {2525,2525, 0, 0, 40000, 46,0.000000 }, - {2526,2526, 0, 0, 1140, 1140,0.000000 }, - { 346, 346, 30, 0, 46, 46,0.000000 }, - { 346, 346, 31, 0, 33, 33,0.000000 }, - { 346, 346, 32, 0, 40, 40,0.000000 }, - { 346, 346, 33, 0, 46, 46,0.000000 }, - { 346, 346, 34, 0, 53, 53,0.000000 }, - { 346, 346, 35, 0, 33, 33,0.000000 }, - { 346, 346, 37, 0, 146, 146,0.000000 }, - { 346, 346, 39, 0, 186, 186,0.000000 }, - { 346, 346, 41, 0, 206, 206,0.000000 }, - { 346, 346, 43, 0, 133, 133,0.000000 }, - { 346, 346, 45, 0, 126, 126,0.000000 }, - { 346, 346, 47, 0, 153, 153,0.000000 }, - { 346, 346, 48, 0, 153, 153,0.000000 }, - { 346, 346, 49, 0, 153, 153,0.000000 }, - { 512, 512, 84, 0, 73, 73,0.000000 }, - {2206,2206, 84, 0, 140, 140,0.000000 }, - {2527,2527, 55, 0, 13, 13,0.000000 }, - {2528,2528, 36, 0, 20, 20,0.000000 }, - {2529,2529, 38, 0, 66, 66,0.000000 }, - {2530,2530, 60, 0, 86, 86,0.000000 }, - {2531,2531, 38, 0, 60, 60,0.000000 }, - {2532,2532, 17, 0, 2233, 2233,0.000000 }, - {2532,2532, 18, 0, 2266, 2266,0.000000 }, - {2532,2532, 19, 0, 2280, 2280,0.000000 }, - {2532,2532, 20, 0, 46, 46,0.000000 }, - {2532,2532, 21, 0, 46, 46,0.000000 }, - {2532,2532, 22, 0, 40, 40,0.000000 }, - {2532,2532, 23, 0, 53, 53,0.000000 }, - {2532,2532, 24, 0, 100, 100,0.000000 }, - {2532,2532, 25, 0, 46, 46,0.000000 }, - {2532,2532, 26, 0, 60, 60,0.000000 }, - {2532,2532, 27, 0, 40, 40,0.000000 }, - {2532,2532, 28, 0, 53, 53,0.000000 }, - {2532,2532, 29, 0, 126, 126,0.000000 }, - {2533,2533, 84, 0, 133, 133,0.000000 }, - {2534,2534, 48, 0, 86, 86,0.000000 }, - {2535,2535, 65, 0, 486, 486,0.000000 }, - {2536,2536, 65, 0, 526, 526,0.000000 }, - {2537,2537, 55, 0, 66, 66,0.000000 }, - {2537,2537, 41, 0, 60, 60,0.000000 }, - { 346, 346, 63, 0, 106, 106,0.000000 }, - { 346, 346, 55, 0, 106, 106,0.000000 }, - {2538,2538, 55, 0, 1200, 1200,0.000000 }, - {2538,2538, 53, 0, 1206, 1206,0.000000 }, - {2534,2534, 50, 0, 86, 86,0.000000 }, - { 506, 506, 84, 0, 133, 133,0.000000 }, - { 506, 506, 74, 0, 133, 133,0.000000 }, - { 504, 504, 84, 0, 473, 473,0.000000 }, - { 504, 504, 74, 0, 486, 486,0.000000 }, - {2539,2539, 84, 0, 133, 133,0.000000 }, - {2540,2540, 74, 0, 20, 20,0.000000 }, - {1911,1911, 48, 0, 46, 46,0.000000 }, - {1911,1911, 36, 0, 20, 20,0.000000 }, - {2541,2541, 74, 0, 133, 133,0.000000 }, - {2542,2542, 0, 0, 613, 613,0.000000 }, - {2543,2543, 0, 0, 40000, 480,0.000000 }, - {2544,2544, 0, 0, 1133, 1133,0.000000 }, - {2545,2545, 0, 0, 1200, 1200,0.000000 }, - {2546,2547, 0, 0, 1313, 1313,0.000000 }, - {2548,2549, 0, 0, 40000, 6,0.000000 }, - {2550,2550, 0, 0, 1740, 1740,0.000000 }, - {2551,2552, 0, 0, 860, 860,0.000000 }, - {2553,2553, 0, 0, 40000, 0,0.000000 }, - {2554,2554, 0, 0, 2300, 2300,0.000000 }, - {2555,2556, 0, 0, 40000, 0,0.000000 }, - {2557,2557, 0, 0, 506, 13,0.000000 }, - {2558,1467, 0, 0, 40000, 6,0.000000 }, - {2559,2560, 0, 0, 40000, 0,0.000000 }, - {2561,2561, 0, 0, 40000, 106,0.000000 }, - {2562,2562, 0, 0, 1013, 1013,0.000000 }, - {2563,2564, 0, 0, 1020, 1020,0.000000 }, - {2565,2565, 0, 0, 1746, 1746,0.000000 }, - {2566,2567, 0, 0, 1446, 1446,0.000000 }, - {2568,2568, 0, 0, 826, 826,0.000000 }, - {2569,2569, 0, 0, 2293, 2293,0.000000 }, - {2570,2570, 0, 0, 2386, 2386,0.000000 }, - {2571,2572, 0, 0, 40000, 46,0.000000 }, - {2573,2574, 0, 0, 40000, 26,0.000000 }, - {2575,2575, 0, 0, 40000, 126,0.000000 }, - { 229,2576, 0, 0, 1893, 13,0.000000 }, - {2577,2577, 0, 0, 1046, 1046,0.000000 }, - { 239,2578, 0, 0, 40000, 53,0.000000 }, - {2579,2579, 0, 0, 40000, 13,0.000000 }, - {2580,2580, 0, 0, 40000, 66,0.000000 }, - {2581,2582, 0, 0, 3466, 3466,0.000000 }, - {2583,2584, 0, 0, 233, 233,0.000000 }, - {2585,2586, 0, 0, 40000, 0,0.000000 }, - {2587,2588, 0, 0, 40000, 0,0.000000 }, - {2589,2589, 0, 0, 40000, 6,0.000000 }, - {2590,2590, 0, 0, 40000, 6,0.000000 }, - {2591,2592, 0, 0, 40000, 0,0.000000 }, - {2593,2594, 0, 0, 680, 0,0.000000 }, - {2595,2595, 0, 0, 40000, 0,0.000000 }, - {2596,2597, 0, 0, 40000, 6,0.000000 }, - {2598,2599, 0, 0, 40000, 0,0.000000 }, - {2600,2601, 0, 0, 40000, 13,0.000000 }, - {2602,2602, 0, 0, 5093, 5093,0.000000 }, - {2603,2603, 0, 0, 40000, 6,0.000000 }, - {2604,2604, 0, 0, 40000, 40,0.000000 }, - {2605,2606, 0, 0, 40000, 0,0.000000 }, - {2607,2607, 0, 0, 40000, 0,0.000000 }, - {2608,2609, 0, 0, 40000, 6,0.000000 }, - {2610,2610, 0, 0, 40000, 20,0.000000 }, - {2611,2611, 0, 0, 40000, 6,0.000000 }, - {2612,2612, 0, 0, 2253, 2253,0.000000 }, - {2613,2613, 0, 0, 973, 973,0.000000 }, - {2614,2615, 0, 0, 40000, 6,0.000000 }, - {2616,2617, 0, 0, 40000, 6,0.000000 }, - {2618,2619, 0, 0, 353, 353,0.000000 }, - {2620,2621, 0, 0, 40000, 500,0.000000 }, - {2622,2623, 0, 0, 40000, 26,0.000000 }, - {2624,2625, 0, 0, 40000, 53,0.000000 }, - {2626,2627, 0, 0, 4026, 13,0.000000 }, - {2628,2629, 0, 0, 3400, 13,0.000000 }, - {1516,2630, 0, 0, 226, 226,0.000000 }, - {2631,2632, 0, 0, 40000, 193,0.000000 }, - {2633,2633, 0, 0, 2380, 2380,0.000000 }, - {2634,2635, 0, 0, 40000, 373,0.000000 }, - {2636,2636, 0, 0, 40000, 133,0.000000 }, - {2637,2637, 0, 0, 280, 280,0.000000 }, - {2638,2639, 0, 0, 273, 273,0.000000 }, - {2640,2641, 0, 0, 3333, 3333,0.000000 }, - {2642,2642, 0, 0, 40000, 266,0.000000 }, - {2643,2644, 0, 0, 166, 166,0.000000 }, - {2645,2645, 0, 0, 40000, 6,0.000000 }, - {2646,2647, 0, 0, 40000, 0,0.000000 }, - {2648,2648, 0, 0, 586, 586,0.000000 }, - {2649,2649, 0, 0, 1660, 86,0.000000 }, - {2650,2651, 0, 0, 440, 440,0.000000 }, - {2652,2652, 0, 0, 40000, 113,0.000000 }, - {2653,2654, 0, 0, 40000, 6,0.000000 }, - {2655,2656, 0, 0, 1766, 1766,0.000000 }, - {2657,2657, 0, 0, 40000, 0,0.000000 }, - {2658,2658, 35, 0, 20, 20,0.000000 }, - {2659,2659, 35, 0, 26, 26,0.000000 }, - {2660,2660, 52, 0, 20, 20,0.000000 }, - {2661,2661, 60, 0, 80, 80,0.000000 }, - {2662,2662, 58, 0, 20, 20,0.000000 }, - {2663,2663, 60, 0, 113, 113,0.000000 }, - {2664,2664, 50, 0, 433, 433,0.000000 }, - {2665,2665, 43, 0, 40, 40,0.000000 }, - {2664,2664, 55, 0, 513, 513,0.000000 }, - {1553,1553, 43, 0, 26, 26,0.000000 }, - {2666,2666, 50, 0, 173, 173,0.000000 }, - {2667,2667, 43, 0, 173, 173,0.000000 }, - {2666,2666, 53, 0, 40, 40,0.000000 }, - {2666,2666, 57, 0, 186, 186,0.000000 }, - {2668,2668, 72, 0, 260, 260,0.000000 }, - {2666,2666, 60, 0, 220, 220,0.000000 }, - { 373, 373, 76, 0, 260, 260,0.000000 }, - {2669,2669, 84, 0, 213, 213,0.000000 }, - {2670,2670, 42, 0, 406, 406,0.000000 }, - {2671,2671, 65, 0, 86, 86,0.000000 }, - {2672,2672, 84, 0, 113, 113,0.000000 }, - {2673,2673, 84, 0, 426, 426,0.000000 }, - {2674,2674, 24, 0, 46, 46,0.000000 }, - { 383, 383, 77, 0, 246, 246,0.000000 }, - {2675,2675, 58, 0, 153, 153,0.000000 }, - {2676,2676, 53, 0, 153, 153,0.000000 }, - {2677,2677, 64, 0, 73, 73,0.000000 }, - {2678,2678, 71, 0, 26, 26,0.000000 }, - {2679,2679, 44, 0, 606, 606,0.000000 }, - {2680,2680, 40, 0, 366, 366,0.000000 }, - {2681,2681, 69, 0, 20, 20,0.000000 }, - {2682,2682, 60, 0, 360, 360,0.000000 }, - {2683,2683, 80, 0, 60, 60,0.000000 }, - {2684,2684, 64, 0, 873, 873,0.000000 }, - {2685,2685, 72, 0, 33, 33,0.000000 }, - {2686,2686, 70, 0, 273, 273,0.000000 }, - {2687,2687, 48, 0, 40, 40,0.000000 }, - {2688,2688, 53, 0, 213, 213,0.000000 }, - {2689,2690, 0, 0, 40000, 140,0.000000 }, - {2691,2692, 0, 0, 626, 626,0.000000 }, - {2693,2694, 0, 0, 40000, 0,0.000000 }, - {2695,2696, 0, 0, 2213, 2213,0.000000 }, - {2697,2698, 0, 0, 4140, 0,0.000000 }, - { 192,2699, 0, 0, 40000, 6,0.000000 }, - {2700,2701, 0, 0, 153, 153,0.000000 }, - {2702,2703, 0, 0, 253, 253,0.000000 }, - {2704,2705, 0, 0, 40000, 0,0.000000 }, - {2706,2707, 0, 0, 40000, 6,0.000000 }, - {2708,2709, 0, 0, 40000, 6,0.000000 }, - {2710,2711, 0, 0, 440, 440,0.000000 }, - {2712,1473, 0, 0, 213, 213,0.000000 }, - {2713,2714, 0, 0, 120, 120,0.000000 }, - {2715,2716, 0, 0, 140, 140,0.000000 }, - {1478,2717, 0, 0, 40000, 113,0.000000 }, - {2718,2719, 0, 0, 40000, 6,0.000000 }, - { 286,2720, 0, 0, 40000, 20,0.000000 }, - {2721,2722, 0, 0, 40000, 13,0.000000 }, - {2723,2724, 0, 0, 40000, 0,0.000000 }, - {2725,2726, 0, 0, 526, 526,0.000000 }, - {2727,2724, 0, 0, 40000, 0,0.000000 }, - {1514,2728, 0, 0, 40000, 53,0.000000 }, - {2729,2730, 0, 0, 186, 186,0.000000 }, - {2731,2732, 0, 0, 226, 226,0.000000 }, - {2733,2734, 0, 0, 266, 266,0.000000 }, - {2735,2736, 0, 0, 80, 80,0.000000 }, - {2737,2738, 0, 0, 40000, 0,0.000000 }, - {2739,2740, 0, 0, 260, 260,0.000000 }, - {2741,2742, 0, 0, 286, 286,0.000000 }, - {2743,2744, 0, 0, 193, 193,0.000000 }, - {2745,2746, 0, 0, 40000, 0,0.000000 }, - {2747,2747, 35, 0, 20, 20,0.000000 }, - {2748,2748, 60, 0, 153, 153,0.000000 }, - {2749,2749, 43, 0, 20, 20,0.000000 }, - {2750,2750, 0, 0, 626, 626,0.000000 }, - {2751,2752, 0, 0, 1553, 1553,0.000000 }, - {2753,2754, 0, 0, 1386, 1386,0.000000 }, - {2755,2756, 0, 0, 2026, 2026,0.000000 }, - {2757,2758, 0, 0, 1020, 1020,0.000000 }, - {2759,2760, 0, 0, 2486, 2486,0.000000 }, - {2761,2762, 0, 0, 2880, 2880,0.000000 }, - {2763,2764, 0, 0, 2346, 2346,0.000000 }, - {2765,2766, 0, 0, 40000, 0,0.000000 }, - {2767,2768, 0, 0, 40000, 100,0.000000 }, - {2769,2770, 0, 0, 40000, 13,0.000000 }, - {2771,2772, 0, 0, 40000, 6,0.000000 }, - {2773,2774, 0, 0, 40000, 0,0.000000 }, - {2775,2776, 0, 0, 40000, 13,0.000000 }, - {2777,2778, 0, 0, 40000, 0,0.000000 }, - {2779,2780, 0, 0, 40000, 6,0.000000 }, - {2781,2782, 0, 0, 40000, 6,0.000000 }, - {2783,2784, 0, 0, 913, 913,0.000000 }, - {2785,2786, 0, 0, 1653, 1653,0.000000 }, - {2787,2788, 0, 0, 40000, 153,0.000000 }, - {2789,2790, 0, 0, 1273, 1273,0.000000 }, - {2791,2792, 0, 0, 1166, 1166,0.000000 }, - {2793,2794, 0, 0, 386, 386,0.000000 }, - {2795,2796, 0, 0, 40000, 1993,0.000000 }, - {2795,2797, 0, 0, 2526, 1333,0.000000 }, - {2798,2799, 0, 0, 166, 6,0.000000 }, - {2800,2801, 0, 0, 13080, 213,0.000000 }, - {2802,2803, 0, 0, 40000, 6,0.000000 }, - {2804,2805, 0, 0, 40000, 6,0.000000 }, - {2806,2807, 0, 0, 493, 493,0.000000 }, - {2808,2809, 0, 0, 126, 6,0.000000 }, - {2810,2811, 0, 0, 80, 80,0.000000 }, - {2812,2813, 0, 0, 380, 380,0.000000 }, - {2814,2815, 0, 0, 2506, 2506,0.000000 }, - {2816,2817, 0, 0, 2186, 2186,0.000000 }, - {2818,2819, 0, 0, 2520, 2520,0.000000 }, - {2820,2821, 0, 0, 440, 440,0.000000 }, - {2822,2823, 0, 0, 106, 106,0.000000 }, - {2824,2825, 0, 0, 1933, 1933,0.000000 }, - {2826,2827, 0, 0, 40000, 6,0.000000 }, - {2828,2829, 0, 0, 1933, 1933,0.000000 }, - {2830,2831, 0, 0, 546, 546,0.000000 }, - {2832,2833, 0, 0, 973, 973,0.000000 }, - {2834,2835, 0, 0, 586, 26,0.000000 }, - {2836,2837, 0, 0, 786, 786,0.000000 }, - {2838,2839, 0, 0, 1920, 1920,0.000000 }, - {2840,2841, 0, 0, 333, 333,0.000000 }, - {2842,2843, 0, 0, 1120, 1120,0.000000 }, - {2844,2845, 0, 0, 40000, 153,0.000000 }, - {2846,2847, 0, 0, 40000, 153,0.000000 }, - {2848,2849, 0, 0, 40000, 153,0.000000 }, - {2850,2851, 0, 0, 40000, 213,0.000000 }, - {2852,2853, 0, 0, 40000, 240,0.000000 }, - {2854,2855, 0, 0, 40000, 93,0.000000 }, - {2856,2856, 0, 0, 333, 333,0.000000 }, - {2857,2857, 0, 0, 333, 333,0.000000 }, - {2858,2859, 0, 0, 866, 866,0.000000 }, - {2860,2861, 0, 0, 40000, 180,0.000000 }, - {2862,2863, 0, 0, 866, 866,0.000000 }, - {2864,2865, 0, 0, 40000, 6,0.000000 }, - {2866,2867, 0, 0, 946, 946,0.000000 }, - {2868,2869, 0, 0, 906, 906,0.000000 }, - {2870,2871, 0, 0, 846, 846,0.000000 }, - {2872,2873, 0, 0, 160, 160,0.000000 }, - {2874,2875, 0, 0, 586, 6,0.000000 }, - {2876,2877, 0, 0, 160, 160,0.000000 }, - {2878,2879, 0, 0, 760, 760,0.000000 }, - {2880,2881, 0, 0, 1193, 1193,0.000000 }, - {2882,2883, 0, 0, 493, 493,0.000000 }, - {2884,2885, 0, 0, 893, 893,0.000000 }, - {2886,2887, 0, 0, 40000, 0,0.000000 }, - {2888,2888, 0, 0, 4413, 4413,0.000000 }, - {2889,2889, 0, 0, 4193, 4193,0.000000 }, - {2890,2890, 0, 0, 3946, 3946,0.000000 }, - {2891,2892, 0, 0, 40000, 26,0.000000 }, - {2893,2893, 0, 0, 60, 60,0.000000 }, - {2894,2894, 0, 0, 40, 40,0.000000 }, - {2895,2895, 0, 0, 1773, 1773,0.000000 }, - {2896,2897, 0, 0, 40000, 6,0.000000 }, - {2898,2899, 0, 0, 1073, 6,0.000000 }, - {2353,2900, 0, 0, 4800, 4800,0.000000 }, - {2901,2901, 0, 0, 126, 126,0.000000 }, - {2902,2903, 0, 0, 40000, 20,0.000000 }, - {2904,2905, 0, 0, 40000, 20,0.000000 }, - {2906,2907, 0, 0, 40000, 6,0.000000 }, - {2908,2909, 0, 0, 4546, 4546,0.000000 }, - {2910,2911, 0, 0, 40000, 6,0.000000 }, - {2912,2912, 0, 0, 913, 913,0.000000 }, - {2913,2914, 0, 0, 486, 486,0.000000 }, - {2915,2916, 0, 0, 1740, 1740,0.000000 }, - {2917,2918, 0, 0, 1726, 1726,0.000000 }, - {2919,2920, 0, 0, 1740, 1740,0.000000 }, - {2921,2922, 0, 0, 1420, 1420,0.000000 }, - {2923,2924, 0, 0, 546, 546,0.000000 }, - {2925,2926, 0, 0, 513, 513,0.000000 }, - {2927,2928, 0, 0, 226, 226,0.000000 }, - {2929,2929, 0, 0, 40000, 73,0.000000 }, - {2930,2931, 0, 0, 40000, 33,0.000000 }, - {2932,2933, 0, 0, 40000, 6,0.000000 }, - {2934,2935, 0, 0, 40000, 20,0.000000 }, - {2936,2937, 0, 0, 40000, 13,0.000000 }, - {2938,2939, 0, 0, 40000, 106,0.000000 }, - {2940,2940, 0, 0, 40, 40,0.000000 }, - {2941,2941, 0, 0, 40000, 86,0.000000 }, - {2942,2943, 0, 0, 293, 293,0.000000 }, - {2944,2944, 0, 0, 53, 53,0.000000 }, - {2945,2945, 0, 0, 260, 260,0.000000 }, - {2946,2947, 0, 0, 1026, 1026,0.000000 }, - {2948,2948, 0, 0, 40000, 120,0.000000 }, - {2949,2949, 0, 0, 280, 280,0.000000 }, - {2950,2951, 0, 0, 3926, 3926,0.000000 }, - {2952,2953, 0, 0, 6373, 206,0.000000 }, - {2954,2954, 60, 0, 40000, 0,0.000000 }, - {2955,2956, 0, 0, 40000, 153,0.000000 }, - {2957,2958, 0, 0, 40000, 6,0.000000 }, - {2959,2960, 0, 0, 40000, 26,0.000000 }, - {2961,2962, 0, 0, 700, 140,0.000000 }, - {2963,2963, 0, 0, 506, 506,0.000000 }, - {2964,2964, 0, 0, 40000, 680,0.000000 }, - {2436,2436, 49, 0, 593, 593,0.000000 }, - {2357,2357, 61, 0, 26, 26,0.000000 }, - {2357,2357, 56, 0, 26, 26,0.000000 }, - {2357,2357, 58, 0, 26, 26,0.000000 }, - {2357,2357, 49, 0, 33, 33,0.000000 }, - {2357,2357, 44, 0, 26, 26,0.000000 }, - {2965,2965, 0, 0, 40000, 186,0.000000 }, - {2966,2966, 0, 0, 186, 186,0.000000 }, - {2967,2967, 0, 0, 1793, 1793,0.000000 }, - {2968,2968, 0, 0, 40000, 53,0.000000 }, - {2969,2969, 0, 0, 80, 80,0.000000 }, - {2970,2970, 84, 0, 40, 40,0.000000 }, - {2971,2971, 72, 0, 140, 140,0.000000 }, - {2972,2972, 0, 0, 2080, 2080,0.000000 }, - {2973,2973, 0, 0, 40000, 66,0.000000 }, - {2974,2974, 0, 0, 40000, 2100,0.000000 }, - {2975,2975, 0, 0, 1766, 1766,0.000000 }, - {2976,2976, 0, 0, 120, 120,0.000000 }, - {2977,2977, 0, 0, 40000, 0,0.000000 }, - {2978,2978, 0, 0, 40000, 66,0.000000 }, - {2979,2979, 0, 0, 40000, 73,0.000000 }, - {2980,2980, 0, 0, 40000, 126,0.000000 }, - {2981,2981, 0, 0, 40000, 506,0.000000 }, - {2982,2982, 0, 0, 40000, 373,0.000000 }, - {2983,2983, 0, 0, 920, 920,0.000000 }, - {2984,2984, 0, 0, 1106, 1106,0.000000 }, - {2985,2985, 0, 0, 40000, 0,0.000000 }, - {2986,2986, 0, 0, 40000, 0,0.000000 }, - {2987,2987, 0, 0, 40000, 0,0.000000 }, - {2988,2988, 0, 0, 40000, 140,0.000000 }, - {2989,2989, 0, 0, 726, 726,0.000000 }, - {2990,2990, 0, 0, 573, 573,0.000000 }, - {2991,2991, 0, 0, 40000, 146,0.000000 }, - {2992,2992, 0, 0, 40000, 66,0.000000 }, - {2993,2993, 0, 0, 146, 146,0.000000 }, - {2994,2994, 0, 0, 1380, 1380,0.000000 }, - {2995,2995, 0, 0, 40000, 113,0.000000 }, - {2996,2996, 0, 0, 1140, 1140,0.000000 }, - {2997,2997, 0, 0, 1180, 1180,0.000000 }, - {2998,2998, 0, 0, 40000, 0,0.000000 }, - {2999,2999, 0, 0, 40000, 13,0.000000 }, - {3000,3000, 0, 0, 193, 193,0.000000 }, - {3001,3001, 0, 0, 40000, 0,0.000000 }, - {3002,3002, 0, 0, 40000, 433,0.000000 }, - {3003,3003, 0, 0, 1206, 1206,0.000000 }, - {3004,3004, 0, 0, 40000, 480,0.000000 }, - {3005,3005, 0, 0, 40000, 0,0.000000 }, - {3006,3006, 0, 0, 126, 126,0.000000 }, - {3007,3007, 0, 0, 333, 333,0.000000 }, - { 350, 350, 0, 0, 66, 66,0.000000 }, - {3008,3008, 0, 0, 46, 46,0.000000 }, - {3009,3009, 0, 0, 26, 26,0.000000 }, - {3010,3010, 0, 0, 320, 320,0.000000 }, - {3011,3011, 0, 0, 2380, 2380,0.000000 }, - {3012,3012, 0, 0, 180, 180,0.000000 }, - {3013,3013, 0, 0, 260, 260,0.000000 }, - {3014,3014, 0, 0, 40000, 6,0.000000 }, - {3015,3015, 0, 0, 40000, 146,0.000000 }, - { 350, 350, 36, 0, 86, 86,0.000000 }, - { 369, 369, 37, 0, 33, 33,0.000000 }, - {3008,3008, 38, 0, 53, 53,0.000000 }, - { 369, 369, 24, 0, 33, 33,0.000000 }, - {3008,3008, 32, 0, 60, 60,0.000000 }, - { 369, 369, 48, 0, 33, 33,0.000000 }, - {3009,3009, 42, 0, 33, 33,0.000000 }, - { 369, 369, 50, 0, 100, 100,0.000000 }, - { 369, 369, 52, 0, 66, 66,0.000000 }, - { 369, 369, 54, 0, 93, 93,0.000000 }, - { 369, 369, 55, 0, 86, 86,0.000000 }, - { 369, 369, 57, 0, 100, 100,0.000000 }, - {3010,3010, 51, 0, 333, 333,0.000000 }, - { 144, 144, 61, 0, 40, 40,0.000000 }, - {3016,3016, 0, 0, 106, 106,0.000000 }, - {3016,3016, 63, 0, 86, 86,0.000000 }, - {3016,3016, 64, 0, 86, 86,0.000000 }, - {3017,3017, 40, 0, 60, 60,0.000000 }, - {3017,3017, 70, 0, 40, 40,0.000000 }, - {3018,3018, 0, 0, 40000, 6,0.000000 }, - {3019,3019, 0, 0, 40000, 0,0.000000 }, - {3020,3020, 0, 0, 40000, 6,0.000000 }, - {3021,3021, 0, 0, 40000, 6,0.000000 }, - {3022,3022, 38, 0, 46, 46,0.000000 }, - {2441,2441, 57, 0, 73, 73,0.000000 }, - {3023,3023, 63, 0, 33, 33,0.000000 }, - {3024,3024, 74, 0, 80, 80,0.000000 }, - {3025,3025, 74, 0, 206, 206,0.000000 }, - {3026,3026, 60, 0, 300, 300,0.000000 }, - {1593,1594, 35, 0, 646, 646,0.000000 }, - {1564,1565, 35, 0, 66, 66,0.000000 }, - { 248,3027, 0, 0, 40000, 33,0.000000 }, - {1445,3028, 0, 0, 1860, 1860,0.000000 }, - {1447,3029, 0, 0, 1626, 1626,0.000000 }, - {1452,3030, 0, 0, 1740, 1740,0.000000 }, - {1544,3031, 0, 0, 160, 160,0.000000 }, - {1546,3032, 0, 0, 1913, 1913,0.000000 }, - { 398, 399, 35, 0, 600, 600,0.000000 }, - {1550,3033, 35, 0, 20, 20,0.000000 }, - {1556,1557, 35, 0, 386, 386,0.000000 }, - {1558,1559, 35, 0, 346, 346,0.000000 }, - {1570,1571, 35, 0, 360, 360,0.000000 }, - {1608,1609, 35, 0, 460, 460,0.000000 }, - {1595,1596, 35, 0, 60, 60,0.000000 }, - { 159,1597, 35, 0, 60, 60,0.000000 }, - {1610,1611, 35, 0, 60, 60,0.000000 }, - { 397,1588, 35, 0, 100, 100,0.000000 }, - {1606,1607, 35, 0, 46, 46,0.000000 }, - { 145,1576, 35, 0, 6, 6,0.000000 }, - {1612,1613, 35, 0, 60, 60,0.000000 }, - {1577,1578, 35, 0, 53, 53,0.000000 }, - {1614,1615, 35, 0, 6, 6,0.000000 }, - {1550,1551, 35, 0, 20, 20,0.000000 }, - { 364, 365, 35, 0, 20, 20,0.000000 }, - { 129,1549, 35, 0, 93, 93,0.000000 }, - { 132,1552, 35, 0, 20, 20,0.000000 }, - {1553,1554, 35, 0, 33, 33,0.000000 }, - { 129,1548, 35, 0, 86, 86,0.000000 }, - { 134,1555, 35, 0, 133, 133,0.000000 }, - {1560,1561, 35, 0, 140, 140,0.000000 }, - {1562,1563, 35, 0, 40000, 0,0.000000 }, - {1572,1573, 35, 0, 66, 66,0.000000 }, - {1574,1575, 35, 0, 60, 60,0.000000 }, - {1581,1582, 35, 0, 20, 20,0.000000 }, - { 149,1583, 35, 0, 53, 53,0.000000 }, - {1584,1585, 35, 0, 206, 206,0.000000 }, - {1591,1592, 35, 0, 386, 386,0.000000 }, - {1579,1580, 35, 0, 53, 53,0.000000 }, - {1586,1587, 35, 0, 180, 180,0.000000 }, - {1589,1590, 35, 0, 20, 20,0.000000 }, - {1600,1601, 35, 0, 140, 140,0.000000 }, - {1602,1603, 35, 0, 220, 220,0.000000 }, - {1604,1605, 35, 0, 1293, 1293,0.000000 }, - {1598,1599, 35, 0, 233, 233,0.000000 }, - { 374, 375, 35, 0, 580, 580,0.000000 }, - {1566,1567, 35, 0, 1666, 0,0.000000 }, - {2306,2307, 35, 0, 140, 140,0.000000 }, - {3034, 339, 35, 0, 4293, 4293,0.000000 }, - {2305,1548, 35, 0, 80, 80,0.000000 }, - {1595,1595, 35, 0, 60, 60,0.000000 }, - {2303,2304, 35, 0, 20, 20,0.000000 }, - {1560,2308, 35, 0, 140, 140,0.000000 }, - {2309,2310, 35, 0, 40000, 0,0.000000 }, - {1568,1568, 35, 0, 20, 20,0.000000 }, - {2311,2312, 35, 0, 66, 66,0.000000 }, - {2313,2314, 35, 0, 66, 66,0.000000 }, - {2315,2315, 35, 0, 13, 13,0.000000 }, - {2316,2316, 35, 0, 53, 53,0.000000 }, - {1612,1612, 35, 0, 60, 60,0.000000 }, - {2317,2317, 35, 0, 153, 153,0.000000 }, - {2318,2318, 35, 0, 86, 86,0.000000 }, - {2319,2319, 35, 0, 46, 46,0.000000 }, - {2320,2320, 35, 0, 46, 46,0.000000 }, - {2321,2321, 35, 0, 20, 20,0.000000 }, - {2322,2322, 35, 0, 426, 426,0.000000 }, - {2323,2323, 35, 0, 826, 826,0.000000 }, - {2324, 392, 35, 0, 20, 20,0.000000 }, - {2324, 393, 35, 0, 346, 346,0.000000 }, - {2325,2326, 35, 0, 220, 220,0.000000 }, - {2327,2328, 35, 0, 180, 180,0.000000 }, - {2329,2330, 35, 0, 160, 160,0.000000 }, - {2331,2332, 35, 0, 573, 573,0.000000 }, - {3035,3036, 35, 0, 1026, 1026,0.000000 }, - {3037,3038, 35, 0, 400, 400,0.000000 }, - {1564,1564, 35, 0, 60, 60,0.000000 }, - {3039,3039, 0, 0, 2293, 2293,0.000000 }, - {3040,3040, 0, 0, 2006, 2006,0.000000 }, - {3041,3041, 0, 0, 1226, 1226,0.000000 }, - {3042,3042, 0, 0, 1026, 1026,0.000000 }, - {3043,3043, 0, 0, 760, 760,0.000000 }, - {3044,3044, 0, 0, 546, 546,0.000000 }, - {3045,3045, 0, 0, 613, 613,0.000000 }, - {3046,3046, 0, 0, 40000, 53,0.000000 }, - {3047,3047, 0, 0, 1013, 1013,0.000000 }, - {3048,3048, 0, 0, 40000, 606,0.000000 }, - {3049,3049, 0, 0, 3626, 3626,0.000000 }, - {3050,3050, 0, 0, 993, 993,0.000000 }, - {3051,3051, 0, 0, 266, 266,0.000000 }, - {3052,3052, 0, 0, 140, 140,0.000000 }, - {3053,3053, 0, 0, 2253, 2253,0.000000 }, - {3054,3054, 0, 0, 2513, 2513,0.000000 }, - {3055,3055, 0, 0, 40000, 0,0.000000 }, - {3056,3056, 0, 0, 40000, 0,0.000000 }, - {3057,3057, 0, 0, 2146, 2146,0.000000 }, - {3058,3058, 0, 0, 40000, 0,0.000000 }, - {3059,3059, 0, 0, 40000, 6,0.000000 }, - {3060,3060, 0, 0, 40000, 6,0.000000 }, - {3061,3061, 0, 0, 460, 460,0.000000 }, - {3062,3062, 0, 0, 1846, 1846,0.000000 }, - {3063,3063, 0, 0, 933, 933,0.000000 }, - {3064,3064, 0, 0, 206, 206,0.000000 }, - {3065,3065, 0, 0, 40000, 20,0.000000 }, - {3066,3066, 0, 0, 80, 80,0.000000 }, - {3067,3067, 0, 0, 1973, 1973,0.000000 }, - {3068,3068, 0, 0, 40000, 60,0.000000 }, - {3069,3069, 0, 0, 1120, 1120,0.000000 }, - {3070,3070, 0, 0, 420, 420,0.000000 }, - {3071,3071, 0, 0, 1120, 1120,0.000000 }, - {3072,3072, 0, 0, 806, 806,0.000000 }, - {3073,3073, 0, 0, 1186, 1186,0.000000 }, - {3074,3074, 0, 0, 1900, 1900,0.000000 }, - {3075,3075, 0, 0, 40000, 0,0.000000 }, - {3076,3076, 0, 0, 1046, 1046,0.000000 }, - {3077,3077, 0, 0, 600, 600,0.000000 }, - {3078,3078, 0, 0, 146, 13,0.000000 }, - {3079,3079, 0, 0, 786, 786,0.000000 }, - {3080,3080, 0, 0, 40000, 26,0.000000 }, - {3081,3081, 0, 0, 146, 13,0.000000 }, - {3082,3082, 0, 0, 40000, 46,0.000000 }, - {3083,3083, 0, 0, 100, 100,0.000000 }, - {3084,3084, 0, 0, 1580, 1580,0.000000 }, - {3085,3085, 0, 0, 280, 280,0.000000 }, - {3086,3086, 0, 0, 40000, 80,0.000000 }, - {3087,3087, 0, 0, 40000, 73,0.000000 }, - {3088,3088, 0, 0, 40000, 66,0.000000 }, - {3089,3089, 0, 0, 40000, 73,0.000000 }, - {3090,3090, 0, 0, 40000, 0,0.000000 }, - {3091,3091, 0, 0, 40000, 6,0.000000 }, - {3092,3092, 0, 0, 40000, 6,0.000000 }, - {3093,3093, 0, 0, 246, 246,0.000000 }, - {3094,3094, 0, 0, 40000, 73,0.000000 }, - {3095,3095, 0, 0, 273, 126,0.000000 }, - {3096,3096, 0, 0, 40000, 33,0.000000 }, - {3097,3097, 0, 0, 40000, 6,0.000000 }, - {3098,3098, 0, 0, 40000, 73,0.000000 }, - {3099,3099, 0, 0, 1233, 53,0.000000 }, - {3100,3100, 0, 0, 40000, 66,0.000000 }, - {3101,3101, 0, 0, 40000, 0,0.000000 }, - {3102,3102, 0, 0, 40000, 0,0.000000 }, - {3103,3103, 0, 0, 40000, 6,0.000000 }, - {3104,3104, 0, 0, 40000, 0,0.000000 }, - {3105,3105, 0, 0, 40000, 6,0.000000 }, - {3106,3106, 0, 0, 40000, 0,0.000000 }, - {3107,3107, 0, 0, 40000, 0,0.000000 }, - {3108,3108, 0, 0, 40000, 6,0.000000 }, - {3109,3109, 0, 0, 40000, 6,0.000000 }, - {3110,3110, 0, 0, 40000, 6,0.000000 }, - {3111,3111, 0, 0, 40000, 20,0.000000 }, - {3112,3112, 0, 0, 2240, 2240,0.000000 }, - {3113,3113, 0, 0, 100, 6,0.000000 }, - {3114,3114, 0, 0, 4466, 4466,0.000000 }, - {3115,3115, 0, 0, 40000, 20,0.000000 }, - {3116,3116, 0, 0, 40000, 13,0.000000 }, - {3117,3117, 0, 0, 40000, 53,0.000000 }, - {3118,3118, 0, 0, 40000, 300,0.000000 }, - {3119,3119, 0, 0, 6906, 6906,0.000000 }, - {3120,3120, 0, 0, 40000, 33,0.000000 }, - {3121,3121, 0, 0, 40000, 6,0.000000 }, - {3122,3122, 0, 0, 1953, 1953,0.000000 }, - {3123,3123, 0, 0, 973, 973,0.000000 }, - {3124,3124, 0, 0, 2240, 6,0.000000 }, - {3125,3125, 0, 0, 1986, 1986,0.000000 }, - {3126,3126, 0, 0, 1613, 1613,0.000000 }, - {3127,3127, 0, 0, 353, 353,0.000000 }, - {3128,3128, 0, 0, 66, 66,0.000000 }, - {3129,3129, 0, 0, 5586, 5586,0.000000 }, - {3130,3130, 0, 0, 173, 173,0.000000 }, - {3131,3131, 35, 0, 120, 120,0.000000 }, - {3132,3132, 35, 0, 6, 6,0.000000 }, - {3133,3133, 35, 0, 40000, 46,0.000000 }, - {3134,3134, 35, 0, 40000, 46,0.000000 }, - {3135,3135, 35, 0, 100, 100,0.000000 }, - {3061,3061, 35, 0, 140, 140,0.000000 }, - {3136,3136, 35, 0, 20, 20,0.000000 }, - { 739, 739, 35, 0, 40, 40,0.000000 }, - {3137,3137, 35, 0, 186, 186,0.000000 }, - {3138,3138, 35, 0, 160, 160,0.000000 }, - {3139,3139, 35, 0, 93, 93,0.000000 }, - {3140,3140, 35, 0, 80, 80,0.000000 }, - {3141,3141, 35, 0, 133, 133,0.000000 }, - {3142,3142, 35, 0, 66, 66,0.000000 }, - {3143,3143, 35, 0, 133, 133,0.000000 }, - {3144,3144, 35, 0, 666, 666,0.000000 }, - {3145,3145, 35, 0, 93, 93,0.000000 }, - {3146,3146, 35, 0, 1433, 1433,0.000000 }, - {3147,3147, 35, 0, 393, 393,0.000000 }, - {3148,3148, 35, 0, 93, 93,0.000000 }, - {3149,3149, 35, 0, 1480, 1480,0.000000 }, - {3150,3150, 0, 0, 1126, 1126,0.000000 }, - {3151,3151, 0, 0, 1766, 1766,0.000000 }, - {3152,3152, 0, 0, 620, 620,0.000000 }, - {3153,3153, 0, 0, 460, 460,0.000000 }, - {3154,3154, 0, 0, 1173, 1173,0.000000 }, - {3155,3155, 0, 0, 626, 626,0.000000 }, - {3156,3156, 0, 0, 1740, 1740,0.000000 }, - {3157,3157, 0, 0, 280, 280,0.000000 }, - {3158,3158, 0, 0, 46, 46,0.000000 }, - {3159,3159, 0, 0, 280, 280,0.000000 }, - {3160,3160, 0, 0, 1153, 1153,0.000000 }, - {3161,3161, 0, 0, 40000, 0,0.000000 }, - {3162,3162, 0, 0, 40000, 6,0.000000 }, - {3163,3163, 0, 0, 40000, 46,0.000000 }, - {3164,3164, 0, 0, 40000, 0,0.000000 }, - {3165,3165, 0, 0, 40000, 6,0.000000 }, - {3166,3166, 0, 0, 2280, 2280,0.000000 }, - {3167,3167, 0, 0, 40000, 0,0.000000 }, - {3168,3168, 0, 0, 486, 486,0.000000 }, - {3169,3169, 0, 0, 600, 600,0.000000 }, - {3170,3170, 0, 0, 466, 466,0.000000 }, - {3171,3171, 0, 0, 593, 593,0.000000 }, - {3172,3172, 0, 0, 1153, 1153,0.000000 }, - {3173,3173, 0, 0, 280, 280,0.000000 }, - {3174,3174, 0, 0, 586, 586,0.000000 }, - {3175,3175, 0, 0, 206, 13,0.000000 }, - {3176,3176, 0, 0, 186, 60,0.000000 }, - {3177,3177, 0, 0, 2366, 2366,0.000000 }, - {3178,3178, 0, 0, 1700, 1700,0.000000 }, - {3179,3179, 0, 0, 46, 46,0.000000 }, - {3180,3180, 0, 0, 886, 886,0.000000 }, - {3181,3181, 0, 0, 186, 186,0.000000 }, - {3182,3182, 0, 0, 40000, 186,0.000000 }, - {3183,3183, 0, 0, 560, 560,0.000000 }, - {3184,3184, 0, 0, 1466, 1466,0.000000 }, - {3185,3185, 0, 0, 1553, 1553,0.000000 }, - {3186,3186, 0, 0, 380, 380,0.000000 }, - {3187,3187, 0, 0, 146, 146,0.000000 }, - {3188,3188, 0, 0, 146, 146,0.000000 }, - {3189,3189, 0, 0, 4726, 4726,0.000000 }, - {3190,3190, 0, 0, 40000, 6,0.000000 }, - {3191,3191, 0, 0, 1766, 1766,0.000000 }, - {3192,3192, 0, 0, 40000, 0,0.000000 }, - {3193,3193, 0, 0, 806, 806,0.000000 }, - {3194,3194, 0, 0, 1133, 1133,0.000000 }, - {3195,3195, 0, 0, 40000, 6,0.000000 }, - {3196,3196, 0, 0, 40000, 0,0.000000 }, - {3197,3197, 0, 0, 40000, 6,0.000000 }, - {3198,3198, 0, 0, 40000, 20,0.000000 }, - {3199,3199, 0, 0, 40000, 6,0.000000 }, - {3200,3200, 0, 0, 213, 213,0.000000 }, - {3201,3201, 0, 0, 40000, 106,0.000000 }, - {3202,3202, 0, 0, 1113, 1113,0.000000 }, - {3203,3203, 0, 0, 1953, 1953,0.000000 }, - {3204,3204, 0, 0, 40000, 0,0.000000 }, - {3205,3205, 0, 0, 40000, 0,0.000000 }, - {3206,3206, 0, 0, 340, 340,0.000000 }, - {3207,3207, 0, 0, 40000, 6,0.000000 }, - {3208,3208, 0, 0, 1126, 1126,0.000000 }, - {3209,3209, 0, 0, 300, 300,0.000000 }, - {3210,3210, 0, 0, 40000, 46,0.000000 }, - {3211,3211, 0, 0, 40000, 113,0.000000 }, - {3212,3212, 0, 0, 1126, 1126,0.000000 }, - {3213,3213, 0, 0, 2853, 2853,0.000000 }, - {3214,3214, 0, 0, 40000, 0,0.000000 }, - {3215,3215, 0, 0, 1286, 0,0.000000 }, - {3216,3216, 0, 0, 40000, 233,0.000000 }, - {3217,3217, 0, 0, 40000, 1640,0.000000 }, - {3218,3218, 0, 0, 40000, 240,0.000000 }, - {3219,3219, 0, 0, 1766, 1766,0.000000 }, - {3220,3220, 0, 0, 40000, 1493,0.000000 }, - {3221,3221, 0, 0, 40000, 2120,0.000000 }, - {3222,3222, 0, 0, 1226, 1226,0.000000 }, - {3223,3223, 0, 0, 306, 306,0.000000 }, - {3224,3224, 0, 0, 40000, 6,0.000000 }, - {3225,3225, 0, 0, 666, 13,0.000000 }, - {3226,3226, 0, 0, 153, 153,0.000000 }, - {3227,3227, 0, 0, 626, 626,0.000000 }, - {3228,3228, 0, 0, 140, 140,0.000000 }, - {3229,3229, 0, 0, 233, 233,0.000000 }, - { 499, 499, 0, 0, 40, 40,0.000000 }, - {3230,3230, 0, 0, 46, 46,0.000000 }, - {3231,3231, 0, 0, 146, 146,0.000000 }, - {3232,3232, 0, 0, 206, 206,0.000000 }, - {3233,3233, 0, 0, 5686, 5686,0.000000 }, - {3234,3234, 0, 0, 326, 326,0.000000 }, - { 403, 403, 0, 0, 40000, 0,0.000000 }, - {3235,3235, 0, 0, 1913, 1913,0.000000 }, - {3236,3236, 0, 0, 2080, 2080,0.000000 }, - {3237,3237, 0, 0, 2026, 2026,0.000000 }, - {3238,3238, 0, 0, 1166, 1166,0.000000 }, - {3239,3239, 0, 0, 600, 600,0.000000 }, - {3240,3240, 0, 0, 1213, 1213,0.000000 }, - {3241,3241, 0, 0, 40000, 0,0.000000 }, - {3242,3242, 0, 0, 40000, 0,0.000000 }, - {3243,3243, 0, 0, 40000, 0,0.000000 }, - {3244,3244, 0, 0, 40000, 0,0.000000 }, - {3245,3245, 0, 0, 40000, 146,0.000000 }, - {3246,3246, 0, 0, 40000, 146,0.000000 }, - {3247,3247, 0, 0, 40000, 146,0.000000 }, - {3248,3248, 0, 0, 40000, 153,0.000000 }, - {3249,3249, 0, 0, 873, 873,0.000000 }, - {3250,3250, 0, 0, 553, 553,0.000000 }, - {3251,3251, 0, 0, 873, 873,0.000000 }, - {3252,3252, 0, 0, 606, 606,0.000000 }, - {3253,3253, 0, 0, 606, 606,0.000000 }, - {3254,3254, 0, 0, 600, 600,0.000000 }, - {3255,3255, 0, 0, 1013, 1013,0.000000 }, - {3256,3256, 0, 0, 626, 626,0.000000 }, - {3257,3257, 0, 0, 40000, 0,0.000000 }, - {3258,3258, 0, 0, 40000, 0,0.000000 }, - {3259,3259, 0, 0, 40000, 66,0.000000 }, - {3260,3260, 0, 0, 40000, 66,0.000000 }, - {3261,3261, 0, 0, 40000, 20,0.000000 }, - {3262,3262, 0, 0, 1120, 1120,0.000000 }, - {3263,3263, 0, 0, 1206, 1206,0.000000 }, - {3264,3264, 0, 0, 940, 940,0.000000 }, - {3265,3265, 0, 0, 3626, 3626,0.000000 }, - {3266,3266, 0, 0, 40000, 320,0.000000 }, - {3267,3267, 0, 0, 40000, 6,0.000000 }, - {3268,3268, 0, 0, 3926, 3926,0.000000 }, - {3269,3269, 0, 0, 40000, 446,0.000000 }, - {3270,3270, 0, 0, 40000, 180,0.000000 }, - {3271,3271, 0, 0, 3426, 3426,0.000000 }, - {3272,3272, 0, 0, 2253, 2253,0.000000 }, - {3273,3273, 0, 0, 40000, 2133,0.000000 }, - {3274,3274, 0, 0, 2566, 2566,0.000000 }, - {3275,3275, 0, 0, 166, 26,0.000000 }, - {3276,3276, 0, 0, 40000, 346,0.000000 }, - {3277,3277, 0, 0, 40000, 66,0.000000 }, - {3278,3278, 0, 0, 40000, 213,0.000000 }, - {3279,3279, 0, 0, 2273, 2273,0.000000 }, - {3280,3280, 0, 0, 40000, 40,0.000000 }, - {3281,3281, 0, 0, 40000, 906,0.000000 }, - {3282,3282, 0, 0, 40000, 213,0.000000 }, - {3283,3283, 0, 0, 40000, 186,0.000000 }, - {3284,3284, 0, 0, 60, 60,0.000000 }, - {3285,3285, 0, 0, 146, 146,0.000000 }, - {3286,3286, 0, 0, 146, 13,0.000000 }, - {3287,3287, 0, 0, 786, 786,0.000000 }, - {3288,3288, 0, 0, 40000, 26,0.000000 }, - {3289,3289, 0, 0, 1580, 1580,0.000000 }, - {3290,3290, 0, 0, 653, 653,0.000000 }, - {3291,3291, 0, 0, 2400, 2400,0.000000 }, - {3292,3292, 0, 0, 1926, 1926,0.000000 }, - {3293,3293, 0, 0, 2593, 2593,0.000000 }, - {3294,3294, 0, 0, 2360, 2360,0.000000 }, - {3295,3295, 0, 0, 4933, 4933,0.000000 }, - {3296,3296, 0, 0, 593, 593,0.000000 }, - {3297,3297, 0, 0, 253, 253,0.000000 }, - {3298,3298, 0, 0, 306, 306,0.000000 }, - {3299,3299, 0, 0, 613, 613,0.000000 }, - {3300,3300, 0, 0, 1900, 1900,0.000000 }, - {3301,3301, 0, 0, 1213, 1213,0.000000 }, - {3302,3302, 0, 0, 273, 273,0.000000 }, - {3303,3303, 0, 0, 566, 566,0.000000 }, - {3304,3304, 0, 0, 40000, 6,0.000000 }, - {3305,3305, 0, 0, 40000, 6,0.000000 }, - {3306,3306, 0, 0, 40000, 6,0.000000 }, - {3307,3307, 0, 0, 40000, 6,0.000000 }, - {3308,3308, 0, 0, 40000, 6,0.000000 }, - {3309,3309, 0, 0, 40000, 0,0.000000 }, - {3310,3310, 0, 0, 40000, 0,0.000000 }, - {3311,3311, 0, 0, 40000, 0,0.000000 }, - {3312,3312, 0, 0, 40000, 6,0.000000 }, - {3313,3313, 0, 0, 40000, 53,0.000000 }, - {3314,3314, 0, 0, 40000, 73,0.000000 }, - {3315,3315, 0, 0, 40000, 0,0.000000 }, - {3316,3316, 0, 0, 40000, 0,0.000000 }, - {3317,3317, 0, 0, 40000, 0,0.000000 }, - {3318,3318, 0, 0, 40000, 80,0.000000 }, - {3319,3319, 0, 0, 40, 60,0.000000 }, - {3320,3320, 0, 0, 1320, 1320,0.000000 }, - {3321,3321, 0, 0, 40000, 126,0.000000 }, - {3322,3322, 0, 0, 273, 126,0.000000 }, - {3323,3323, 0, 0, 40000, 126,0.000000 }, - {3324,3324, 0, 0, 40000, 33,0.000000 }, - {3325,3325, 0, 0, 40000, 73,0.000000 }, - {3326,3326, 0, 0, 40, 40,0.000000 }, - {3327,3327, 0, 0, 40000, 233,0.000000 }, - {3328,3328, 0, 0, 40000, 233,0.000000 }, - {3329,3329, 0, 0, 593, 593,0.000000 }, - {3330,3330, 0, 0, 40000, 213,0.000000 }, - {3331,3331, 0, 0, 40000, 606,0.000000 }, - {3332,3332, 0, 0, 2253, 2253,0.000000 }, - {3333,3333, 0, 0, 140, 140,0.000000 }, - {3334,3334, 0, 0, 40000, 926,0.000000 }, - {3335,3335, 0, 0, 40000, 33,0.000000 }, - {3336,3336, 0, 0, 40000, 1120,0.000000 }, - {3337,3337, 0, 0, 160, 160,0.000000 }, - {3338,3338, 0, 0, 40000, 6,0.000000 }, - {3339,3339, 0, 0, 40000, 1093,0.000000 }, - {3340,3340, 0, 0, 160, 160,0.000000 }, - {3341,3341, 0, 0, 1206, 1206,0.000000 }, - {3342,3342, 0, 0, 146, 146,0.000000 }, - {3343,3343, 0, 0, 146, 146,0.000000 }, - {3344,3344, 0, 0, 140, 140,0.000000 }, - {3345,3345, 0, 0, 140, 140,0.000000 }, - {3346,3346, 0, 0, 140, 140,0.000000 }, - {3347,3347, 0, 0, 80, 80,0.000000 }, - {3348,3348, 0, 0, 40000, 33,0.000000 }, - {3349,3349, 0, 0, 1186, 1186,0.000000 }, - {3350,3350, 0, 0, 40000, 1413,0.000000 }, - {3351,3351, 0, 0, 1226, 1226,0.000000 }, - {3352,3352, 0, 0, 40, 40,0.000000 }, - {3353,3353, 0, 0, 60, 60,0.000000 }, - {3354,3354, 0, 0, 100, 100,0.000000 }, - {3355,3355, 0, 0, 246, 246,0.000000 }, - {3356,3356, 0, 0, 1140, 1140,0.000000 }, - { 142, 142, 20, 0, 20, 20,0.000000 }, - {3357,1451, 0, 0, 613, 613,0.000000 }, - {3358,3359, 0, 0, 1740, 1740,0.000000 }, - {3360,1455, 0, 0, 280, 280,0.000000 }, - {3361,1463, 0, 0, 40000, 133,0.000000 }, - { 225,3362, 0, 0, 2026, 2026,0.000000 }, - {3363,1545, 0, 0, 153, 153,0.000000 }, - {3364,1547, 0, 0, 40000, 60,0.000000 }, - {3365,3366, 39, 0, 13, 13,0.000000 }, - {3367, 368, 58, 0, 46, 46,0.000000 }, - {3368,1551, 48, 0, 93, 93,0.000000 }, - {3368,3033, 49, 0, 73, 73,0.000000 }, - {3368,3033, 51, 0, 153, 153,0.000000 }, - {3368,3033, 54, 0, 140, 140,0.000000 }, - {3368,3033, 57, 0, 126, 126,0.000000 }, - {3368,3033, 60, 0, 133, 133,0.000000 }, - {3369,3370, 70, 0, 893, 893,0.000000 }, - {1564,1565, 80, 0, 66, 66,0.000000 }, - {3371,1571, 44, 0, 520, 520,0.000000 }, - {3372,3372, 0, 0, 1486, 1486,0.000000 }, - {3373,3373, 0, 0, 1806, 1806,0.000000 }, - {3374,3374, 0, 0, 633, 633,0.000000 }, - {3375,3375, 0, 0, 1273, 1273,0.000000 }, - {3376,3376, 0, 0, 1553, 1553,0.000000 }, - {3377,3377, 0, 0, 1206, 1206,0.000000 }, - {3378,3378, 0, 0, 313, 313,0.000000 }, - {3379,3379, 0, 0, 1486, 1486,0.000000 }, - {3380,3380, 0, 0, 626, 626,0.000000 }, - {3381,3381, 0, 0, 1846, 1846,0.000000 }, - {3382,3382, 0, 0, 300, 300,0.000000 }, - {3383,3383, 0, 0, 1740, 1740,0.000000 }, - {3384,3384, 0, 0, 513, 513,0.000000 }, - {3385,3385, 0, 0, 46, 46,0.000000 }, - {3386,3386, 0, 0, 1226, 1226,0.000000 }, - {3387,3387, 0, 0, 333, 333,0.000000 }, - {3388,3388, 0, 0, 40000, 300,0.000000 }, - {3389,3389, 0, 0, 40000, 533,0.000000 }, - {3390,3390, 0, 0, 40000, 126,0.000000 }, - {3391,3391, 0, 0, 40000, 0,0.000000 }, - {3392,3392, 0, 0, 40000, 33,0.000000 }, - {3393,3393, 0, 0, 40000, 6,0.000000 }, - {3394,3394, 0, 0, 40000, 6,0.000000 }, - {3395,3395, 0, 0, 3880, 6,0.000000 }, - {3396,3396, 0, 0, 1066, 1066,0.000000 }, - {3397,3397, 0, 0, 4086, 4086,0.000000 }, - {3398,3398, 0, 0, 866, 866,0.000000 }, - {3399,3399, 0, 0, 466, 466,0.000000 }, - {3400,3400, 0, 0, 420, 420,0.000000 }, - {3401,3401, 0, 0, 40000, 73,0.000000 }, - {3402,3402, 0, 0, 40000, 20,0.000000 }, - {3403,3403, 0, 0, 40000, 26,0.000000 }, - {3404,3404, 0, 0, 4266, 4266,0.000000 }, - {3405,3405, 0, 0, 40000, 0,0.000000 }, - {3406,3406, 0, 0, 253, 253,0.000000 }, - {3407,3407, 0, 0, 380, 380,0.000000 }, - {3408,3408, 0, 0, 573, 573,0.000000 }, - {3409,3409, 0, 0, 280, 280,0.000000 }, - {3410,3410, 0, 0, 40000, 6,0.000000 }, - {3411,3411, 0, 0, 1933, 1933,0.000000 }, - {3412,3412, 0, 0, 40000, 140,0.000000 }, - {3413,3413, 0, 0, 40000, 140,0.000000 }, - {3414,3414, 0, 0, 40000, 40,0.000000 }, - {3415,3415, 0, 0, 353, 353,0.000000 }, - {3416,3416, 0, 0, 40000, 166,0.000000 }, - {3417,3417, 0, 0, 273, 273,0.000000 }, - {3418,3418, 0, 0, 126, 126,0.000000 }, - {3419,3419, 0, 0, 146, 146,0.000000 }, - {3420,3420, 0, 0, 40000, 213,0.000000 }, - {3421,3421, 0, 0, 40000, 173,0.000000 }, - {3422,3422, 0, 0, 40000, 206,0.000000 }, - {3423,3423, 0, 0, 40000, 213,0.000000 }, - {3424,3424, 0, 0, 40000, 153,0.000000 }, - {3425,3425, 0, 0, 40000, 106,0.000000 }, - {3426,3426, 0, 0, 40000, 6,0.000000 }, - {3427,3427, 0, 0, 233, 233,0.000000 }, - {3428,3428, 0, 0, 40000, 6,0.000000 }, - {3429,3429, 0, 0, 11193, 13,0.000000 }, - {3430,3430, 0, 0, 40000, 126,0.000000 }, - {3431,3431, 0, 0, 40000, 13,0.000000 }, - {3432,3432, 0, 0, 40000, 6,0.000000 }, - {3433,3433, 0, 0, 40000, 0,0.000000 }, - {3434,3434, 0, 0, 40000, 0,0.000000 }, - {3435,3435, 0, 0, 40000, 0,0.000000 }, - {3436,3436, 0, 0, 40000, 0,0.000000 }, - {3437,3437, 0, 0, 40000, 0,0.000000 }, - {3438,3438, 0, 0, 40000, 0,0.000000 }, - {3439,3439, 0, 0, 6013, 6013,0.000000 }, - {3440,3440, 0, 0, 40000, 0,0.000000 }, - {3441,3441, 0, 0, 40000, 0,0.000000 }, - {3442,3442, 0, 0, 293, 293,0.000000 }, - {3443,3443, 0, 0, 40000, 6,0.000000 }, - {3444,3444, 0, 0, 40000, 20,0.000000 }, - {3445,3445, 0, 0, 40000, 0,0.000000 }, - {3446,3446, 0, 0, 40000, 0,0.000000 }, - {3447,3447, 0, 0, 40000, 0,0.000000 }, - {3448,3448, 0, 0, 40000, 0,0.000000 }, - {3449,3449, 0, 0, 40000, 20,0.000000 }, - {3450,3450, 0, 0, 40000, 153,0.000000 }, - {3451,3451, 0, 0, 440, 440,0.000000 }, - {3452,3452, 0, 0, 3253, 3253,0.000000 }, - {3453,3453, 0, 0, 3946, 3946,0.000000 }, - {3454,3454, 0, 0, 140, 140,0.000000 }, - {3455,3455, 0, 0, 40000, 20,0.000000 }, - {3456,3456, 0, 0, 40000, 33,0.000000 }, - {3457,3457, 0, 0, 40000, 20,0.000000 }, - {3458,3458, 0, 0, 40000, 26,0.000000 }, - {3459,3459, 0, 0, 40000, 20,0.000000 }, - {3460,3460, 0, 0, 786, 786,0.000000 }, - {3461,3461, 0, 0, 2560, 2560,0.000000 }, - {3462,3462, 0, 0, 40000, 26,0.000000 }, - {3463,3463, 0, 0, 40000, 580,0.000000 }, - {3464,3464, 0, 0, 40000, 2393,0.000000 }, - {3465,3465, 0, 0, 40000, 53,0.000000 }, - {3466,3466, 0, 0, 40000, 33,0.000000 }, - {3467,3467, 0, 0, 40000, 193,0.000000 }, - {3468,3468, 0, 0, 40000, 280,0.000000 }, - {3469,3469, 0, 0, 40000, 86,0.000000 }, - {3470,3470, 0, 0, 40000, 820,0.000000 }, - {3471,3471, 0, 0, 40000, 286,0.000000 }, - {3472,3472, 0, 0, 40000, 1100,0.000000 }, - {3473,3473, 0, 0, 40000, 1073,0.000000 }, - {3474,3474, 0, 0, 40000, 1173,0.000000 }, - {3475,3475, 0, 0, 340, 340,0.000000 }, - {3476,3476, 0, 0, 4846, 4846,0.000000 }, - {3477,3477, 0, 0, 40000, 0,0.000000 }, - {3478,3478, 0, 0, 4846, 4846,0.000000 }, - {3479,3479, 0, 0, 606, 606,0.000000 }, - {3480,3480, 0, 0, 106, 106,0.000000 }, - {3481,3481, 0, 0, 40000, 0,0.000000 }, - {3482,3482, 0, 0, 40000, 73,0.000000 }, - {3483,3483, 0, 0, 40000, 0,0.000000 }, - {3484,3484, 0, 0, 586, 586,0.000000 }, - {3485,3485, 0, 0, 73, 73,0.000000 }, - {3486,3486, 0, 0, 460, 460,0.000000 }, - {3487,3487, 0, 0, 40, 40,0.000000 }, - {3488,3488, 0, 0, 526, 526,0.000000 }, - {3489,3489, 0, 0, 540, 540,0.000000 }, - {3490,3490, 0, 0, 253, 253,0.000000 }, - {3491,3491, 0, 0, 2280, 2280,0.000000 }, - {3492,3492, 0, 0, 73, 73,0.000000 }, - {3493,3493, 0, 0, 40000, 73,0.000000 }, - {3494,3494, 0, 0, 40000, 2220,0.000000 }, - {3495,3495, 0, 0, 146, 146,0.000000 }, - {3496,3496, 0, 0, 40000, 13,0.000000 }, - {3497,3497, 0, 0, 380, 13,0.000000 }, - {3498,3498, 0, 0, 40000, 2220,0.000000 }, - {3499,3499, 0, 0, 40000, 146,0.000000 }, - {3500,3500, 0, 0, 1513, 13,0.000000 }, - { 739, 739, 46, 0, 53, 53,0.000000 }, - {3501,3501, 47, 0, 106, 106,0.000000 }, - {3502,3502, 64, 0, 26, 26,0.000000 }, - {3503,3503, 40, 0, 106, 106,0.000000 }, - {3504,3504, 48, 0, 13, 13,0.000000 }, - {3505,3505, 48, 0, 80, 80,0.000000 }, - {3506,3506, 46, 0, 86, 86,0.000000 }, - {3507,3507,111, 0, 33, 33,0.000000 }, - {3508,3508, 49, 0, 140, 140,0.000000 }, - {3509,3509, 56, 0, 33, 33,0.000000 }, - {3510,3510, 52, 0, 140, 140,0.000000 }, - {3511,3511, 96, 0, 440, 440,0.000000 }, - {3510,3510, 54, 0, 140, 140,0.000000 }, - {3512,3512, 57, 0, 260, 260,0.000000 }, - {3513,3513, 82, 0, 486, 486,0.000000 }, - {3510,3510, 60, 0, 153, 153,0.000000 }, - {3514,3514, 60, 0, 606, 606,0.000000 }, - {3515,3515, 92, 0, 60, 60,0.000000 }, - {3516,3516, 60, 0, 46, 46,0.000000 }, - {3517,3517, 58, 0, 40, 40,0.000000 }, - {3518,3518, 22, 0, 53, 53,0.000000 }, - {3519,3519, 60, 0, 613, 613,0.000000 }, - {3520,3520, 72, 0, 80, 80,0.000000 }, - {3521,3521, 77, 0, 80, 80,0.000000 }, - {3522,3522, 70, 0, 73, 73,0.000000 }, - {3523,3523, 75, 0, 60, 60,0.000000 }, - {3524,3524, 69, 0, 140, 140,0.000000 }, - {3525,3525, 59, 0, 113, 113,0.000000 }, - {3526,3526, 48, 0, 153, 153,0.000000 }, - {3527,3527, 89, 0, 133, 133,0.000000 }, - {3528,3528, 84, 0, 260, 260,0.000000 }, - {3529,3529, 33, 0, 93, 93,0.000000 }, - {3530,3530, 55, 0, 86, 86,0.000000 }, - {3531,3531, 58, 0, 186, 186,0.000000 }, - {3532,3532, 52, 0, 166, 166,0.000000 }, - {3533,3533, 57, 0, 46, 46,0.000000 }, - {3534,3534, 57, 0, 73, 73,0.000000 }, - {3535,3535, 85, 0, 60, 60,0.000000 }, - {3536,3536, 68, 0, 40, 40,0.000000 }, - {3536,3536, 61, 0, 60, 60,0.000000 }, - {3537,3537, 64, 0, 93, 93,0.000000 }, - {3538,3538, 44, 0, 386, 386,0.000000 }, - {3539,3539,100, 0, 80, 80,0.000000 }, - {3540,3540,100, 0, 306, 306,0.000000 }, - {3541,3541, 0, 0, 1400, 1400,0.000000 }, - {3542,3542, 0, 0, 253, 253,0.000000 }, - {3543,3543, 0, 0, 460, 460,0.000000 }, - {3544,3544, 0, 0, 1226, 1226,0.000000 }, - {3545,3545, 0, 0, 886, 886,0.000000 }, - {3546,3546, 0, 0, 40, 40,0.000000 }, - {3547,3547, 0, 0, 2406, 2406,0.000000 }, - {3548,3548, 0, 0, 1180, 1180,0.000000 }, - {3549,3549, 0, 0, 40000, 0,0.000000 }, - {3550,3550, 0, 0, 40000, 0,0.000000 }, - {3551,3551, 0, 0, 40000, 33,0.000000 }, - {3552,3552, 0, 0, 40000, 0,0.000000 }, - {3553,3553, 0, 0, 40000, 6,0.000000 }, - {3554,3554, 0, 0, 1553, 1553,0.000000 }, - {3555,3555, 0, 0, 40000, 0,0.000000 }, - {3556,3556, 0, 0, 573, 573,0.000000 }, - {3557,3557, 0, 0, 526, 526,0.000000 }, - {3558,3558, 0, 0, 940, 940,0.000000 }, - {3559,3559, 0, 0, 606, 606,0.000000 }, - {3560,3560, 0, 0, 40000, 6,0.000000 }, - {3561,3561, 0, 0, 40000, 0,0.000000 }, - {3562,3562, 0, 0, 1120, 1120,0.000000 }, - {3563,3563, 0, 0, 700, 700,0.000000 }, - {3564,3564, 0, 0, 66, 66,0.000000 }, - {3565,3565, 0, 0, 620, 620,0.000000 }, - {3566,3566, 0, 0, 286, 286,0.000000 }, - {3567,3567, 0, 0, 40000, 73,0.000000 }, - {3568,3568, 0, 0, 80, 80,0.000000 }, - {3569,3569, 0, 0, 186, 186,0.000000 }, - {3570,3570, 0, 0, 1506, 1506,0.000000 }, - {3571,3571, 0, 0, 40000, 0,0.000000 }, - {3572,3572, 0, 0, 40000, 186,0.000000 }, - {3573,3573, 0, 0, 126, 126,0.000000 }, - {3574,3574, 0, 0, 40000, 106,0.000000 }, - {3575,3575, 0, 0, 166, 166,0.000000 }, - {3576,3576, 0, 0, 80, 80,0.000000 }, - {3577,3577, 0, 0, 453, 13,0.000000 }, - {3578,3578, 0, 0, 40000, 0,0.000000 }, - {3579,3579, 0, 0, 40000, 6,0.000000 }, - {3580,3580, 0, 0, 2020, 2020,0.000000 }, - {3581,3581, 0, 0, 1646, 1646,0.000000 }, - {3582,3582, 0, 0, 166, 166,0.000000 }, - {3583,3583, 0, 0, 40000, 0,0.000000 }, - {3584,3584, 0, 0, 146, 146,0.000000 }, - {3585,3585, 0, 0, 340, 340,0.000000 }, - {3586,3586, 0, 0, 40000, 0,0.000000 }, - {3587,3587, 0, 0, 100, 13,0.000000 }, - {3588,3588, 0, 0, 5013, 5013,0.000000 }, - {3589,3589, 0, 0, 40000, 6,0.000000 }, - {3590,3590, 0, 0, 1766, 1766,0.000000 }, - {3591,3591, 0, 0, 4186, 4186,0.000000 }, - {3592,3592, 0, 0, 700, 700,0.000000 }, - {3593,3593, 0, 0, 940, 940,0.000000 }, - {3594,3594, 0, 0, 246, 246,0.000000 }, - {3595,3595, 0, 0, 40000, 6,0.000000 }, - {3596,3596, 0, 0, 40000, 6,0.000000 }, - {3597,3597, 0, 0, 40000, 6,0.000000 }, - {3598,3598, 0, 0, 40000, 0,0.000000 }, - {3599,3599, 0, 0, 40000, 306,0.000000 }, - {3600,3600, 0, 0, 40000, 53,0.000000 }, - {3601,3601, 0, 0, 40000, 6,0.000000 }, - {3602,3602, 0, 0, 40000, 233,0.000000 }, - {3603,3603, 0, 0, 120, 120,0.000000 }, - {3604,3604, 0, 0, 40000, 6,0.000000 }, - {3605,3605, 0, 0, 40000, 153,0.000000 }, - {3606,3606, 0, 0, 1086, 1086,0.000000 }, - {3607,3607, 0, 0, 1900, 1900,0.000000 }, - {3608,3608, 0, 0, 40000, 0,0.000000 }, - {3609,3609, 0, 0, 40000, 13,0.000000 }, - {3610,3610, 0, 0, 253, 253,0.000000 }, - {3611,3611, 0, 0, 40000, 6,0.000000 }, - {3612,3612, 0, 0, 40000, 126,0.000000 }, - {3613,3613, 0, 0, 3680, 3680,0.000000 }, - {3614,3614, 0, 0, 660, 660,0.000000 }, - {3615,3615, 0, 0, 40000, 206,0.000000 }, - {3616,3616, 0, 0, 40000, 106,0.000000 }, - {3617,3617, 0, 0, 546, 546,0.000000 }, - {3618,3618, 0, 0, 126, 6,0.000000 }, - {3619,3619, 0, 0, 40000, 1306,0.000000 }, - {3620,3620, 0, 0, 1240, 1240,0.000000 }, - {3621,3621, 0, 0, 1400, 1400,0.000000 }, - {3622,3622, 0, 0, 40000, 0,0.000000 }, - {3623,3623, 0, 0, 40000, 0,0.000000 }, - {3624,3624, 0, 0, 40000, 833,0.000000 }, - {3625,3625, 0, 0, 40000, 240,0.000000 }, - {3626,3626, 0, 0, 40000, 1973,0.000000 }, - {3627,3627, 0, 0, 4466, 4466,0.000000 }, - {3628,3628, 0, 0, 13, 13,0.000000 }, - {3629,3629, 0, 0, 40000, 2206,0.000000 }, - {3630,3630, 0, 0, 40000, 1180,0.000000 }, - {3631,3631, 0, 0, 4933, 4933,0.000000 }, - {3632,3632, 0, 0, 200, 200,0.000000 }, - {3633,3633, 0, 0, 1226, 1226,0.000000 }, - {3634,3634, 0, 0, 273, 273,0.000000 }, - {3635,3635, 0, 0, 353, 13,0.000000 }, - {3636,3636, 0, 0, 40000, 6,0.000000 }, - {3637,3637, 0, 0, 60, 60,0.000000 }, - {3638,3638, 0, 0, 160, 160,0.000000 }, - {3639,3639, 0, 0, 313, 313,0.000000 }, - {3640,3640, 0, 0, 46, 46,0.000000 }, - {3641,3641, 0, 0, 313, 313,0.000000 }, - {3642,3642, 0, 0, 40, 40,0.000000 }, - {3643,3643, 0, 0, 86, 86,0.000000 }, - {3644,3644, 0, 0, 46, 46,0.000000 }, - {3645,3645, 0, 0, 146, 146,0.000000 }, - {3646,3646, 0, 0, 146, 146,0.000000 }, - {3647,3647, 0, 0, 220, 220,0.000000 }, - {3648,3648, 0, 0, 86, 86,0.000000 }, - {3649,3649, 0, 0, 40000, 320,0.000000 }, - {3650,3650, 0, 0, 40000, 6,0.000000 }, - {3651,3651, 0, 0, 40000, 0,0.000000 }, - {3652,3652, 0, 0, 300, 300,0.000000 }, - {3653,3653, 0, 0, 2940, 2940,0.000000 }, - {3654,3654, 0, 0, 1233, 1233,0.000000 }, - {3655,3655, 0, 0, 3840, 3840,0.000000 }, - {3656,3656, 0, 0, 2280, 2280,0.000000 }, - {3657,3657, 0, 0, 1793, 1793,0.000000 }, - {3658,3658, 0, 0, 1120, 1120,0.000000 }, - {3659,3659, 0, 0, 1013, 1013,0.000000 }, - {3660,3660, 0, 0, 1233, 1233,0.000000 }, - {3661,3661, 0, 0, 600, 600,0.000000 }, - {3662,3662, 0, 0, 280, 280,0.000000 }, - {3663,3663, 0, 0, 266, 266,0.000000 }, - {3664,3664, 0, 0, 1740, 1740,0.000000 }, - {3665,3665, 0, 0, 246, 246,0.000000 }, - {3666,3666, 0, 0, 140, 140,0.000000 }, - {3667,3667, 0, 0, 1166, 1166,0.000000 }, - {3668,3668, 0, 0, 273, 273,0.000000 }, - {3669,3669, 0, 0, 40000, 0,0.000000 }, - {3670,3670, 0, 0, 40000, 0,0.000000 }, - {3671,3671, 0, 0, 313, 313,0.000000 }, - {3672,3672, 0, 0, 40000, 6,0.000000 }, - {3673,3673, 0, 0, 40000, 133,0.000000 }, - {3674,3674, 0, 0, 40000, 133,0.000000 }, - {3675,3675, 0, 0, 40000, 66,0.000000 }, - {3676,3676, 0, 0, 40000, 133,0.000000 }, - {3677,3677, 0, 0, 940, 940,0.000000 }, - {3678,3678, 0, 0, 760, 760,0.000000 }, - {3679,3679, 0, 0, 1200, 1200,0.000000 }, - {3680,3680, 0, 0, 566, 566,0.000000 }, - {3681,3681, 0, 0, 573, 573,0.000000 }, - {3682,3682, 0, 0, 40000, 0,0.000000 }, - {3683,3683, 0, 0, 1353, 1353,0.000000 }, - {3684,3684, 0, 0, 2226, 2226,0.000000 }, - {3685,3685, 0, 0, 866, 866,0.000000 }, - {3686,3686, 0, 0, 2293, 2293,0.000000 }, - {3687,3687, 0, 0, 2113, 2113,0.000000 }, - {3688,3688, 0, 0, 1280, 1280,0.000000 }, - {3689,3689, 0, 0, 886, 886,0.000000 }, - {3690,3690, 0, 0, 833, 833,0.000000 }, - {3691,3691, 0, 0, 886, 886,0.000000 }, - {3692,3692, 0, 0, 940, 940,0.000000 }, - {3693,3693, 0, 0, 40000, 260,0.000000 }, - {3694,3694, 0, 0, 166, 13,0.000000 }, - {3695,3695, 0, 0, 40000, 26,0.000000 }, - {3696,3696, 0, 0, 146, 146,0.000000 }, - {3697,3697, 0, 0, 40000, 86,0.000000 }, - {3698,3698, 0, 0, 940, 940,0.000000 }, - {3699,3699, 0, 0, 100, 100,0.000000 }, - {3700,3700, 0, 0, 513, 513,0.000000 }, - {3701,3701, 0, 0, 40000, 286,0.000000 }, - {3702,3702, 0, 0, 40000, 133,0.000000 }, - {3703,3703, 0, 0, 40000, 233,0.000000 }, - {3704,3704, 0, 0, 40000, 40,0.000000 }, - {3705,3705, 0, 0, 3413, 3413,0.000000 }, - {3706,3706, 0, 0, 3413, 3413,0.000000 }, - {3707,3707, 0, 0, 253, 253,0.000000 }, - {3708,3708, 0, 0, 60, 73,0.000000 }, - {3709,3709, 0, 0, 40000, 66,0.000000 }, - {3710,3710, 0, 0, 40000, 33,0.000000 }, - {3711,3711, 0, 0, 40000, 0,0.000000 }, - {3712,3712, 0, 0, 40000, 126,0.000000 }, - {3713,3713, 0, 0, 40000, 6,0.000000 }, - {3714,3714, 0, 0, 40000, 0,0.000000 }, - {3715,3715, 0, 0, 40000, 126,0.000000 }, - {3716,3716, 0, 0, 40000, 26,0.000000 }, - {3717,3717, 0, 0, 40000, 20,0.000000 }, - {3718,3718, 0, 0, 40000, 20,0.000000 }, - {3719,3719, 0, 0, 40000, 0,0.000000 }, - {3720,3720, 0, 0, 40000, 0,0.000000 }, - {3721,3721, 0, 0, 40000, 6,0.000000 }, - {3722,3722, 0, 0, 40000, 33,0.000000 }, - {3723,3723, 0, 0, 40000, 6,0.000000 }, - {3724,3724, 0, 0, 40000, 6,0.000000 }, - {3725,3725, 0, 0, 40000, 20,0.000000 }, - {3726,3726, 0, 0, 40000, 33,0.000000 }, - {3727,3727, 0, 0, 100, 100,0.000000 }, - {3728,3728, 0, 0, 40000, 26,0.000000 }, - {3729,3729, 0, 0, 40000, 26,0.000000 }, - {3730,3730, 0, 0, 40000, 53,0.000000 }, - {3731,3731, 0, 0, 40000, 0,0.000000 }, - {3732,3732, 0, 0, 40000, 0,0.000000 }, - {3733,3733, 0, 0, 40000, 26,0.000000 }, - {3734,3734, 0, 0, 40000, 13,0.000000 }, - {3735,3735, 0, 0, 40000, 6,0.000000 }, - {3736,3736, 0, 0, 3766, 3766,0.000000 }, - {3737,3737, 0, 0, 40000, 140,0.000000 }, - {3738,3738, 0, 0, 40000, 13,0.000000 }, - {3739,3739, 0, 0, 40000, 233,0.000000 }, - {3740,3740, 0, 0, 40000, 413,0.000000 }, - {3741,3741, 0, 0, 1873, 26,0.000000 }, - {3742,3742, 0, 0, 40000, 566,0.000000 }, - {3743,3743, 0, 0, 753, 753,0.000000 }, - {3744,3744, 0, 0, 1166, 13,0.000000 }, - {3745,3745, 0, 0, 40000, 566,0.000000 }, - {3746,3746, 0, 0, 40000, 53,0.000000 }, - {3747,3747, 0, 0, 1020, 1020,0.000000 }, - {3748,3748, 0, 0, 40000, 340,0.000000 }, - {3749,3749, 0, 0, 440, 440,0.000000 }, - {3750,3750, 0, 0, 40000, 106,0.000000 }, - {3751,3751, 0, 0, 2286, 2286,0.000000 }, - {3752,3752, 0, 0, 40000, 966,0.000000 }, - {3753,3753, 0, 0, 40000, 766,0.000000 }, - {3754,3754, 0, 0, 40000, 6,0.000000 }, - {3755,3755, 0, 0, 4933, 4933,0.000000 }, - {3756,3756, 0, 0, 1126, 1126,0.000000 }, - {3757,3757, 0, 0, 273, 273,0.000000 }, - {3758,3758, 0, 0, 546, 546,0.000000 }, - {3759,3759, 0, 0, 280, 280,0.000000 }, - {3760,3760, 0, 0, 40000, 6,0.000000 }, - {3761,3761, 0, 0, 40000, 6,0.000000 }, - {3762,3762, 0, 0, 40000, 0,0.000000 }, - {3763,3763, 0, 0, 1106, 1106,0.000000 }, - {3764,3764, 0, 0, 86, 86,0.000000 }, - {3765,3765, 0, 0, 280, 280,0.000000 }, - {3766,3766, 0, 0, 53, 53,0.000000 }, - {3767,3767, 0, 0, 40, 40,0.000000 }, - {3768,3768, 0, 0, 60, 60,0.000000 }, - {3769,3769, 0, 0, 253, 253,0.000000 }, - {3770,3770, 0, 0, 1213, 1213,0.000000 }, - {3771,3771, 0, 0, 293, 293,0.000000 }, - {3772,3772, 0, 0, 153, 153,0.000000 }, - {3773,3773, 0, 0, 4586, 4586,0.000000 }, - {3774,3774, 0, 0, 146, 146,0.000000 }, - {3775,3775, 0, 0, 40000, 0,0.000000 }, - {3776,3776, 0, 0, 146, 146,0.000000 }, - {3777,3777, 0, 0, 40000, 113,0.000000 }, - {3778,3778, 0, 0, 153, 153,0.000000 }, - {3779,3779, 0, 0, 1726, 13,0.000000 }, - { 738, 738, 44, 0, 40, 40,0.000000 }, - {3780,3780, 36, 0, 33, 33,0.000000 }, - {3781,3781, 32, 0, 6, 6,0.000000 }, - {2030,2030, 60, 0, 86, 86,0.000000 }, - {3782,3782, 24, 0, 6, 6,0.000000 }, - {3783,3783, 60, 0, 26, 26,0.000000 }, - {3784,3784, 44, 0, 140, 140,0.000000 }, - { 132, 132, 44, 0, 20, 20,0.000000 }, - {3785,3785, 47, 0, 153, 153,0.000000 }, - { 152, 152, 44, 0, 86, 86,0.000000 }, - {3784,3784, 50, 0, 160, 160,0.000000 }, - { 139, 139, 44, 0, 86, 86,0.000000 }, - {3784,3784, 54, 0, 160, 160,0.000000 }, - {3784,3784, 57, 0, 160, 160,0.000000 }, - {3786,3786, 60, 0, 606, 606,0.000000 }, - {3784,3784, 60, 0, 166, 166,0.000000 }, - {3787,3787, 60, 0, 606, 606,0.000000 }, - {3788,3788, 60, 0, 626, 626,0.000000 }, - {3789,3789, 60, 0, 626, 626,0.000000 }, - {3790,3790, 60, 0, 606, 606,0.000000 }, - {3791,3791, 44, 0, 300, 300,0.000000 }, - {2037,2037, 44, 0, 40, 40,0.000000 }, - { 144, 144, 44, 0, 40, 40,0.000000 }, - {2038,2038, 44, 0, 13, 13,0.000000 }, - {3792,3792, 44, 0, 33, 33,0.000000 }, - {3793,3793, 44, 0, 60, 60,0.000000 }, - {3794,3794, 45, 0, 80, 80,0.000000 }, - {3795,3795, 33, 0, 100, 100,0.000000 }, - {3796,3796, 56, 0, 153, 153,0.000000 }, - {3796,3796, 51, 0, 153, 153,0.000000 }, - {3797,3797, 44, 0, 40, 40,0.000000 }, - {3798,3798, 44, 0, 200, 200,0.000000 }, - {3534,3534, 56, 0, 73, 73,0.000000 }, - { 158, 158, 68, 0, 20, 20,0.000000 }, - {3799,3799, 51, 0, 160, 160,0.000000 }, - {3800,3800, 46, 0, 153, 153,0.000000 }, - {3801,3801, 44, 0, 160, 160,0.000000 }, - {3802,3802, 44, 0, 1233, 1233,0.000000 }, - { 152, 152, 45, 0, 80, 80,0.000000 }, - {3803,3803, 0, 0, 40000, 20,0.000000 }, - {3804,3804, 0, 0, 40000, 66,0.000000 }, - {3805,3805, 0, 0, 40000, 0,0.000000 }, - {3806,3806, 0, 0, 40000, 0,0.000000 }, - {3807,3807, 0, 0, 40000, 20,0.000000 }, - {3808,3808, 0, 0, 1226, 1226,0.000000 }, - {3809,3809, 0, 0, 546, 546,0.000000 }, - {3810,3810, 0, 0, 280, 280,0.000000 }, - {3811,3811, 0, 0, 60, 60,0.000000 }, - {3812,3812, 0, 0, 1213, 1213,0.000000 }, - {3780,3780, 45, 0, 440, 440,0.000000 }, - {3061,3061, 45, 0, 606, 606,0.000000 }, - {3813,3813, 60, 0, 20, 20,0.000000 }, - {3781,3781, 60, 0, 6, 6,0.000000 }, - {3814,3814, 44, 0, 173, 173,0.000000 }, - {3815,3815, 57, 0, 46, 46,0.000000 }, - {3816,3816, 56, 0, 73, 73,0.000000 }, - {3817,3817, 60, 0, 20, 20,0.000000 }, - {3818,3818, 60, 0, 20, 20,0.000000 }, - {3517,3517, 45, 0, 40, 40,0.000000 }, - {3819,3819, 0, 0, 820, 820,0.000000 }, - {3820,3820, 0, 0, 573, 573,0.000000 }, - {3821,3821, 0, 0, 473, 473,0.000000 }, - {3822,3822, 0, 0, 1020, 1020,0.000000 }, - {3823,3823, 0, 0, 40000, 393,0.000000 }, - {3824,3825, 0, 1, 40000, 266,0.078125 }, - {3826,3826, 0, 0, 3833, 13,0.000000 }, - {3827,3827, 0, 0, 40000, 406,0.000000 }, - {3828,1172, 0, 1, 40000, 320,0.156250 }, - {3829,3829, 0, 0, 40000, 13,0.000000 }, - {3830,3830, 0, 0, 120, 120,0.000000 }, - {3831,3831, 32, 0, 20, 20,0.000000 }, - {3832,3832, 36, 0, 46, 46,0.000000 }, - {3833,3833, 88, 0, 93, 93,0.000000 }, - {3834,3834, 0, 0, 1660, 1660,0.000000 }, - {3835,3835, 0, 0, 1746, 1746,0.000000 }, - {3836,3836, 0, 0, 1980, 1980,0.000000 }, - {3837,3837, 0, 0, 1553, 1553,0.000000 }, - {3838,3838, 0, 0, 1233, 1233,0.000000 }, - {3839,3839, 0, 0, 1980, 1980,0.000000 }, - {3840,3840, 0, 0, 940, 940,0.000000 }, - {3841,3841, 0, 0, 1740, 1740,0.000000 }, - {3842,3842, 0, 0, 40000, 133,0.000000 }, - {3843,3843, 0, 0, 40000, 26,0.000000 }, - {3844,3844, 0, 0, 40000, 60,0.000000 }, - {3845,3845, 0, 0, 40000, 320,0.000000 }, - {3846,3846, 0, 0, 40000, 53,0.000000 }, - {3847,3847, 0, 0, 40000, 160,0.000000 }, - {3848,3848, 0, 0, 40000, 106,0.000000 }, - {3849,3849, 0, 0, 1026, 1026,0.000000 }, - {3850,3850, 0, 0, 1826, 1826,0.000000 }, - {3851,3851, 0, 0, 1813, 1813,0.000000 }, - {3852,3852, 0, 0, 1013, 1013,0.000000 }, - {3853,3853, 0, 0, 40000, 0,0.000000 }, - {3854,3854, 0, 0, 40000, 53,0.000000 }, - {3855,3855, 0, 0, 40000, 53,0.000000 }, - {3856,3856, 0, 0, 4200, 4200,0.000000 }, - {3857,3857, 0, 0, 40000, 106,0.000000 }, - {3858,3858, 0, 0, 1746, 1746,0.000000 }, - {3859,3859, 0, 0, 40000, 120,0.000000 }, - {3860,3860, 0, 0, 2400, 2400,0.000000 }, - {3861,3861, 0, 0, 4606, 4606,0.000000 }, - {3862,3862, 0, 0, 1740, 1740,0.000000 }, - {3863,3863, 0, 0, 40000, 66,0.000000 }, - {3864,3864, 0, 0, 40000, 46,0.000000 }, - {3865,3865, 0, 0, 40000, 66,0.000000 }, - {3866,3866, 0, 0, 40000, 26,0.000000 }, - {3867,3867, 0, 0, 40000, 293,0.000000 }, - {3868,3868, 0, 0, 40000, 133,0.000000 }, - {3869,3869, 0, 0, 333, 333,0.000000 }, - {3870,3870, 0, 0, 993, 993,0.000000 }, - {3871,3871, 0, 0, 40000, 280,0.000000 }, - {3872,3872, 0, 0, 1766, 13,0.000000 }, - {3873,3873, 0, 0, 40000, 820,0.000000 }, - {3874,3874, 0, 0, 40000, 286,0.000000 }, - {3875,3875, 0, 0, 40000, 580,0.000000 }, - {3876,3876, 0, 0, 2300, 2300,0.000000 }, - {3877,3877, 0, 0, 40000, 146,0.000000 }, - {3878,3878, 0, 0, 40000, 6,0.000000 }, - {3879,3879, 0, 0, 40000, 66,0.000000 }, - {3880,3880, 0, 0, 40000, 6,0.000000 }, - {3881,3881, 0, 0, 40000, 6,0.000000 }, - {3882,3882, 0, 0, 40000, 126,0.000000 }, - {3883,3883, 0, 0, 40000, 33,0.000000 }, - {3884,3884, 0, 0, 40000, 53,0.000000 }, - {3885,3885, 0, 0, 40000, 93,0.000000 }, - {3886,3886, 0, 0, 40000, 73,0.000000 }, - {3887,3887, 0, 0, 40000, 66,0.000000 }, - {3888,3888, 0, 0, 40000, 86,0.000000 }, - {3889,3889, 0, 0, 40000, 46,0.000000 }, - {3890,3890, 0, 0, 3940, 13,0.000000 }, - {3891,3891, 0, 0, 40000, 33,0.000000 }, - {3892,3892, 0, 0, 40000, 53,0.000000 }, - {3893,3893, 0, 0, 40000, 53,0.000000 }, - {3894,3894, 0, 0, 40000, 73,0.000000 }, - {3895,3895, 0, 0, 40000, 126,0.000000 }, - {3896,3896, 0, 0, 40000, 60,0.000000 }, - {3897,3897, 0, 0, 40000, 106,0.000000 }, - {3898,3898, 0, 0, 40000, 6,0.000000 }, - {3899,3899, 0, 0, 40000, 266,0.000000 }, - {3900,3900, 0, 0, 40000, 53,0.000000 }, - {3901,3901, 0, 0, 40000, 233,0.000000 }, - {3902,3902, 0, 0, 1660, 180,0.000000 }, - {3903,3903, 0, 0, 40000, 566,0.000000 }, - {3904,3904, 0, 0, 40000, 100,0.000000 }, - {3905,3905, 0, 0, 593, 13,0.000000 }, - {3906,3906, 0, 0, 40000, 173,0.000000 }, - {3907,3907, 0, 0, 40000, 53,0.000000 }, - {3908,3908, 0, 0, 40000, 106,0.000000 }, - {3909,3909, 0, 0, 2133, 2133,0.000000 }, - {3910,3910, 0, 0, 40000, 846,0.000000 }, - {3911,3911, 0, 0, 40000, 773,0.000000 }, - {3912,3912, 0, 0, 1066, 1066,0.000000 }, - {3913,3913, 0, 0, 40000, 73,0.000000 }, - {3914,3914, 0, 0, 40000, 86,0.000000 }, - {3915,3915, 0, 0, 40000, 46,0.000000 }, - {3916,3916, 0, 0, 1126, 1126,0.000000 }, - {3917,3917, 0, 0, 40000, 13,0.000000 }, - {3918,3918, 0, 0, 300, 300,0.000000 }, - {3919,3919, 0, 0, 146, 146,0.000000 }, - {3920,3920, 0, 0, 140, 140,0.000000 }, - {3921,3921, 0, 0, 1920, 1920,0.000000 }, - {3922,3922, 0, 0, 40000, 0,0.000000 }, - {3923,3923, 0, 0, 533, 533,0.000000 }, - {3924,3924, 0, 0, 486, 486,0.000000 }, - {3925,3925, 0, 0, 940, 940,0.000000 }, - {3926,3926, 0, 0, 600, 600,0.000000 }, - {3927,3927, 0, 0, 253, 253,0.000000 }, - {3928,3928, 0, 0, 380, 73,0.000000 }, - {3929,3929, 0, 0, 40, 40,0.000000 }, - {3930,3930, 0, 0, 40000, 20,0.000000 }, - {3931,3931, 0, 0, 40000, 13,0.000000 }, - {3932,3932, 0, 0, 4600, 4600,0.000000 }, - {3933,3933, 0, 0, 4820, 4820,0.000000 }, - { 523, 523, 0, 0, 46, 46,0.000000 }, - {3934,3934, 0, 0, 66, 66,0.000000 }, - {3935,3935, 0, 0, 53, 53,0.000000 }, - {3936,3936, 48, 0, 26, 26,0.000000 }, - {3937,3937, 27, 0, 53, 53,0.000000 }, - {3938,3938, 40, 0, 40, 40,0.000000 }, - {3939,3939, 48, 0, 13, 13,0.000000 }, - {3938,3938, 45, 0, 46, 46,0.000000 }, - {3940,3940, 48, 0, 26, 26,0.000000 }, - {3938,3938, 47, 0, 66, 66,0.000000 }, - {3941,3941, 48, 0, 13, 13,0.000000 }, - {3938,3938, 49, 0, 73, 73,0.000000 }, - {3938,3938, 53, 0, 80, 80,0.000000 }, - {3938,3938, 56, 0, 66, 66,0.000000 }, - { 129, 129, 52, 0, 73, 73,0.000000 }, - { 130, 130, 48, 0, 40, 40,0.000000 }, - { 129, 129, 58, 0, 73, 73,0.000000 }, - { 132, 132, 47, 0, 20, 20,0.000000 }, - { 492, 492, 43, 0, 26, 26,0.000000 }, - { 132, 132, 49, 0, 20, 20,0.000000 }, - { 132, 132, 51, 0, 20, 20,0.000000 }, - { 132, 132, 54, 0, 20, 20,0.000000 }, - { 132, 132, 57, 0, 13, 13,0.000000 }, - { 492, 492, 72, 0, 140, 140,0.000000 }, - { 137, 137, 76, 0, 600, 600,0.000000 }, - { 138, 138, 84, 0, 246, 246,0.000000 }, - { 139, 139, 36, 0, 113, 113,0.000000 }, - { 140, 140, 76, 0, 586, 586,0.000000 }, - { 141, 141, 84, 0, 60, 60,0.000000 }, - { 135, 135, 83, 0, 413, 413,0.000000 }, - { 142, 142, 84, 0, 106, 106,0.000000 }, - {3942,3942, 24, 0, 600, 600,0.000000 }, - { 137, 137, 77, 0, 606, 606,0.000000 }, - { 144, 144, 60, 0, 40, 40,0.000000 }, - { 145, 145, 65, 0, 13, 13,0.000000 }, - { 146, 146, 59, 0, 40, 40,0.000000 }, - { 147, 147, 51, 0, 40, 40,0.000000 }, - { 148, 148, 45, 0, 46, 46,0.000000 }, - { 149, 149, 71, 0, 140, 140,0.000000 }, - { 150, 150, 60, 0, 146, 146,0.000000 }, - { 151, 151, 58, 0, 153, 153,0.000000 }, - { 152, 152, 53, 0, 80, 80,0.000000 }, - { 153, 153, 64, 0, 13, 13,0.000000 }, - { 154, 154, 71, 0, 273, 273,0.000000 }, - { 156, 156, 61, 0, 53, 53,0.000000 }, - { 158, 158, 48, 0, 40, 40,0.000000 }, - { 159, 159, 69, 0, 20, 20,0.000000 }, - { 160, 160, 68, 0, 20, 20,0.000000 }, - { 161, 161, 63, 0, 113, 113,0.000000 }, - { 162, 162, 74, 0, 293, 293,0.000000 }, - { 163, 163, 60, 0, 113, 113,0.000000 }, - { 164, 164, 80, 0, 440, 440,0.000000 }, - { 165, 165, 64, 0, 40, 40,0.000000 }, - { 166, 166, 69, 0, 286, 286,0.000000 }, - { 167, 167, 73, 0, 126, 126,0.000000 }, - { 168, 168, 75, 0, 20, 20,0.000000 }, - { 169, 169, 68, 0, 33, 33,0.000000 }, - { 131, 131, 48, 0, 93, 93,0.000000 }, - {3061,3061, 53, 0, 833, 833,0.000000 }, - {3943,3944, 0, 0, 546, 546,0.000000 }, - {3945,3946, 0, 0, 1813, 1813,0.000000 }, - { 174,3947, 0, 0, 1260, 1260,0.000000 }, - {3948,3949, 0, 0, 160, 160,0.000000 }, - { 9,3950, 0, 0, 1100, 1100,0.000000 }, - {3951,3952, 0, 0, 3360, 3360,0.000000 }, - {3953,3954, 0, 0, 1720, 1720,0.000000 }, - { 15,3955, 0, 0, 226, 226,0.000000 }, - {3956,3957, 0, 0, 40000, 6,0.000000 }, - {3958,3959, 0, 0, 40000, 6,0.000000 }, - {3960,3961, 0, 0, 40000, 186,0.000000 }, - {3962,3963, 0, 0, 40000, 6,0.000000 }, - {3964,3965, 0, 0, 146, 146,0.000000 }, - {3966,3965, 0, 0, 146, 146,0.000000 }, - { 31,3967, 0, 0, 3386, 3386,0.000000 }, - {3968,3969, 0, 0, 3520, 3520,0.000000 }, - {3970,3971, 0, 0, 126, 0,0.000000 }, - {3972,3971, 0, 0, 126, 0,0.000000 }, - {3973,3974, 0, 0, 1013, 1013,0.000000 }, - {3975,3976, 0, 0, 3733, 3733,0.000000 }, - {3977,3976, 0, 0, 3733, 3733,0.000000 }, - {3978,3979, 0, 0, 3020, 3020,0.000000 }, - {3980,3981, 0, 0, 686, 686,0.000000 }, - {3982,3983, 0, 0, 40000, 46,0.000000 }, - {3984,3985, 0, 0, 40000, 373,0.000000 }, - { 54,3986, 0, 0, 340, 340,0.000000 }, - {3987,3988, 0, 0, 1213, 6,0.000000 }, - {3989,3990, 0, 0, 40000, 53,0.000000 }, - {3991, 253, 0, 0, 40000, 126,0.000000 }, - {3992,3992, 0, 0, 40, 40,0.000000 }, - {3993,3993, 0, 0, 40000, 53,0.000000 }, - {3994,3995, 0, 0, 40000, 13,0.000000 }, - {3996,3997, 0, 0, 40000, 40,0.000000 }, - {3998,3999, 0, 0, 40000, 13,0.000000 }, - {1503,4000, 0, 0, 40000, 33,0.000000 }, - { 88,4001, 0, 0, 40000, 500,0.000000 }, - {3743,4002, 0, 0, 900, 900,0.000000 }, - { 92,4003, 0, 0, 1720, 13,0.000000 }, - { 93,4004, 0, 0, 1060, 80,0.000000 }, - { 94,4005, 0, 0, 40000, 53,0.000000 }, - { 96,4006, 0, 0, 1913, 533,0.000000 }, - { 103,4007, 0, 0, 1813, 1813,0.000000 }, - { 104,4008, 0, 0, 846, 846,0.000000 }, - { 105,4009, 0, 0, 900, 900,0.000000 }, - { 107,4010, 0, 0, 300, 300,0.000000 }, - { 108,4011, 0, 0, 40000, 6,0.000000 }, - { 110,4012, 0, 0, 40000, 6,0.000000 }, - { 111,4013, 0, 0, 1126, 1126,0.000000 }, - {4014,4015, 0, 0, 140, 140,0.000000 }, - { 115,4016, 0, 0, 146, 146,0.000000 }, - { 118,4017, 0, 0, 2280, 2280,0.000000 }, - { 119,4018, 0, 0, 286, 286,0.000000 }, - { 120,4019, 0, 0, 586, 586,0.000000 }, - { 121,4020, 0, 0, 4426, 4426,0.000000 }, - { 123,4021, 0, 0, 193, 193,0.000000 }, - { 124,4022, 0, 0, 146, 260,0.000000 }, - { 125,4023, 0, 0, 40000, 773,0.000000 }, - {4024,4024, 35, 0, 40, 40,0.000000 }, - {4025,4026, 38, 1, 93, 93,0.000000 }, - {4027,4028, 38, 1, 120, 120,0.000000 }, - {4029,4030, 48, 1, 73, 73,-1.906250 }, - {4031,4031, 51, 0, 26, 26,0.000000 }, - {4032,4033, 48, 1, 293, 293,-1.906250 }, - {4034,4034, 61, 1, 1873, 1873,0.093750 }, - {3369,1557, 70, 0, 906, 906,0.000000 }, - {4035,4036, 79, 1, 293, 293,0.078125 }, - {4037,4037, 62, 0, 1726, 1726,0.000000 }, - {4038,4039, 67, 1, 240, 240,0.078125 }, - {4040,4040, 62, 1, 2040, 2040,0.093750 }, - {4041,4042, 54, 1, 80, 80,0.000000 }, - {4041,4043, 48, 1, 80, 80,0.000000 }, - { 389, 389, 42, 0, 180, 180,0.000000 }, - {4044,4045, 48, 1, 80, 80,0.000000 }, - {4046,4047, 48, 1, 53, 53,0.000000 }, - {4048,4048, 16, 0, 20, 20,0.000000 }, - {4049,4049, 16, 0, 146, 146,0.000000 }, - {4050,4051, 64, 0, 646, 646,0.000000 }, - { 844, 844,244, 0, 13, 13,0.000000 }, - { 855, 855,244, 0, 246, 6,0.000000 }, - { 880, 880,232, 0, 46, 46,0.000000 }, - { 882, 882,220, 0, 5433, 5433,0.000000 }, - { 887, 887, 35, 0, 46, 46,0.000000 }, - { 884, 884, 35, 0, 73, 73,0.000000 }, - { 885, 885, 35, 0, 73, 73,0.000000 }, - { 886, 886, 35, 0, 20, 20,0.000000 }, - { 361, 361, 35, 0, 113, 113,0.000000 }, - { 767, 767, 35, 0, 760, 760,0.000000 }, - { 888, 888, 35, 0, 40, 40,0.000000 }, - {2141,2141, 35, 0, 53, 53,0.000000 }, - { 891, 891, 35, 0, 106, 106,0.000000 }, - {2142,2142, 35, 0, 53, 53,0.000000 }, - {2143,2143, 35, 0, 73, 73,0.000000 }, - {2144,2144, 35, 0, 800, 800,0.000000 }, - {2145,2145, 35, 0, 420, 420,0.000000 }, - { 376, 376, 35, 0, 146, 146,0.000000 }, - { 895, 895, 35, 0, 126, 126,0.000000 }, - {2146,2146, 35, 0, 100, 100,0.000000 }, - { 382, 382, 35, 0, 20, 20,0.000000 }, - {2147,2147, 35, 0, 20, 20,0.000000 }, - { 898, 898, 35, 0, 60, 60,0.000000 }, - { 899, 899, 35, 0, 206, 206,0.000000 }, - { 900, 900, 35, 0, 193, 193,0.000000 }, - { 871, 871, 35, 0, 120, 120,0.000000 }, - { 388, 388, 35, 0, 73, 73,0.000000 }, - { 901, 901, 35, 0, 173, 173,0.000000 }, - { 902, 902, 35, 0, 1006, 1006,0.000000 }, - { 903, 903, 35, 0, 40, 40,0.000000 }, - {3500,3500, 35, 0, 5233, 6,0.000000 }, - {4052,4052, 0, 0, 1400, 1400,0.000000 }, - {4053,4053, 0, 0, 1206, 1206,0.000000 }, - {4054,4054, 0, 0, 40000, 46,0.000000 }, - {4055,4055, 0, 0, 40000, 26,0.000000 }, - {4056,4056, 0, 0, 3600, 3600,0.000000 }, - {4057,4057, 0, 0, 40000, 6,0.000000 }, - {4058,4058, 0, 0, 960, 960,0.000000 }, - {4059,4059, 0, 0, 80, 80,0.000000 }, - {4060,4060, 0, 0, 40000, 20,0.000000 }, - {4061,4061, 0, 0, 40000, 20,0.000000 }, - {4062,4062, 0, 0, 4473, 4473,0.000000 }, - {4063,4063, 0, 0, 380, 6,0.000000 }, - {4064,4064, 0, 0, 40000, 20,0.000000 }, - {4065,4065, 0, 0, 4593, 4593,0.000000 }, - {4066,4066, 0, 0, 40000, 20,0.000000 }, - {4067,4067, 0, 0, 40000, 0,0.000000 }, - {4068,4068, 0, 0, 40, 40,0.000000 }, - {4069,4069, 0, 0, 60, 60,0.000000 }, - {4070,4070, 0, 0, 100, 100,0.000000 }, - {4071,4071, 0, 0, 40000, 40,0.000000 }, - {4072,4072, 0, 0, 126, 126,0.000000 }, - {4073,4073, 0, 0, 40, 40,0.000000 }, - {4074,4074, 0, 0, 40000, 0,0.000000 }, - {4075,4075, 0, 0, 4193, 4193,0.000000 }, - {4076,4076, 0, 0, 806, 0,0.000000 }, - {4077,4077, 0, 0, 526, 526,0.000000 }, - {4078,4078, 0, 0, 40000, 73,0.000000 }, - {4079,4079, 0, 0, 40000, 6,0.000000 }, - {4080,4080, 0, 0, 40000, 153,0.000000 }, - {4081,4081, 0, 0, 40000, 0,0.000000 }, - {4082,4082, 0, 0, 40, 6,0.000000 }, - {4083,4083, 0, 0, 3400, 0,0.000000 }, - {4084,4084, 0, 0, 2280, 0,0.000000 }, - {4085,4085, 0, 0, 40000, 426,0.000000 }, - {4086,4086, 0, 0, 1206, 1206,0.000000 }, - { 127, 127, 36, 0, 20, 20,0.000000 }, - {4087,4087, 36, 0, 6, 6,0.000000 }, - {2030,2030, 36, 0, 93, 93,0.000000 }, - {3782,3782, 48, 0, 6, 6,0.000000 }, - {3783,3783, 36, 0, 33, 33,0.000000 }, - {4088,4088, 48, 0, 246, 246,0.000000 }, - { 132, 132, 69, 0, 13, 13,0.000000 }, - {4088,4088, 52, 0, 360, 360,0.000000 }, - { 152, 152, 48, 0, 86, 86,0.000000 }, - {4088,4088, 55, 0, 493, 493,0.000000 }, - { 139, 139, 57, 0, 93, 93,0.000000 }, - {4088,4088, 58, 0, 560, 560,0.000000 }, - {4088,4088, 60, 0, 580, 580,0.000000 }, - {4089,4089, 62, 0, 840, 840,0.000000 }, - {4088,4088, 63, 0, 626, 626,0.000000 }, - { 134, 134, 70, 0, 140, 140,0.000000 }, - {4090,4090, 70, 0, 286, 286,0.000000 }, - {4091,4091, 53, 0, 613, 613,0.000000 }, - {3516,3516, 48, 0, 40, 40,0.000000 }, - {4092,4092, 84, 0, 420, 420,0.000000 }, - {4093,4093, 43, 0, 160, 160,0.000000 }, - {4094,4094, 56, 0, 326, 326,0.000000 }, - {3791,3791, 24, 0, 140, 140,0.000000 }, - { 134, 134, 65, 0, 160, 160,0.000000 }, - { 146, 146, 48, 0, 40, 40,0.000000 }, - { 146, 146, 54, 0, 40, 40,0.000000 }, - {4095,4095, 42, 0, 46, 46,0.000000 }, - {4095,4095, 39, 0, 46, 46,0.000000 }, - {3816,3816, 52, 0, 106, 106,0.000000 }, - {4096,4096, 52, 0, 200, 200,0.000000 }, - { 158, 158, 60, 0, 33, 33,0.000000 }, - { 158, 158, 66, 0, 33, 33,0.000000 }, - { 158, 158, 59, 0, 33, 33,0.000000 }, - {3538,3538, 91, 0, 273, 273,0.000000 }, - {3547,3547,109, 0, 1780, 1780,0.000000 }, - {4097,4097, 79, 0, 126, 126,0.000000 }, - {4098,4098, 0, 0, 3413, 3413,0.000000 }, - {4099,4100, 0, 1, 1613, 1613,0.031250 }, - {4101,4102, 0, 1, 2146, 2146,0.031250 }, - {4103,4104, 0, 1, 1646, 1646,0.046875 }, - {4105,4106, 0, 1, 1900, 1900,0.156250 }, - {4107,4108, 0, 1, 1473, 1473,0.046875 }, - {4109,4110, 0, 1, 1020, 1020,0.062500 }, - {4111,4112, 0, 1, 2126, 2126,0.000000 }, - {4113,4114, 0, 1, 1740, 1740,0.000000 }, - {4115,4116, 0, 1, 993, 993,0.000025 }, - {4117,4118, 0, 1, 886, 886,0.000000 }, - {4119,4120, 0, 1, 1153, 1153,0.046875 }, - {4121,4122, 0, 1, 1420, 1420,0.000000 }, - {4123,4124, 0, 1, 193, 193,0.000000 }, - {4125,4126, 0, 1, 406, 406,0.000000 }, - {4127,4128, 0, 1, 1400, 1400,0.031250 }, - {4129,4129, 0, 1, 980, 980,0.031250 }, - {4130,4131, 0, 1, 40000, 6,0.156250 }, - {4132,4133, 0, 1, 40, 13,0.078125 }, - {4134,4135, 0, 1, 7180, 13,0.156250 }, - {4136,4137, 0, 1, 40000, 180,0.031250 }, - {4138,4139, 0, 1, 40000, 46,0.062500 }, - {4140,4141, 0, 1, 40000, 46,0.140625 }, - {4142,4143, 0, 1, 40000, 6,0.000000 }, - {4144,4145, 0, 1, 40000, 153,0.109375 }, - {4146,4147, 0, 1, 920, 920,0.000000 }, - {4148,4149, 0, 1, 653, 653,0.000025 }, - {4150,4151, 0, 1, 633, 633,0.000000 }, - {4152,4153, 0, 1, 893, 893,0.046875 }, - {4154,4155, 0, 1, 440, 440,0.000000 }, - {4156,4157, 0, 1, 40000, 60,-1.906250 }, - {4158,4159, 0, 1, 40000, 60,-1.906250 }, - {4160,4161, 0, 1, 2033, 2033,0.234375 }, - {4162,4163, 0, 1, 1900, 1900,0.031250 }, - {4164,4165, 0, 1, 1453, 1453,0.000000 }, - {4166,4167, 0, 1, 2186, 2186,0.000000 }, - {4168,4169, 0, 1, 1933, 1933,0.046875 }, - {4170,4171, 0, 1, 633, 633,0.000000 }, - {4172,4173, 0, 1, 486, 486,0.000000 }, - {4174,4174, 0, 0, 313, 313,0.000000 }, - {4175,4176, 0, 1, 2533, 2533,0.078125 }, - {4177,4178, 0, 1, 2040, 13,0.000000 }, - {4179,4179, 0, 0, 40000, 66,0.000000 }, - {4180,4181, 0, 1, 40000, 60,0.000025 }, - {4182,4182, 0, 0, 40000, 133,0.000000 }, - {4183,4184, 0, 1, 40000, 173,0.078125 }, - {4185,4186, 0, 1, 333, 333,0.109375 }, - {4187,4188, 0, 1, 1813, 1813,0.031250 }, - {4189,4190, 0, 1, 1473, 1473,0.031250 }, - {4191,4192, 0, 1, 40000, 213,0.062500 }, - {4193,4194, 0, 1, 40000, 500,-0.062500 }, - {4195,4195, 0, 1, 40000, 326,0.109375 }, - {4196,4196, 0, 1, 40000, 406,0.109375 }, - {4197,4198, 0, 1, 40000, 280,0.140625 }, - {4199,4200, 0, 1, 40000, 53,0.140625 }, - {4201,4202, 0, 1, 40000, 286,0.156250 }, - {4203,4204, 0, 1, 206, 206,0.125000 }, - {4205,4206, 0, 1, 40000, 26,0.000000 }, - {4207,4208, 0, 1, 40000, 20,0.031250 }, - {4209,4209, 0, 0, 40000, 6,0.000000 }, - {4210,4210, 0, 0, 40000, 20,0.000000 }, - {4211,4212, 0, 1, 40000, 160,0.031250 }, - {4213,4214, 0, 1, 40000, 73,0.062500 }, - {4215,4216, 0, 1, 2526, 2526,0.093750 }, - {4217,4217, 0, 1, 5153, 5153,0.125000 }, - {4218,4219, 0, 1, 40000, 73,0.000000 }, - {4220,4220, 0, 0, 40000, 60,0.000000 }, - {4221,4221, 0, 0, 40000, 0,0.000000 }, - {4222,4222, 0, 0, 40000, 0,0.000000 }, - {4223,4224, 0, 1, 40000, 73,0.000000 }, - {4225,4225, 0, 0, 40000, 33,0.000000 }, - {4226,4226, 0, 0, 40000, 6,0.000000 }, - {4227,4228, 0, 1, 40000, 40,0.000000 }, - {4229,4229, 0, 0, 40000, 0,0.000000 }, - {4230,4230, 0, 0, 40000, 6,0.000000 }, - {4231,4231, 0, 0, 40000, 33,0.000000 }, - {4232,4233, 0, 1, 40000, 53,0.031250 }, - {4234,4235, 0, 1, 40000, 20,0.046875 }, - {4236,4237, 0, 1, 420, 420,0.031250 }, - {4238,4238, 0, 0, 40000, 106,0.000000 }, - {4239,4239, 0, 0, 40000, 6,0.000000 }, - {4240,4241, 0, 1, 40000, 6,0.125000 }, - {4242,4243, 0, 1, 40000, 13,0.109375 }, - {4244,4245, 0, 1, 40000, 53,0.109375 }, - {4246,4247, 0, 1, 226, 6,-0.031250 }, - {4248,4248, 0, 0, 40000, 6,0.000000 }, - {4249,4250, 0, 1, 40000, 133,0.156250 }, - {4251,4252, 0, 1, 4186, 13,0.125000 }, - {4253,4254, 0, 1, 40000, 26,0.031250 }, - {4255,4256, 0, 1, 40000, 660,0.078125 }, - {4257,4258, 0, 1, 846, 66,0.109375 }, - {4259,4260, 0, 1, 1293, 80,0.078125 }, - {4261,4262, 0, 1, 40000, 300,0.140625 }, - {4263,4264, 0, 1, 2040, 2040,0.109375 }, - {4265,4266, 0, 1, 1360, 1360,0.062500 }, - {4267,4268, 0, 1, 40000, 433,0.093750 }, - {4269,4270, 0, 1, 40000, 533,0.109375 }, - {4271,4272, 0, 1, 826, 826,0.093750 }, - {4273,4274, 0, 1, 40000, 926,0.125000 }, - {4275,4275, 0, 1, 886, 886,0.109375 }, - {4276,4277, 0, 1, 2186, 2186,-0.046875 }, - {4278,4279, 0, 1, 1486, 1486,0.125000 }, - {4280,4281, 0, 1, 40000, 393,-0.078125 }, - {4282,4283, 0, 1, 40000, 1166,0.140625 }, - {4284,4285, 0, 1, 360, 360,0.078125 }, - {4286,4287, 0, 1, 1693, 1693,0.031250 }, - {4288,4289, 0, 1, 760, 760,0.000000 }, - {4290,4291, 0, 1, 126, 126,0.031250 }, - {4292,4292, 0, 0, 300, 300,0.000000 }, - {4293,4294, 0, 1, 280, 280,0.000000 }, - {4295,4296, 0, 1, 40000, 26,0.062500 }, - {4297,4297, 0, 0, 40000, 66,0.000000 }, - {4298,4298, 0, 0, 40000, 53,0.000000 }, - {4299,4299, 0, 0, 1940, 1940,0.000000 }, - {4300,4300, 0, 0, 86, 86,0.000000 }, - {4301,4302, 0, 1, 280, 280,0.031250 }, - {4303,4303, 0, 0, 40, 40,0.000000 }, - {4304,4305, 0, 1, 53, 53,0.000000 }, - {4306,4307, 0, 1, 140, 140,0.000000 }, - {4308,4309, 0, 1, 26, 26,0.000000 }, - {4310,4311, 0, 1, 2153, 2153,0.109375 }, - {4312,4312, 0, 0, 293, 293,0.000000 }, - {4313,4314, 0, 1, 993, 26,0.000000 }, - {4315,4316, 0, 1, 5613, 5613,0.000000 }, - {4317,4317, 0, 0, 220, 220,0.000000 }, - {4318,4319, 0, 1, 10306, 526,0.000000 }, - {4320,4321, 0, 1, 1486, 13,0.000000 }, - {4322,4323, 0, 1, 40000, 660,0.000000 }, - {4324,4324, 0, 0, 120, 120,0.000000 }, - {4325,4325, 34, 0, 40, 40,0.000000 }, - {4326,4326, 28, 0, 73, 73,0.000000 }, - {4327,4328, 39, 1, 233, 233,0.000000 }, - {4327,4328, 33, 1, 193, 193,0.000000 }, - {4329,4330, 63, 1, 33, 33,0.000000 }, - {4331,4331, 15, 0, 13, 13,0.000000 }, - {4332,4332, 36, 0, 13, 13,0.000000 }, - {4332,4333, 36, 1, 133, 133,0.406250 }, - {4334,4335, 25, 1, 13, 13,0.000000 }, - {4336,4335, 25, 1, 33, 33,0.000000 }, - {4337,4338, 61, 1, 40, 40,0.000000 }, - {4339,4340, 37, 1, 53, 53,0.000000 }, - {4341,4342, 15, 1, 320, 320,0.000000 }, - {4343,4344, 48, 1, 73, 73,-1.906250 }, - {4341,4345, 19, 1, 320, 320,0.000000 }, - {4346,4346, 48, 0, 53, 53,0.000000 }, - {4341,4342, 22, 1, 353, 353,0.000000 }, - {4341,4342, 24, 1, 360, 360,0.000000 }, - {4341,4347, 27, 1, 393, 393,0.000000 }, - {4341,4342, 31, 1, 380, 380,0.000000 }, - {4348,4349, 60, 1, 246, 246,0.031250 }, - {4350,4350, 70, 0, 340, 340,0.000000 }, - {4351,4352, 80, 1, 106, 106,0.125000 }, - {4353,4353, 58, 0, 73, 73,0.000000 }, - {4354,4355, 31, 1, 313, 313,0.000000 }, - {4356,4356, 61, 0, 253, 253,0.000000 }, - {4357,4358, 41, 1, 100, 100,0.000000 }, - {4359,4360, 35, 1, 160, 160,0.000000 }, - {4361,4362, 29, 1, 40, 40,0.000000 }, - {4363,4364, 41, 1, 166, 166,0.000000 }, - {4363,4364, 37, 1, 160, 160,0.000000 }, - {4365,4366, 54, 1, 80, 80,0.000000 }, - {4365,4367, 48, 1, 80, 80,0.000000 }, - {4368,4369, 77, 1, 53, 53,0.000000 }, - {4370,4371, 72, 1, 46, 46,0.000000 }, - {4372,4372, 40, 0, 140, 140,0.000000 }, - {4373,4373, 38, 0, 73, 73,0.000000 }, - {4374,4374, 36, 0, 533, 533,0.000000 }, - {4375,4376, 60, 1, 26, 26,0.000000 }, - {4376,4377, 60, 1, 26, 26,0.000000 }, - {4378,4378, 73, 0, 60, 60,0.000000 }, - {4379,4380, 68, 1, 40, 40,0.000000 }, - {4381,4382, 18, 1, 60, 60,0.000000 }, - {4383,4384, 18, 1, 106, 106,0.000000 }, - {4385,4385, 90, 0, 80, 80,0.000000 }, - {4386,4386, 90, 0, 306, 306,0.000000 }, - {4387,4388, 64, 1, 233, 233,0.031250 }, - {4389,4390, 80, 1, 140, 140,0.031250 }, - {4391,4392, 64, 1, 606, 606,0.000000 }, - {4393,4393, 67, 0, 20, 20,0.000000 }, - {4394,4395, 50, 1, 53, 53,0.000000 }, - {4396,4396, 36, 0, 66, 66,0.000000 }, - {4397,4397, 0, 0, 40000, 20,0.000000 }, - {4398,4398, 0, 0, 40000, 0,0.000000 }, - {4399,4399, 0, 0, 360, 360,0.000000 }, - {4400,4400, 0, 0, 586, 586,0.000000 }, - {4401,4401, 0, 0, 40000, 0,0.000000 }, - {4402,4402, 0, 0, 40000, 0,0.000000 }, - {4403,4403, 0, 0, 40000, 0,0.000000 }, - {4404,4404, 0, 0, 40000, 6,0.000000 }, - {4405,4405, 0, 0, 40000, 0,0.000000 }, - {4406,4406, 0, 0, 146, 146,0.000000 }, - {4406,4406, 73, 0, 886, 886,0.000000 }, - {4407,4407, 0, 0, 40, 0,0.000000 }, - {4408,4408, 0, 0, 486, 0,0.000000 }, - {4409,4409, 0, 0, 1226, 1226,0.000000 }, - {4410,4410, 0, 0, 1480, 1480,0.000000 }, - {4411,4411, 0, 0, 46, 46,0.000000 }, - {4412,4412, 0, 0, 126, 126,0.000000 }, - {4412,4412, 12, 0, 106, 106,0.000000 }, - {4413,4413, 0, 0, 160, 160,0.000000 }, - {4413,4413, 1, 0, 153, 153,0.000000 }, - {4414,4414, 0, 0, 20, 20,0.000000 }, - {4414,4414, 23, 0, 26, 26,0.000000 }, - {4415,4415, 0, 0, 140, 140,0.000000 }, - {4416,4416, 0, 0, 486, 486,0.000000 }, - {4417,4417, 0, 0, 40000, 13,0.000000 }, - {4418,4418, 0, 0, 40000, 0,0.000000 }, - {4419,4419, 0, 0, 1226, 1226,0.000000 }, - {4420,4420, 0, 0, 766, 766,0.000000 }, - {4421,4421, 0, 0, 93, 93,0.000000 }, - {4422,4422, 0, 2, 40000, 0,0.000000 }, + { 0, 0, 0, 0, 9006, 133,0 }, + { 1, 1, 0, 0, 9206, 146,0 }, + { 2, 2, 0, 0, 9246, 240,0 }, + { 3, 3, 0, 0, 9440, 140,0 }, + { 4, 4, 0, 0, 8900, 120,0 }, + { 5, 5, 0, 0, 9400, 140,0 }, + { 6, 6, 0, 0, 7460, 380,0 }, + { 7, 7, 0, 0, 9226, 93,0 }, + { 8, 8, 0, 0, 4613, 420,0 }, + { 9, 9, 0, 0, 7286, 4713,0 }, + { 10, 10, 0, 0, 2280, 746,0 }, + { 11, 11, 0, 0, 9233, 240,0 }, + { 12, 12, 0, 0, 346, 153,0 }, + { 13, 13, 0, 0, 633, 233,0 }, + { 14, 14, 0, 0, 4660, 1573,0 }, + { 15, 15, 0, 0, 1166, 400,0 }, + { 16, 16, 0, 0, 40000, 126,0 }, + { 17, 17, 0, 0, 40000, 93,0 }, + { 18, 18, 0, 0, 40000, 93,0 }, + { 19, 19, 0, 0, 40000, 553,0 }, + { 20, 20, 0, 0, 40000, 660,0 }, + { 21, 21, 0, 0, 40000, 73,0 }, + { 22, 22, 0, 0, 40000, 146,0 }, + { 23, 23, 0, 0, 40000, 146,0 }, + { 24, 24, 0, 0, 4026, 100,0 }, + { 25, 25, 0, 0, 14286, 120,0 }, + { 26, 26, 0, 0, 9233, 106,0 }, + { 27, 27, 0, 0, 4480, 100,0 }, + { 28, 28, 0, 0, 40000, 60,0 }, + { 29, 29, 0, 0, 40000, 80,0 }, + { 30, 30, 0, 0, 40000, 80,0 }, + { 31, 31, 0, 0, 18226, 100,0 }, + { 32, 32, 0, 0, 40000, 0,0 }, + { 33, 33, 0, 0, 40000, 80,0 }, + { 34, 34, 0, 0, 40000, 0,0 }, + { 35, 35, 0, 0, 40000, 53,0 }, + { 36, 36, 0, 0, 40000, 0,0 }, + { 37, 37, 0, 0, 40000, 0,0 }, + { 38, 38, 0, 0, 40000, 0,0 }, + { 39, 39, 0, 0, 40000, 160,0 }, + { 40, 40, 0, 0, 40000, 233,0 }, + { 41, 41, 0, 0, 40000, 73,0 }, + { 42, 42, 0, 0, 40000, 233,0 }, + { 43, 43, 0, 0, 40000, 213,0 }, + { 44, 44, 0, 0, 1246, 453,0 }, + { 45, 45, 0, 0, 4580, 786,0 }, + { 46, 46, 0, 0, 6873, 1246,0 }, + { 47, 47, 0, 0, 40000, 100,0 }, + { 48, 48, 0, 0, 40000, 140,0 }, + { 49, 49, 0, 0, 40000, 393,0 }, + { 50, 50, 0, 0, 40000, 406,0 }, + { 51, 51, 0, 0, 40000, 373,0 }, + { 52, 52, 0, 0, 40000, 0,0 }, + { 53, 53, 0, 0, 40000, 360,0 }, + { 54, 54, 0, 0, 1060, 380,0 }, + { 55, 55, 0, 0, 40000, 80,0 }, + { 56, 56, 0, 0, 40000, 73,0 }, + { 57, 57, 0, 0, 40000, 66,0 }, + { 58, 58, 0, 0, 40000, 60,0 }, + { 59, 59, 0, 0, 40000, 73,0 }, + { 60, 60, 0, 0, 40000, 66,0 }, + { 61, 61, 0, 0, 40000, 86,0 }, + { 62, 62, 0, 0, 40000, 66,0 }, + { 63, 63, 0, 0, 40000, 73,0 }, + { 64, 64, 0, 0, 40000, 80,0 }, + { 65, 65, 0, 0, 40000, 80,0 }, + { 66, 66, 0, 0, 40000, 73,0 }, + { 67, 67, 0, 0, 40000, 73,0 }, + { 68, 68, 0, 0, 40000, 53,0 }, + { 69, 69, 0, 0, 40000, 73,0 }, + { 70, 70, 0, 0, 40000, 126,0 }, + { 71, 71, 0, 0, 40000, 73,0 }, + { 72, 72, 0, 0, 40000, 73,0 }, + { 73, 73, 0, 0, 40000, 73,0 }, + { 74, 74, 0, 0, 40000, 66,0 }, + { 75, 75, 0, 0, 40000, 153,0 }, + { 76, 76, 0, 0, 40000, 153,0 }, + { 77, 77, 0, 0, 40000, 146,0 }, + { 78, 78, 0, 0, 40000, 146,0 }, + { 79, 79, 0, 0, 40000, 66,0 }, + { 80, 80, 0, 0, 40000, 60,0 }, + { 81, 81, 0, 0, 40000, 86,0 }, + { 82, 82, 0, 0, 40000, 73,0 }, + { 83, 83, 0, 0, 40000, 66,0 }, + { 84, 84, 0, 0, 40000, 153,0 }, + { 85, 85, 0, 0, 40000, 233,0 }, + { 86, 86, 0, 0, 40000, 80,0 }, + { 87, 87, 0, 0, 40000, 400,0 }, + { 88, 88, 0, 0, 40000, 1373,0 }, + { 89, 89, 0, 0, 40000, 193,0 }, + { 90, 90, 0, 0, 40000, 1273,0 }, + { 91, 91, 0, 0, 40000, 186,0 }, + { 92, 92, 0, 0, 40000, 86,0 }, + { 93, 93, 0, 0, 40000, 286,0 }, + { 94, 94, 0, 0, 40000, 140,0 }, + { 95, 95, 0, 0, 7440, 2473,0 }, + { 96, 96, 0, 0, 40000, 1220,0 }, + { 97, 97, 0, 0, 4946, 2713,0 }, + { 98, 98, 0, 0, 40000, 160,0 }, + { 99, 99, 0, 0, 8966, 406,0 }, + { 100, 100, 0, 0, 40000, 1353,0 }, + { 101, 101, 0, 0, 40000, 1306,0 }, + { 102, 102, 0, 0, 40000, 933,0 }, + { 103, 103, 0, 0, 9086, 226,0 }, + { 104, 104, 0, 0, 7233, 326,0 }, + { 105, 105, 0, 0, 7286, 200,0 }, + { 106, 106, 0, 0, 14180, 4406,0 }, + { 107, 107, 0, 0, 1180, 406,0 }, + { 108, 108, 0, 0, 40000, 66,0 }, + { 109, 109, 0, 0, 40000, 213,0 }, + { 110, 110, 0, 0, 40000, 73,0 }, + { 111, 111, 0, 0, 4606, 413,0 }, + { 112, 112, 0, 0, 613, 240,0 }, + { 113, 113, 0, 0, 1166, 400,0 }, + { 114, 114, 0, 0, 200, 353,0 }, + { 115, 115, 0, 0, 4553, 1480,0 }, + { 116, 116, 0, 0, 3740, 1260,0 }, + { 117, 117, 0, 0, 7240, 2300,0 }, + { 118, 118, 0, 0, 3020, 73,0 }, + { 119, 119, 0, 0, 1626, 800,0 }, + { 120, 120, 0, 0, 2466, 620,0 }, + { 121, 121, 0, 0, 12053, 3160,0 }, + { 122, 122, 0, 0, 466, 120,0 }, + { 123, 123, 0, 0, 1000, 320,0 }, + { 124, 124, 0, 0, 380, 60,0 }, + { 125, 125, 0, 0, 40000, 200,0 }, + { 126, 126, 0, 0, 560, 86,0 }, + { 127, 127, 35, 0, 386, 160,0 }, + { 128, 128, 52, 0, 126, 26,0 }, + { 129, 129, 48, 0, 286, 126,0 }, + { 130, 130, 58, 0, 173, 93,0 }, + { 129, 129, 60, 0, 286, 126,0 }, + { 131, 131, 47, 0, 520, 200,0 }, + { 132, 132, 43, 0, 173, 93,0 }, + { 131, 131, 49, 0, 520, 200,0 }, + { 133, 133, 43, 0, 160, 80,0 }, + { 131, 131, 51, 0, 526, 206,0 }, + { 134, 134, 43, 0, 1860, 653,0 }, + { 131, 131, 54, 0, 520, 200,0 }, + { 131, 131, 57, 0, 520, 200,0 }, + { 135, 135, 72, 0, 1860, 633,0 }, + { 131, 131, 60, 0, 506, 200,0 }, + { 136, 136, 76, 0, 1566, 546,0 }, + { 137, 137, 84, 0, 1340, 466,0 }, + { 138, 138, 36, 0, 1220, 433,0 }, + { 139, 139, 65, 0, 293, 133,0 }, + { 140, 140, 84, 0, 1333, 460,0 }, + { 141, 141, 83, 0, 220, 113,0 }, + { 135, 135, 84, 0, 1366, 473,0 }, + { 142, 142, 24, 0, 1893, 633,0 }, + { 136, 136, 77, 0, 1586, 553,0 }, + { 143, 143, 60, 0, 173, 93,0 }, + { 144, 144, 65, 0, 213, 126,0 }, + { 145, 145, 59, 0, 173, 0,0 }, + { 146, 146, 51, 0, 173, 100,0 }, + { 147, 147, 45, 0, 260, 206,0 }, + { 148, 148, 71, 0, 433, 180,0 }, + { 149, 149, 60, 0, 280, 26,0 }, + { 150, 150, 58, 0, 500, 186,0 }, + { 151, 151, 53, 0, 513, 200,0 }, + { 152, 152, 64, 0, 220, 86,0 }, + { 153, 153, 71, 0, 106, 46,0 }, + { 154, 154, 61, 0, 993, 340,0 }, + { 155, 155, 61, 0, 1906, 640,0 }, + { 156, 156, 44, 0, 206, 86,0 }, + { 157, 157, 40, 0, 586, 140,0 }, + { 158, 158, 69, 0, 126, 140,0 }, + { 159, 159, 68, 0, 126, 140,0 }, + { 160, 160, 63, 0, 146, 166,0 }, + { 161, 161, 74, 0, 280, 100,0 }, + { 162, 162, 60, 0, 1026, 320,0 }, + { 163, 163, 80, 0, 226, 100,0 }, + { 164, 164, 64, 0, 2713, 913,0 }, + { 165, 165, 72, 0, 120, 66,0 }, + { 166, 166, 73, 0, 386, 80,0 }, + { 167, 167, 70, 0, 553, 306,0 }, + { 168, 168, 68, 0, 126, 140,0 }, + { 169, 169, 48, 0, 386, 373,0 }, + { 131, 131, 53, 0, 520, 206,0 }, + { 170, 170, 0, 0, 40000, 0,0 }, + { 171, 171, 0, 0, 40000, 73,0 }, + { 172, 173, 0, 4, 5886, 100,0 }, + { 174, 175, 0, 4, 6913, 0,0 }, + { 176, 177, 0, 4, 4873, 0,0 }, + { 178, 178, 0, 0, 40000, 0,0 }, + { 179, 180, 0, 4, 4653, 433,0 }, + { 181, 181, 0, 0, 2280, 746,0 }, + { 182, 182, 0, 0, 40000, 0,0 }, + { 183, 184, 0, 4, 626, 0,0 }, + { 185, 186, 0, 4, 4653, 1546,0 }, + { 187, 187, 0, 0, 1166, 400,0 }, + { 188, 189, 0, 4, 40000, 60,0 }, + { 190, 191, 0, 4, 40000, 60,0 }, + { 192, 193, 0, 4, 40000, 73,0 }, + { 194, 194, 0, 0, 40000, 73,0 }, + { 195, 196, 0, 4, 40000, 66,0 }, + { 197, 198, 0, 4, 40000, 86,0 }, + { 199, 200, 0, 4, 40000, 66,0 }, + { 201, 202, 0, 4, 3713, 100,0 }, + { 203, 204, 0, 4, 14753, 126,0 }, + { 205, 206, 0, 4, 9286, 146,0 }, + { 207, 208, 0, 4, 14713, 126,0 }, + { 209, 210, 0, 4, 4653, 0,0 }, + { 211, 212, 0, 4, 40000, 66,0 }, + { 213, 213, 0, 0, 40000, 73,0 }, + { 214, 215, 0, 4, 626, 0,0 }, + { 216, 217, 0, 4, 4066, 100,0 }, + { 218, 219, 0, 4, 14586, 193,0 }, + { 220, 221, 0, 4, 2813, 106,0 }, + { 222, 223, 0, 4, 500, 0,0 }, + { 224, 224, 0, 0, 40000, 0,0 }, + { 225, 226, 0, 4, 7993, 93,0 }, + { 227, 227, 0, 0, 40000, 0,0 }, + { 228, 228, 0, 0, 40000, 133,0 }, + { 229, 230, 0, 4, 720, 213,0 }, + { 231, 232, 0, 4, 40000, 146,0 }, + { 233, 234, 0, 4, 40000, 0,0 }, + { 235, 236, 0, 4, 1000, 340,0 }, + { 235, 237, 0, 4, 3280, 1120,0 }, + { 46, 238, 0, 4, 6920, 0,0 }, + { 239, 240, 0, 4, 40000, 140,0 }, + { 241, 242, 0, 4, 40000, 146,0 }, + { 243, 243, 0, 0, 40000, 100,0 }, + { 244, 244, 0, 0, 40000, 60,0 }, + { 245, 245, 0, 0, 40000, 73,0 }, + { 246, 247, 0, 4, 720, 106,0 }, + { 248, 249, 0, 4, 40000, 126,0 }, + { 250, 250, 0, 0, 40000, 0,0 }, + { 251, 251, 0, 0, 40000, 126,0 }, + { 252, 253, 0, 4, 40000, 66,0 }, + { 254, 255, 0, 4, 40000, 93,0 }, + { 256, 257, 0, 4, 40000, 73,0 }, + { 258, 259, 0, 4, 40000, 86,0 }, + { 260, 261, 0, 4, 40000, 93,0 }, + { 262, 263, 0, 4, 40000, 80,0 }, + { 264, 265, 0, 4, 40000, 200,0 }, + { 266, 267, 0, 4, 40000, 73,0 }, + { 268, 269, 0, 4, 40000, 80,0 }, + { 270, 271, 0, 4, 40000, 73,0 }, + { 272, 273, 0, 4, 40000, 126,0 }, + { 274, 275, 0, 4, 40000, 100,0 }, + { 276, 276, 0, 0, 40000, 113,0 }, + { 277, 278, 0, 4, 40000, 186,0 }, + { 279, 280, 0, 4, 40000, 160,0 }, + { 281, 282, 0, 4, 40000, 206,0 }, + { 283, 283, 0, 0, 40000, 80,0 }, + { 284, 285, 0, 4, 40000, 73,0 }, + { 286, 287, 0, 4, 40000, 73,0 }, + { 288, 288, 0, 0, 40000, 93,0 }, + { 289, 290, 0, 4, 40000, 66,0 }, + { 291, 292, 0, 4, 40000, 153,0 }, + { 293, 294, 0, 4, 40000, 153,0 }, + { 295, 296, 0, 4, 40000, 320,0 }, + { 88, 297, 0, 4, 40000, 1280,0 }, + { 298, 299, 0, 4, 40000, 266,0 }, + { 300, 301, 0, 4, 40000, 1180,0 }, + { 302, 302, 0, 0, 40000, 286,0 }, + { 303, 303, 0, 0, 40000, 140,0 }, + { 304, 304, 0, 0, 13246, 2473,0 }, + { 305, 306, 0, 4, 40000, 1073,0 }, + { 307, 307, 0, 0, 9233, 240,0 }, + { 308, 308, 0, 0, 1186, 406,0 }, + { 309, 309, 0, 0, 40000, 1306,0 }, + { 310, 310, 0, 0, 40000, 933,0 }, + { 311, 312, 0, 4, 9146, 240,0 }, + { 313, 314, 0, 4, 7306, 326,0 }, + { 315, 316, 0, 4, 3586, 326,0 }, + { 317, 318, 0, 4, 7180, 0,0 }, + { 107, 319, 0, 4, 1180, 406,0 }, + { 108, 320, 0, 4, 40000, 66,0 }, + { 109, 321, 0, 4, 720, 213,0 }, + { 322, 323, 0, 4, 40000, 73,0 }, + { 324, 325, 0, 4, 613, 246,0 }, + { 326, 327, 0, 4, 1213, 386,0 }, + { 328, 328, 0, 0, 173, 106,0 }, + { 329, 329, 0, 0, 966, 333,0 }, + { 330, 331, 0, 4, 1906, 320,0 }, + { 332, 332, 0, 0, 3120, 73,0 }, + { 333, 333, 0, 0, 226, 73,0 }, + { 334, 334, 0, 0, 6600, 806,0 }, + { 335, 335, 0, 0, 273, 60,0 }, + { 336, 336, 0, 0, 12053, 660,0 }, + { 337, 337, 0, 0, 40000, 240,0 }, + { 338, 339, 0, 6, 6, 0,0 }, + { 340, 341, 0, 4, 560, 0,0 }, + { 342, 342, 35, 0, 40000, 0,0 }, + { 343, 343, 0, 0, 180, 100,0 }, + { 344, 344, 35, 0, 340, 146,0 }, + { 345, 345, 35, 0, 213, 33,0 }, + { 346, 346, 50, 0, 306, 20,0 }, + { 347, 347, 18, 0, 420, 146,0 }, + { 348, 348, 72, 0, 173, 86,0 }, + { 349, 349, 74, 0, 160, 93,0 }, + { 350, 350, 35, 0, 380, 146,0 }, + { 351, 351, 16, 0, 1206, 420,0 }, + { 352, 352, 0, 2, 6, 0,0 }, + { 353, 353, 38, 0, 200, 106,0 }, + { 354, 354, 38, 0, 346, 146,0 }, + { 355, 355, 31, 0, 406, 20,0 }, + { 355, 355, 35, 0, 406, 66,0 }, + { 355, 355, 38, 0, 406, 66,0 }, + { 355, 355, 41, 0, 406, 66,0 }, + { 355, 355, 45, 0, 306, 73,0 }, + { 355, 355, 50, 0, 306, 73,0 }, + { 356, 356, 36, 0, 1373, 493,0 }, + { 357, 357, 36, 0, 146, 33,0 }, + { 358, 358, 48, 0, 213, 86,0 }, + { 358, 358, 36, 0, 246, 86,0 }, + { 359, 359, 36, 0, 113, 0,0 }, + { 360, 360, 0, 0, 133, 40,0 }, + { 361, 361, 61, 0, 180, 26,0 }, + { 362, 362, 96, 0, 706, 266,0 }, + { 363, 363, 38, 0, 520, 193,0 }, + { 127, 127, 16, 0, 620, 233,0 }, + { 364, 365, 18, 4, 200, 0,0 }, + { 366, 366, 30, 0, 406, 246,0 }, + { 367, 368, 35, 4, 200, 0,0 }, + { 129, 129, 0, 0, 353, 153,0 }, + { 369, 369, 0, 0, 213, 13,0 }, + { 370, 370, 88, 0, 333, 113,0 }, + { 371, 371, 88, 0, 140, 73,0 }, + { 372, 372, 79, 0, 2540, 1040,0 }, + { 135, 135, 14, 0, 9213, 3066,0 }, + { 373, 373, 46, 0, 1093, 60,0 }, + { 374, 375,129, 4, 1200, 433,0 }, + { 376, 376, 58, 0, 1600, 726,0 }, + { 377, 377,164, 0, 526, 820,0 }, + { 378, 378,142, 0, 9153, 3073,0 }, + { 379, 379, 9, 0, 200, 100,0 }, + { 380, 381, 35, 4, 2353, 813,0 }, + { 382, 382, 28, 0, 1060, 120,0 }, + { 383, 383, 46, 0, 953, 20,0 }, + { 384, 384, 60, 0, 440, 160,0 }, + { 384, 384, 54, 0, 513, 180,0 }, + { 385, 385, 72, 0, 253, 120,0 }, + { 385, 385, 67, 0, 253, 113,0 }, + { 385, 385, 60, 0, 253, 106,0 }, + { 386, 386, 1, 0, 966, 613,0 }, + { 387, 387, 77, 0, 340, 86,0 }, + { 387, 387, 72, 0, 340, 86,0 }, + { 388, 388, 90, 0, 213, 86,0 }, + { 389, 389, 39, 0, 266, 73,0 }, + { 390, 390, 36, 0, 593, 73,0 }, + { 391, 392, 35, 4, 173, 46,0 }, + { 391, 393, 35, 4, 460, 66,0 }, + { 394, 394, 60, 0, 173, 20,0 }, + { 328, 328, 7, 0, 173, 0,0 }, + { 395, 395, 90, 0, 193, 20,0 }, + { 396, 396, 90, 0, 793, 40,0 }, + { 397, 397, 35, 0, 253, 86,0 }, + { 398, 399, 5, 4, 1913, 226,0 }, + { 400, 400,103, 0, 713, 273,0 }, + { 401, 401, 3, 0, 100, 0,0 }, + { 169, 169, 1, 0, 466, 413,0 }, + { 131, 131, 0, 0, 613, 226,0 }, + { 402, 402, 36, 0, 273, 53,0 }, + { 403, 403, 60, 0, 40000, 73,0 }, + { 404, 404, 37, 0, 1193, 426,0 }, + { 405, 405, 36, 0, 406, 20,0 }, + { 406, 406, 32, 0, 146, 73,0 }, + { 407, 407, 50, 0, 40000, 0,0 }, + { 408, 408, 50, 0, 793, 346,0 }, + { 409, 409, 83, 0, 120, 13,0 }, + { 410, 410, 72, 0, 433, 0,0 }, + { 148, 148, 59, 0, 513, 200,0 }, + { 411, 411, 64, 0, 173, 93,0 }, + { 411, 411, 60, 0, 173, 93,0 }, + { 412, 412, 72, 0, 160, 93,0 }, + { 412, 412, 62, 0, 173, 93,0 }, + { 413, 413, 83, 0, 773, 60,0 }, + { 414, 414, 0, 0, 40000, 80,0 }, + { 415, 415, 0, 0, 40000, 0,0 }, + { 416, 416, 0, 0, 40000, 73,0 }, + { 417, 417, 0, 0, 40000, 86,0 }, + { 418, 418, 0, 0, 40000, 0,0 }, + { 419, 419, 0, 0, 3440, 100,0 }, + { 420, 420, 0, 0, 3913, 420,0 }, + { 421, 421, 0, 0, 13620, 4640,0 }, + { 422, 422, 0, 0, 9233, 240,0 }, + { 423, 423, 0, 0, 633, 233,0 }, + { 424, 424, 0, 0, 4660, 1573,0 }, + { 425, 425, 0, 0, 4480, 1413,0 }, + { 426, 426, 0, 0, 40000, 0,0 }, + { 427, 427, 0, 0, 40000, 86,0 }, + { 428, 428, 60, 2, 6, 0,0 }, + { 429, 429, 73, 0, 593, 86,0 }, + { 429, 429, 74, 0, 593, 86,0 }, + { 429, 429, 80, 0, 593, 86,0 }, + { 429, 429, 84, 0, 593, 86,0 }, + { 429, 429, 92, 0, 520, 86,0 }, + { 430, 430, 81, 0, 786, 80,0 }, + { 430, 430, 83, 0, 786, 80,0 }, + { 430, 430, 95, 0, 680, 80,0 }, + { 431, 431, 35, 0, 593, 140,0 }, + { 432, 432, 60, 0, 213, 133,0 }, + { 357, 357, 59, 0, 113, 0,0 }, + { 432, 432, 44, 0, 213, 133,0 }, + { 433, 433, 41, 0, 713, 273,0 }, + { 434, 434, 97, 0, 113, 46,0 }, + { 433, 433, 44, 0, 513, 206,0 }, + { 433, 433, 48, 0, 506, 200,0 }, + { 435, 435, 96, 0, 700, 86,0 }, + { 433, 433, 51, 0, 520, 200,0 }, + { 433, 433, 54, 0, 513, 206,0 }, + { 436, 436, 40, 0, 1506, 793,0 }, + { 433, 433, 57, 0, 380, 160,0 }, + { 437, 437, 58, 0, 1600, 726,0 }, + { 438, 438, 97, 0, 233, 106,0 }, + { 439, 439, 50, 0, 186, 93,0 }, + { 437, 437, 60, 0, 1573, 713,0 }, + { 440, 440, 53, 0, 180, 73,0 }, + { 441, 441, 46, 0, 173, 126,0 }, + { 440, 440, 57, 0, 180, 40,0 }, + { 442, 442, 42, 0, 640, 240,0 }, + { 442, 442, 37, 0, 633, 233,0 }, + { 443, 443, 41, 0, 626, 240,0 }, + { 443, 443, 37, 0, 620, 233,0 }, + { 444, 444, 77, 0, 173, 40,0 }, + { 444, 444, 72, 0, 173, 40,0 }, + { 445, 445, 70, 0, 233, 100,0 }, + { 445, 445, 90, 0, 233, 93,0 }, + { 446, 446, 46, 0, 133, 73,0 }, + { 447, 447, 48, 0, 333, 73,0 }, + { 448, 448, 85, 0, 106, 0,0 }, + { 449, 449, 66, 0, 180, 26,0 }, + { 449, 449, 61, 0, 180, 26,0 }, + { 450, 450, 41, 0, 200, 66,0 }, + { 451, 451, 41, 0, 253, 66,0 }, + { 452, 452, 81, 0, 253, 26,0 }, + { 400, 400, 81, 0, 820, 306,0 }, + { 400, 400, 76, 0, 813, 300,0 }, + { 359, 359, 60, 0, 100, 0,0 }, + { 453, 453, 53, 0, 40000, 0,0 }, + { 454, 454, 0, 2, 6, 0,0 }, + { 455, 455, 0, 0, 200, 20,0 }, + { 456, 456, 0, 0, 4480, 100,0 }, + { 457, 457, 0, 0, 1180, 406,0 }, + { 458, 458, 0, 0, 40000, 86,0 }, + { 459, 459, 0, 0, 40000, 73,0 }, + { 460, 460, 0, 0, 3700, 66,0 }, + { 461, 461, 0, 0, 40000, 0,0 }, + { 462, 462, 0, 0, 6746, 2606,0 }, + { 463, 463, 0, 0, 40000, 213,0 }, + { 464, 464, 0, 0, 40000, 66,0 }, + { 465, 465, 0, 0, 40000, 100,0 }, + { 466, 466, 0, 0, 40000, 100,0 }, + { 467, 467, 0, 0, 5840, 806,0 }, + { 468, 468, 0, 0, 40000, 0,0 }, + { 469, 469, 0, 0, 40000, 0,0 }, + { 470, 470, 0, 0, 40000, 73,0 }, + { 471, 471, 0, 0, 40000, 133,0 }, + { 472, 472, 0, 0, 3320, 800,0 }, + { 473, 473, 0, 0, 40000, 173,0 }, + { 474, 474, 0, 0, 40000, 193,0 }, + { 475, 475, 0, 0, 2373, 800,0 }, + { 476, 476, 0, 0, 40000, 4986,0 }, + { 477, 477, 0, 0, 1180, 413,0 }, + { 478, 478, 0, 0, 3673, 1200,0 }, + { 479, 479, 0, 0, 973, 800,0 }, + { 480, 480, 0, 0, 7233, 2286,0 }, + { 481, 481, 0, 0, 40000, 73,0 }, + { 482, 482, 0, 0, 2526, 73,0 }, + { 483, 483, 0, 0, 393, 126,0 }, + { 484, 484, 0, 0, 40000, 200,0 }, + { 485, 485, 0, 0, 40000, 546,0 }, + { 486, 486, 0, 0, 1186, 413,0 }, + { 487, 487, 0, 0, 14166, 320,0 }, + { 488, 488, 0, 0, 8326, 646,0 }, + { 489, 489, 0, 0, 513, 206,0 }, + { 490, 490, 0, 0, 40000, 93,0 }, + { 491, 491, 50, 0, 1406, 353,0 }, + { 492, 492, 37, 0, 1040, 400,0 }, + { 493, 493, 39, 0, 406, 73,0 }, + { 494, 494, 39, 0, 3746, 860,0 }, + { 495, 495, 86, 0, 2133, 173,0 }, + { 496, 496, 43, 0, 140, 66,0 }, + { 127, 127, 24, 0, 513, 206,0 }, + { 127, 127, 29, 0, 520, 206,0 }, + { 497, 497, 50, 0, 340, 20,0 }, + { 498, 498, 30, 0, 5306, 1266,0 }, + { 498, 498, 33, 0, 3773, 886,0 }, + { 498, 498, 38, 0, 3746, 860,0 }, + { 498, 498, 42, 0, 3793, 906,0 }, + { 499, 499, 24, 0, 266, 0,0 }, + { 499, 499, 27, 0, 260, 153,0 }, + { 499, 499, 29, 0, 260, 153,0 }, + { 499, 499, 32, 0, 260, 153,0 }, + { 500, 500, 32, 0, 106, 0,0 }, + { 501, 501, 53, 0, 373, 186,0 }, + { 501, 501, 57, 0, 380, 193,0 }, + { 502, 502, 60, 0, 286, 133,0 }, + { 503, 503, 55, 0, 460, 126,0 }, + { 486, 486, 85, 0, 813, 293,0 }, + { 504, 504, 90, 0, 1580, 546,0 }, + { 505, 505, 84, 0, 246, 120,0 }, + { 506, 506, 48, 0, 826, 646,0 }, + { 507, 507, 48, 0, 266, 213,0 }, + { 132, 132, 72, 0, 126, 66,0 }, + { 508, 508, 72, 0, 106, 0,0 }, + { 509, 509, 72, 0, 100, 0,0 }, + { 510, 510, 63, 0, 1860, 633,0 }, + { 510, 510, 65, 0, 1853, 633,0 }, + { 511, 511, 79, 0, 1573, 553,0 }, + { 512, 512, 38, 0, 520, 793,0 }, + { 513, 513, 94, 0, 380, 160,0 }, + { 514, 514, 87, 0, 433, 306,0 }, + { 514, 514, 94, 0, 380, 273,0 }, + { 515, 515, 80, 0, 546, 273,0 }, + { 516, 516, 47, 0, 506, 200,0 }, + { 517, 517, 61, 0, 286, 133,0 }, + { 517, 517, 68, 0, 246, 120,0 }, + { 518, 518, 61, 0, 513, 206,0 }, + { 518, 518, 68, 0, 433, 180,0 }, + { 499, 499, 60, 0, 220, 133,0 }, + { 519, 519, 60, 0, 153, 46,0 }, + { 520, 520, 36, 0, 200, 20,0 }, + { 520, 520, 60, 0, 173, 20,0 }, + { 521, 521, 60, 0, 173, 20,0 }, + { 522, 522, 68, 0, 126, 26,0 }, + { 523, 523, 71, 0, 160, 186,0 }, + { 523, 523, 72, 0, 160, 186,0 }, + { 524, 524,101, 0, 966, 353,0 }, + { 525, 525, 36, 0, 3333, 480,0 }, + { 526, 526, 25, 0, 40000, 2293,0 }, + { 527, 527, 37, 0, 2106, 426,0 }, + { 528, 528, 36, 0, 720, 266,0 }, + { 528, 528, 41, 0, 713, 266,0 }, + { 529, 529, 84, 0, 173, 60,0 }, + { 530, 530, 54, 0, 40000, 0,0 }, + { 481, 481, 48, 0, 40000, 73,0 }, + { 531, 531, 0, 0, 10060, 1266,0 }, + { 532, 532, 0, 0, 4600, 606,0 }, + { 533, 533, 0, 0, 40000, 253,0 }, + { 534, 534, 0, 0, 40000, 73,0 }, + { 535, 535, 0, 0, 40000, 66,0 }, + { 536, 536, 0, 0, 40000, 80,0 }, + { 537, 537, 0, 0, 9413, 1393,0 }, + { 538, 538, 0, 0, 9000, 66,0 }, + { 539, 539, 0, 0, 40000, 0,0 }, + { 540, 540, 0, 0, 40000, 80,0 }, + { 541, 541, 0, 0, 40000, 120,0 }, + { 542, 542, 0, 0, 253, 73,0 }, + { 543, 543, 0, 0, 40000, 73,0 }, + { 544, 544, 0, 0, 18280, 800,0 }, + { 545, 545, 0, 0, 40000, 1133,0 }, + { 546, 546, 0, 0, 40000, 1226,0 }, + { 547, 547, 0, 0, 40000, 153,0 }, + { 135, 135, 49, 0, 3633, 1186,0 }, + { 548, 548, 35, 0, 2193, 80,0 }, + { 549, 549, 41, 0, 73, 0,0 }, + { 366, 366, 38, 0, 406, 246,0 }, + { 550, 550, 39, 0, 106, 20,0 }, + { 551, 551, 49, 0, 200, 133,0 }, + { 408, 408, 59, 0, 780, 326,0 }, + { 552, 552, 24, 0, 40000, 0,0 }, + { 552, 552, 27, 0, 40000, 0,0 }, + { 552, 552, 29, 0, 40000, 0,0 }, + { 552, 552, 32, 0, 40000, 0,0 }, + { 553, 553, 84, 0, 200, 33,0 }, + { 512, 512, 79, 0, 346, 460,0 }, + { 554, 554, 61, 0, 400, 126,0 }, + { 554, 554, 68, 0, 353, 120,0 }, + { 555, 555, 36, 0, 146, 86,0 }, + { 555, 555, 60, 0, 113, 0,0 }, + { 556, 556, 36, 0, 273, 53,0 }, + { 115, 115, 37, 0, 4580, 1513,0 }, + { 557, 557, 0, 0, 3806, 73,0 }, + { 558, 558, 0, 0, 40000, 0,0 }, + { 559, 559, 0, 0, 40000, 66,0 }, + { 560, 560, 0, 0, 5886, 133,0 }, + { 561, 561, 0, 0, 253, 26,0 }, + { 562, 562, 0, 0, 3246, 753,0 }, + { 563, 563, 0, 0, 40000, 100,0 }, + { 564, 564, 0, 0, 1620, 366,0 }, + { 565, 565, 0, 0, 40000, 0,0 }, + { 566, 566, 0, 0, 40000, 0,0 }, + { 567, 567, 0, 0, 40000, 0,0 }, + { 568, 568, 0, 0, 40000, 80,0 }, + { 569, 569, 0, 0, 760, 340,0 }, + { 570, 570, 0, 0, 40000, 0,0 }, + { 571, 571, 0, 0, 40000, 0,0 }, + { 572, 572, 0, 0, 40000, 0,0 }, + { 356, 356, 0, 0, 1893, 646,0 }, + { 573, 573, 0, 0, 40000, 93,0 }, + { 574, 574, 0, 0, 40000, 93,0 }, + { 575, 575, 0, 0, 40000, 200,0 }, + { 576, 576, 0, 0, 40000, 200,0 }, + { 577, 577, 0, 0, 40000, 126,0 }, + { 578, 578, 0, 0, 40000, 353,0 }, + { 579, 579, 0, 0, 40000, 346,0 }, + { 580, 580, 0, 0, 40000, 353,0 }, + { 581, 581, 0, 0, 40000, 100,0 }, + { 582, 582, 0, 0, 40000, 133,0 }, + { 583, 583, 0, 0, 2286, 713,0 }, + { 584, 584, 0, 0, 40000, 193,0 }, + { 585, 585, 0, 0, 40000, 0,0 }, + { 516, 516, 0, 0, 633, 240,0 }, + { 586, 586, 0, 0, 40000, 73,0 }, + { 587, 587, 0, 0, 40000, 73,0 }, + { 588, 588, 0, 0, 40000, 73,0 }, + { 498, 498, 26, 0, 5293, 1253,0 }, + { 494, 494, 35, 0, 3800, 913,0 }, + { 350, 350, 41, 0, 380, 153,0 }, + { 353, 353, 48, 0, 173, 100,0 }, + { 354, 354, 67, 0, 246, 120,0 }, + { 502, 502, 24, 0, 340, 146,0 }, + { 346, 346, 36, 0, 406, 73,0 }, + { 346, 346, 38, 0, 406, 20,0 }, + { 346, 346, 40, 0, 406, 73,0 }, + { 346, 346, 42, 0, 406, 20,0 }, + { 346, 346, 44, 0, 306, 20,0 }, + { 510, 510, 55, 0, 1866, 646,0 }, + { 346, 346, 46, 0, 306, 20,0 }, + { 136, 136, 80, 0, 1600, 573,0 }, + { 486, 486, 24, 0, 1193, 426,0 }, + { 153, 153, 50, 0, 106, 40,0 }, + { 346, 346, 24, 0, 540, 73,0 }, + { 516, 516, 31, 0, 626, 240,0 }, + { 498, 498, 35, 0, 3760, 880,0 }, + { 517, 517, 60, 0, 286, 133,0 }, + { 530, 530, 36, 0, 40000, 0,0 }, + { 530, 530, 48, 0, 40000, 0,0 }, + { 589, 589, 0, 0, 40000, 0,0 }, + { 139, 139, 76, 0, 253, 106,0 }, + { 156, 156, 48, 0, 206, 80,0 }, + { 157, 157, 48, 0, 426, 106,0 }, + { 165, 165, 69, 0, 120, 66,0 }, + { 167, 167, 75, 0, 546, 306,0 }, + { 590, 590, 0, 0, 40000, 0,0 }, + { 591, 591, 0, 0, 15486, 1580,0 }, + { 592, 592, 0, 0, 3446, 106,0 }, + { 593, 593, 0, 0, 1926, 146,0 }, + { 594, 594, 0, 0, 7293, 2380,0 }, + { 595, 595, 0, 0, 7613, 1566,0 }, + { 596, 596, 0, 0, 1153, 460,0 }, + { 597, 597, 0, 0, 1166, 400,0 }, + { 598, 598, 0, 0, 40000, 73,0 }, + { 599, 599, 0, 0, 40000, 766,0 }, + { 600, 600, 0, 0, 40000, 80,0 }, + { 601, 601, 0, 0, 1840, 513,0 }, + { 602, 602, 0, 0, 40000, 0,0 }, + { 603, 603, 0, 0, 4480, 733,0 }, + { 604, 604, 0, 0, 18226, 786,0 }, + { 605, 605, 0, 0, 4333, 233,0 }, + { 606, 606, 0, 0, 40000, 106,0 }, + { 607, 607, 0, 0, 40000, 366,0 }, + { 608, 608, 0, 0, 40000, 200,0 }, + { 609, 609, 0, 0, 713, 200,0 }, + { 610, 610, 0, 0, 8866, 1366,0 }, + { 611, 611, 0, 0, 2300, 73,0 }, + { 612, 612, 0, 0, 40000, 126,0 }, + { 613, 613, 0, 0, 40000, 1413,0 }, + { 614, 614, 0, 0, 40000, 333,0 }, + { 615, 615, 0, 0, 40000, 333,0 }, + { 616, 616, 0, 0, 40000, 26,0 }, + { 617, 617, 0, 0, 40000, 40,0 }, + { 618, 618, 0, 0, 4240, 353,0 }, + { 619, 619, 0, 0, 40000, 0,0 }, + { 620, 620, 0, 0, 40000, 73,0 }, + { 621, 621, 0, 0, 9020, 60,0 }, + { 622, 622, 0, 0, 3020, 0,0 }, + { 623, 623, 0, 0, 40000, 60,0 }, + { 624, 624, 0, 0, 40000, 73,0 }, + { 625, 625, 0, 0, 40000, 60,0 }, + { 626, 626, 0, 0, 40000, 53,0 }, + { 627, 627, 0, 0, 40000, 0,0 }, + { 628, 628, 0, 0, 40000, 66,0 }, + { 629, 629, 0, 0, 40000, 66,0 }, + { 630, 630, 0, 0, 5913, 426,0 }, + { 631, 631, 0, 0, 40000, 246,0 }, + { 632, 632, 0, 0, 40000, 206,0 }, + { 633, 633, 0, 0, 40000, 0,0 }, + { 634, 634, 0, 0, 2453, 780,0 }, + { 635, 635, 0, 0, 4740, 240,0 }, + { 636, 636, 0, 0, 1840, 353,0 }, + { 637, 637, 0, 0, 40000, 86,0 }, + { 638, 638, 0, 0, 3446, 1786,0 }, + { 346, 346, 0, 0, 540, 20,0 }, + { 639, 639, 0, 0, 7406, 2486,0 }, + { 404, 404, 0, 0, 1220, 466,0 }, + { 506, 506, 0, 0, 1000, 813,0 }, + { 639, 639, 60, 0, 2666, 913,0 }, + { 639, 639, 79, 0, 1366, 486,0 }, + { 640, 640, 65, 0, 2053, 646,0 }, + { 486, 486, 31, 0, 1206, 440,0 }, + { 486, 486, 36, 0, 1200, 433,0 }, + { 640, 640, 72, 0, 1713, 520,0 }, + { 136, 136, 79, 0, 1580, 560,0 }, + { 148, 148, 57, 0, 520, 206,0 }, + { 150, 150, 53, 0, 500, 193,0 }, + { 641, 641, 84, 0, 226, 66,0 }, + { 520, 520, 66, 0, 173, 20,0 }, + { 642, 642, 31, 0, 40000, 113,0 }, + { 642, 642, 29, 0, 40000, 113,0 }, + { 356, 356, 31, 0, 1366, 486,0 }, + { 356, 356, 19, 0, 1866, 633,0 }, + { 643, 643, 31, 0, 40000, 73,0 }, + { 643, 643, 29, 0, 40000, 73,0 }, + { 644, 644, 31, 0, 2286, 400,0 }, + { 644, 644, 35, 0, 2313, 420,0 }, + { 644, 644, 40, 0, 2353, 433,0 }, + { 644, 644, 47, 0, 1860, 346,0 }, + { 516, 516, 32, 0, 626, 240,0 }, + { 516, 516, 43, 0, 506, 200,0 }, + { 495, 495, 26, 0, 3180, 240,0 }, + { 495, 495, 44, 0, 2553, 206,0 }, + { 496, 496, 26, 0, 160, 73,0 }, + { 496, 496, 51, 0, 146, 66,0 }, + { 496, 496, 39, 0, 160, 73,0 }, + { 495, 495, 30, 0, 3180, 240,0 }, + { 645, 645, 44, 0, 1880, 653,0 }, + { 645, 645, 43, 0, 1886, 653,0 }, + { 646, 646, 0, 0, 2393, 833,0 }, + { 647, 647, 0, 0, 4693, 26,0 }, + { 648, 648, 0, 0, 2306, 773,0 }, + { 649, 649, 0, 0, 40000, 120,0 }, + { 650, 650, 0, 0, 40000, 66,0 }, + { 651, 651, 0, 0, 5866, 1206,0 }, + { 652, 652, 0, 0, 40000, 426,0 }, + { 653, 653, 0, 0, 1873, 633,0 }, + { 654, 654, 0, 0, 40000, 66,0 }, + { 655, 655, 0, 0, 40000, 73,0 }, + { 656, 656, 0, 0, 40000, 73,0 }, + { 657, 657, 0, 0, 40000, 0,0 }, + { 658, 658, 0, 0, 2040, 380,0 }, + { 659, 659, 0, 0, 40000, 73,0 }, + { 660, 660, 0, 0, 3720, 1260,0 }, + { 661, 661, 0, 0, 4080, 1046,0 }, + { 662, 662, 0, 0, 8693, 4666,0 }, + { 663, 663, 0, 0, 1926, 73,0 }, + { 664, 664, 0, 0, 8326, 646,0 }, + { 665, 665, 0, 0, 40000, 240,0 }, + { 666, 666, 0, 0, 40000, 226,0 }, + { 667, 667, 0, 0, 40000, 220,0 }, + { 668, 668, 0, 0, 40000, 0,0 }, + { 669, 669, 0, 0, 40000, 193,0 }, + { 670, 670, 0, 0, 880, 20,0 }, + { 671, 671, 0, 0, 4873, 120,0 }, + { 672, 672, 0, 0, 40000, 413,0 }, + { 673, 673, 0, 0, 700, 106,0 }, + { 674, 674, 0, 0, 700, 100,0 }, + { 675, 675, 0, 0, 40000, 126,0 }, + { 676, 676, 0, 0, 8113, 806,0 }, + { 677, 677, 0, 0, 8900, 80,0 }, + { 678, 678, 0, 0, 1893, 653,0 }, + { 679, 679, 0, 0, 3973, 206,0 }, + { 680, 680, 0, 0, 40000, 173,0 }, + { 681, 681, 0, 0, 40000, 73,0 }, + { 682, 682, 0, 0, 40000, 93,0 }, + { 683, 683, 0, 0, 1606, 640,0 }, + { 684, 684, 0, 0, 15486, 1580,0 }, + { 685, 685, 0, 0, 40000, 346,0 }, + { 686, 686, 0, 0, 40000, 786,0 }, + { 687, 687, 0, 0, 386, 240,0 }, + { 688, 688, 0, 0, 40000, 2066,0 }, + { 689, 689, 0, 0, 15453, 73,0 }, + { 690, 690, 0, 0, 1206, 240,0 }, + { 691, 691, 0, 0, 8866, 1366,0 }, + { 692, 692, 0, 0, 5913, 2253,0 }, + { 693, 693, 0, 0, 773, 106,0 }, + { 694, 694, 0, 0, 3793, 73,0 }, + { 695, 695, 0, 0, 40000, 73,0 }, + { 645, 645, 0, 0, 3633, 1180,0 }, + { 696, 696, 0, 0, 40000, 80,0 }, + { 697, 697, 0, 0, 40000, 0,0 }, + { 698, 698, 0, 0, 40000, 66,0 }, + { 699, 699, 0, 0, 40000, 66,0 }, + { 700, 700, 0, 0, 106, 0,0 }, + { 701, 701, 0, 0, 40000, 200,0 }, + { 702, 702, 0, 0, 3913, 73,0 }, + { 703, 703, 0, 0, 40000, 73,0 }, + { 704, 704, 0, 0, 40000, 73,0 }, + { 705, 705, 0, 0, 40000, 73,0 }, + { 706, 706, 0, 0, 40000, 66,0 }, + { 707, 707, 0, 0, 40000, 313,0 }, + { 708, 708, 0, 0, 40000, 100,0 }, + { 709, 709, 0, 0, 40000, 213,0 }, + { 710, 710, 0, 0, 40000, 53,0 }, + { 711, 711, 0, 0, 40000, 40,0 }, + { 712, 712, 0, 0, 40000, 73,0 }, + { 713, 713, 0, 0, 40000, 140,0 }, + { 714, 714, 0, 0, 40000, 606,0 }, + { 715, 715, 0, 0, 40000, 226,0 }, + { 716, 716, 0, 0, 3746, 1273,0 }, + { 717, 717, 0, 0, 40000, 80,0 }, + { 718, 718, 0, 0, 2360, 806,0 }, + { 719, 719, 0, 0, 1186, 420,0 }, + { 720, 720, 0, 0, 12533, 1953,0 }, + { 721, 721, 0, 0, 973, 1280,0 }, + { 722, 722, 0, 0, 40000, 426,0 }, + { 723, 723, 0, 0, 40000, 53,0 }, + { 724, 724, 0, 0, 40000, 66,0 }, + { 725, 725, 0, 0, 1246, 73,0 }, + { 726, 726, 0, 0, 3726, 1246,0 }, + { 727, 727, 0, 0, 2346, 813,0 }, + { 728, 728, 0, 0, 1206, 433,0 }, + { 507, 507, 0, 0, 306, 246,0 }, + { 512, 512, 0, 0, 526, 840,0 }, + { 729, 729, 0, 0, 14793, 4933,0 }, + { 730, 730, 0, 0, 14640, 4806,0 }, + { 731, 731, 0, 0, 5233, 633,0 }, + { 732, 732, 0, 0, 40000, 2513,0 }, + { 733, 733, 0, 0, 40000, 820,0 }, + { 734, 734, 0, 0, 40000, 0,0 }, + { 735, 735, 0, 0, 1726, 793,0 }, + { 736, 736, 0, 0, 513, 20,0 }, + { 737, 737, 0, 2, 6, 0,0 }, + { 738, 738, 38, 0, 1020, 413,0 }, + { 739, 739, 44, 0, 220, 33,0 }, + { 500, 500, 58, 0, 100, 0,0 }, + { 740, 740, 24, 0, 513, 206,0 }, + { 741, 741, 60, 0, 220, 26,0 }, + { 736, 736, 44, 0, 286, 20,0 }, + { 742, 742, 25, 0, 626, 246,0 }, + { 743, 743, 60, 0, 146, 86,0 }, + { 742, 742, 30, 0, 626, 240,0 }, + { 377, 377, 60, 0, 446, 626,0 }, + { 742, 742, 33, 0, 620, 226,0 }, + { 744, 744, 60, 0, 220, 113,0 }, + { 742, 742, 35, 0, 620, 233,0 }, + { 742, 742, 37, 0, 633, 246,0 }, + { 745, 745, 0, 0, 1880, 640,0 }, + { 742, 742, 40, 0, 640, 260,0 }, + { 746, 746,102, 0, 960, 300,0 }, + { 747, 747, 80, 0, 1106, 126,0 }, + { 377, 377, 0, 0, 500, 760,0 }, + { 748, 748, 56, 0, 100, 0,0 }, + { 749, 749, 0, 0, 973, 1300,0 }, + { 746, 746,100, 0, 960, 340,0 }, + { 750, 750, 40, 0, 626, 240,0 }, + { 750, 750, 35, 0, 626, 240,0 }, + { 751, 751, 29, 0, 206, 106,0 }, + { 750, 750, 29, 0, 633, 240,0 }, + { 750, 750, 22, 0, 640, 233,0 }, + { 500, 500, 0, 0, 106, 0,0 }, + { 752, 752, 0, 0, 206, 26,0 }, + { 753, 753, 84, 0, 166, 20,0 }, + { 754, 754, 84, 0, 1580, 553,0 }, + { 755, 755, 0, 0, 633, 233,0 }, + { 755, 755, 71, 0, 440, 180,0 }, + { 755, 755, 53, 0, 513, 200,0 }, + { 755, 755, 48, 0, 520, 206,0 }, + { 756, 756, 95, 0, 286, 20,0 }, + { 757, 757, 95, 0, 1880, 20,0 }, + { 758, 758, 0, 0, 14413, 333,0 }, + { 759, 759, 0, 0, 14453, 360,0 }, + { 760, 760, 0, 0, 14940, 353,0 }, + { 761, 761, 0, 0, 7286, 340,0 }, + { 762, 762, 0, 0, 14700, 60,0 }, + { 763, 763, 0, 0, 14506, 340,0 }, + { 764, 764, 0, 0, 14706, 200,0 }, + { 765, 765, 0, 0, 40000, 0,0 }, + { 766, 766, 0, 0, 2900, 426,0 }, + { 767, 767, 0, 0, 2986, 753,0 }, + { 768, 768, 0, 0, 1706, 680,0 }, + { 769, 769, 0, 0, 14646, 1253,0 }, + { 770, 770, 0, 0, 1713, 486,0 }, + { 771, 771, 0, 0, 966, 346,0 }, + { 772, 772, 0, 0, 3453, 766,0 }, + { 773, 773, 0, 0, 2866, 486,0 }, + { 774, 774, 0, 0, 40000, 73,0 }, + { 775, 775, 0, 0, 40000, 73,0 }, + { 776, 776, 0, 0, 40000, 166,0 }, + { 777, 777, 0, 0, 40000, 126,0 }, + { 778, 778, 0, 0, 40000, 113,0 }, + { 779, 779, 0, 0, 40000, 113,0 }, + { 780, 780, 0, 0, 40000, 93,0 }, + { 781, 781, 0, 0, 40000, 200,0 }, + { 782, 782, 0, 0, 7186, 93,0 }, + { 783, 783, 0, 0, 6406, 120,0 }, + { 784, 784, 0, 0, 40000, 0,0 }, + { 785, 785, 0, 0, 40000, 0,0 }, + { 786, 786, 0, 0, 1220, 73,0 }, + { 787, 787, 0, 0, 40000, 0,0 }, + { 788, 788, 0, 0, 17566, 66,0 }, + { 789, 789, 0, 0, 2333, 26,0 }, + { 790, 790, 0, 0, 4560, 153,0 }, + { 791, 791, 0, 0, 40000, 0,0 }, + { 792, 792, 0, 0, 40000, 0,0 }, + { 793, 793, 0, 0, 40000, 0,0 }, + { 794, 794, 0, 0, 2506, 126,0 }, + { 795, 795, 0, 0, 2513, 126,0 }, + { 796, 796, 0, 0, 40000, 0,0 }, + { 797, 797, 0, 0, 3386, 80,0 }, + { 798, 798, 0, 0, 40000, 100,0 }, + { 799, 799, 0, 0, 40000, 100,0 }, + { 800, 800, 0, 0, 40000, 120,0 }, + { 801, 801, 0, 0, 40000, 0,0 }, + { 802, 802, 0, 0, 40000, 200,0 }, + { 803, 803, 0, 0, 1080, 180,0 }, + { 804, 804, 0, 0, 3620, 1166,0 }, + { 805, 805, 0, 0, 1186, 393,0 }, + { 806, 806, 0, 0, 40000, 213,0 }, + { 807, 807, 0, 0, 40000, 426,0 }, + { 808, 808, 0, 0, 40000, 146,0 }, + { 809, 809, 0, 0, 40000, 146,0 }, + { 810, 810, 0, 0, 40000, 60,0 }, + { 811, 811, 0, 0, 40000, 113,0 }, + { 812, 812, 0, 0, 40000, 93,0 }, + { 813, 813, 0, 0, 1186, 153,0 }, + { 814, 814, 0, 0, 40000, 0,0 }, + { 815, 815, 0, 0, 40000, 80,0 }, + { 816, 816, 0, 0, 40000, 80,0 }, + { 817, 817, 0, 0, 40000, 46,0 }, + { 818, 818, 0, 0, 40000, 0,0 }, + { 819, 819, 0, 0, 40000, 66,0 }, + { 820, 820, 0, 0, 40000, 126,0 }, + { 821, 821, 0, 0, 40000, 213,0 }, + { 822, 822, 0, 0, 40000, 80,0 }, + { 823, 823, 0, 0, 40000, 73,0 }, + { 824, 824, 0, 0, 40000, 73,0 }, + { 825, 825, 0, 0, 40000, 100,0 }, + { 826, 826, 0, 0, 40000, 93,0 }, + { 827, 827, 0, 0, 40000, 73,0 }, + { 828, 828, 0, 0, 40000, 73,0 }, + { 829, 829, 0, 0, 40000, 80,0 }, + { 830, 830, 0, 0, 40000, 80,0 }, + { 831, 831, 0, 0, 40000, 80,0 }, + { 832, 832, 0, 0, 40000, 73,0 }, + { 833, 833, 0, 0, 40000, 80,0 }, + { 834, 834, 0, 0, 40000, 86,0 }, + { 835, 835, 0, 0, 40000, 100,0 }, + { 836, 836, 0, 0, 40000, 100,0 }, + { 837, 837, 0, 0, 40000, 140,0 }, + { 838, 838, 0, 0, 40000, 73,0 }, + { 839, 839, 0, 0, 40000, 0,0 }, + { 840, 840, 0, 0, 40000, 93,0 }, + { 841, 841, 0, 0, 40000, 0,0 }, + { 842, 842, 0, 0, 40000, 0,0 }, + { 843, 843, 0, 0, 40000, 73,0 }, + { 844, 844, 0, 0, 40000, 66,0 }, + { 845, 845, 0, 0, 40000, 0,0 }, + { 846, 846, 0, 0, 40000, 193,0 }, + { 847, 847, 0, 0, 40000, 340,0 }, + { 848, 848, 0, 0, 40000, 233,0 }, + { 849, 849, 0, 0, 40000, 80,0 }, + { 850, 850, 0, 0, 40000, 186,0 }, + { 851, 851, 0, 0, 9973, 426,0 }, + { 852, 852, 0, 0, 40000, 200,0 }, + { 853, 853, 0, 0, 40000, 400,0 }, + { 854, 854, 0, 0, 14633, 200,0 }, + { 855, 855, 0, 0, 40000, 333,0 }, + { 856, 856, 0, 0, 4620, 800,0 }, + { 857, 857, 0, 0, 8940, 386,0 }, + { 858, 858, 0, 0, 8966, 740,0 }, + { 859, 859, 0, 0, 40000, 273,0 }, + { 860, 860, 0, 0, 40000, 126,0 }, + { 861, 861, 0, 0, 40000, 400,0 }, + { 862, 862, 0, 0, 4480, 213,0 }, + { 863, 863, 0, 0, 633, 100,0 }, + { 864, 864, 0, 0, 3740, 353,0 }, + { 865, 865, 0, 0, 2333, 406,0 }, + { 866, 866, 0, 0, 1933, 566,0 }, + { 867, 867, 0, 0, 40000, 93,0 }, + { 868, 868, 0, 0, 40000, 106,0 }, + { 869, 869, 0, 0, 40000, 100,0 }, + { 870, 870, 0, 0, 3093, 240,0 }, + { 871, 871, 0, 0, 513, 93,0 }, + { 872, 872, 0, 0, 700, 180,0 }, + { 361, 361, 0, 0, 373, 40,0 }, + { 873, 873, 0, 0, 1046, 446,0 }, + { 874, 874, 0, 0, 1886, 520,0 }, + { 875, 875, 0, 0, 1226, 366,0 }, + { 876, 876, 0, 0, 4193, 73,0 }, + { 877, 877, 0, 0, 826, 120,0 }, + { 878, 878, 0, 0, 280, 146,0 }, + { 879, 879, 0, 0, 5266, 806,0 }, + { 880, 880, 0, 0, 386, 80,0 }, + { 881, 881, 0, 0, 40000, 100,0 }, + { 882, 882, 0, 0, 40000, 413,0 }, + { 883, 883, 0, 0, 40000, 0,0 }, + { 884, 884, 36, 0, 233, 80,0 }, + { 885, 885, 48, 0, 193, 93,0 }, + { 885, 885, 36, 0, 226, 100,0 }, + { 886, 886, 36, 0, 113, 0,0 }, + { 887, 887, 32, 0, 133, 40,0 }, + { 767, 767, 96, 0, 1760, 480,0 }, + { 888, 888, 30, 0, 246, 40,0 }, + { 889, 889, 35, 0, 420, 140,0 }, + { 890, 890, 60, 0, 240, 60,0 }, + { 884, 884, 59, 0, 146, 20,0 }, + { 890, 890, 44, 0, 240, 60,0 }, + { 891, 891, 41, 0, 713, 273,0 }, + { 892, 892, 47, 0, 173, 93,0 }, + { 891, 891, 44, 0, 513, 206,0 }, + { 891, 891, 48, 0, 506, 200,0 }, + { 893, 893, 62, 0, 1926, 93,0 }, + { 891, 891, 51, 0, 520, 200,0 }, + { 891, 891, 54, 0, 513, 206,0 }, + { 894, 894, 40, 0, 1280, 793,0 }, + { 891, 891, 57, 0, 380, 160,0 }, + { 895, 895, 97, 0, 233, 106,0 }, + { 896, 896, 50, 0, 220, 93,0 }, + { 376, 376, 60, 0, 1573, 713,0 }, + { 897, 897, 53, 0, 126, 73,0 }, + { 898, 898, 46, 0, 173, 133,0 }, + { 897, 897, 57, 0, 126, 33,0 }, + { 899, 899, 42, 0, 626, 233,0 }, + { 899, 899, 37, 0, 633, 240,0 }, + { 900, 900, 41, 0, 626, 240,0 }, + { 900, 900, 37, 0, 626, 240,0 }, + { 871, 871, 77, 0, 173, 40,0 }, + { 871, 871, 72, 0, 173, 40,0 }, + { 388, 388, 70, 0, 213, 86,0 }, + { 901, 901, 39, 0, 260, 26,0 }, + { 902, 902, 36, 0, 1093, 73,0 }, + { 903, 903, 46, 0, 120, 73,0 }, + { 904, 904, 48, 0, 766, 80,0 }, + { 905, 905, 85, 0, 126, 26,0 }, + { 361, 361, 66, 0, 180, 26,0 }, + { 906, 906, 41, 0, 193, 73,0 }, + { 907, 907, 41, 0, 333, 106,0 }, + { 908, 908, 81, 0, 160, 26,0 }, + { 400, 400, 10, 0, 1186, 413,0 }, + { 886, 886, 60, 0, 100, 0,0 }, + { 873, 873, 53, 0, 846, 360,0 }, + { 909, 909, 0, 0, 5593, 340,0 }, + { 910, 910, 0, 0, 14646, 346,0 }, + { 911, 911, 0, 0, 6826, 280,0 }, + { 912, 912, 0, 0, 7000, 306,0 }, + { 913, 913, 0, 0, 8793, 133,0 }, + { 914, 914, 0, 0, 14680, 346,0 }, + { 915, 915, 0, 0, 7246, 126,0 }, + { 916, 916, 0, 0, 40000, 0,0 }, + { 917, 917, 0, 0, 1866, 433,0 }, + { 362, 362, 0, 0, 1106, 340,0 }, + { 918, 918, 0, 0, 1053, 273,0 }, + { 919, 919, 0, 0, 14513, 1213,0 }, + { 920, 920, 0, 0, 1886, 646,0 }, + { 921, 921, 0, 0, 926, 313,0 }, + { 922, 922, 0, 0, 2340, 806,0 }, + { 923, 923, 0, 0, 2966, 553,0 }, + { 924, 924, 0, 0, 40000, 66,0 }, + { 925, 925, 0, 0, 40000, 73,0 }, + { 926, 926, 0, 0, 40000, 0,0 }, + { 927, 927, 0, 0, 40000, 126,0 }, + { 928, 928, 0, 0, 40000, 113,0 }, + { 929, 929, 0, 0, 40000, 113,0 }, + { 930, 930, 0, 0, 40000, 93,0 }, + { 931, 931, 0, 0, 40000, 113,0 }, + { 932, 932, 0, 0, 7200, 86,0 }, + { 933, 933, 0, 0, 5373, 106,0 }, + { 934, 934, 0, 0, 40000, 0,0 }, + { 935, 935, 0, 0, 40000, 0,0 }, + { 936, 936, 0, 0, 2380, 73,0 }, + { 937, 937, 0, 0, 40000, 0,0 }, + { 938, 938, 0, 0, 40000, 0,0 }, + { 939, 939, 0, 0, 6013, 53,0 }, + { 940, 940, 0, 0, 3713, 126,0 }, + { 941, 941, 0, 0, 17566, 26,0 }, + { 942, 942, 0, 0, 40000, 0,0 }, + { 943, 943, 0, 0, 40000, 0,0 }, + { 944, 944, 0, 0, 2506, 126,0 }, + { 945, 945, 0, 0, 3733, 73,0 }, + { 946, 946, 0, 0, 40000, 0,0 }, + { 947, 947, 0, 0, 3386, 80,0 }, + { 948, 948, 0, 0, 40000, 100,0 }, + { 949, 949, 0, 0, 40000, 100,0 }, + { 950, 950, 0, 0, 40000, 113,0 }, + { 951, 951, 0, 0, 40000, 0,0 }, + { 952, 952, 0, 0, 40000, 200,0 }, + { 953, 953, 0, 0, 1140, 213,0 }, + { 954, 954, 0, 0, 2140, 400,0 }, + { 955, 955, 0, 0, 813, 240,0 }, + { 956, 956, 0, 0, 40000, 100,0 }, + { 957, 957, 0, 0, 40000, 426,0 }, + { 958, 958, 0, 0, 40000, 0,0 }, + { 959, 959, 0, 0, 40000, 146,0 }, + { 960, 960, 0, 0, 40000, 120,0 }, + { 961, 961, 0, 0, 40000, 93,0 }, + { 962, 962, 0, 0, 1193, 153,0 }, + { 963, 963, 0, 0, 40000, 46,0 }, + { 964, 964, 0, 0, 40000, 80,0 }, + { 965, 965, 0, 0, 40000, 80,0 }, + { 966, 966, 0, 0, 40000, 20,0 }, + { 967, 967, 0, 0, 40000, 0,0 }, + { 968, 968, 0, 0, 40000, 93,0 }, + { 969, 969, 0, 0, 40000, 86,0 }, + { 970, 970, 0, 0, 40000, 213,0 }, + { 971, 971, 0, 0, 40000, 80,0 }, + { 972, 972, 0, 0, 40000, 73,0 }, + { 973, 973, 0, 0, 40000, 0,0 }, + { 974, 974, 0, 0, 40000, 93,0 }, + { 975, 975, 0, 0, 40000, 73,0 }, + { 976, 976, 0, 0, 40000, 73,0 }, + { 977, 977, 0, 0, 40000, 66,0 }, + { 978, 978, 0, 0, 40000, 66,0 }, + { 979, 979, 0, 0, 40000, 100,0 }, + { 980, 980, 0, 0, 40000, 73,0 }, + { 981, 981, 0, 0, 40000, 73,0 }, + { 982, 982, 0, 0, 40000, 80,0 }, + { 983, 983, 0, 0, 40000, 100,0 }, + { 984, 984, 0, 0, 40000, 100,0 }, + { 985, 985, 0, 0, 40000, 100,0 }, + { 986, 986, 0, 0, 40000, 80,0 }, + { 987, 987, 0, 0, 40000, 73,0 }, + { 988, 988, 0, 0, 40000, 0,0 }, + { 989, 989, 0, 0, 40000, 86,0 }, + { 990, 990, 0, 0, 40000, 0,0 }, + { 991, 991, 0, 0, 40000, 0,0 }, + { 992, 992, 0, 0, 40000, 80,0 }, + { 993, 993, 0, 0, 40000, 86,0 }, + { 994, 994, 0, 0, 40000, 0,0 }, + { 995, 995, 0, 0, 40000, 0,0 }, + { 996, 996, 0, 0, 40000, 333,0 }, + { 997, 997, 0, 0, 40000, 180,0 }, + { 998, 998, 0, 0, 40000, 80,0 }, + { 999, 999, 0, 0, 40000, 120,0 }, + {1000,1000, 0, 0, 10006, 460,0 }, + {1001,1001, 0, 0, 40000, 186,0 }, + {1002,1002, 0, 0, 40000, 400,0 }, + {1003,1003, 0, 0, 20333, 260,0 }, + {1004,1004, 0, 0, 40000, 373,0 }, + {1005,1005, 0, 0, 4520, 400,0 }, + {1006,1006, 0, 0, 8213, 306,0 }, + {1007,1007, 0, 0, 8646, 360,0 }, + {1008,1008, 0, 0, 40000, 160,0 }, + {1009,1009, 0, 0, 40000, 133,0 }, + {1010,1010, 0, 0, 40000, 400,0 }, + {1011,1011, 0, 0, 4473, 193,0 }, + {1012,1012, 0, 0, 1813, 0,0 }, + {1013,1013, 0, 0, 3726, 353,0 }, + {1014,1014, 0, 0, 4400, 373,0 }, + {1015,1015, 0, 0, 953, 166,0 }, + {1016,1016, 0, 0, 40000, 73,0 }, + {1017,1017, 0, 0, 40000, 100,0 }, + {1018,1018, 0, 0, 40000, 100,0 }, + {1019,1019, 0, 0, 3100, 240,0 }, + { 444, 444, 0, 0, 513, 93,0 }, + {1020,1020, 0, 0, 626, 180,0 }, + { 449, 449, 0, 0, 373, 80,0 }, + { 453, 453, 0, 0, 40000, 0,0 }, + {1021,1021, 0, 0, 1020, 340,0 }, + {1022,1022, 0, 0, 1200, 366,0 }, + {1023,1023, 0, 0, 4193, 73,0 }, + {1024,1024, 0, 0, 820, 120,0 }, + {1025,1025, 0, 0, 680, 213,0 }, + {1026,1026, 0, 0, 5260, 806,0 }, + {1027,1027, 0, 0, 9193, 86,0 }, + {1028,1028, 0, 0, 40000, 100,0 }, + {1029,1029, 0, 0, 40000, 426,0 }, + {1030,1030, 0, 0, 40000, 260,0 }, + {1031,1031, 0, 0, 3480, 66,0 }, + {1032,1032, 32, 0, 133, 46,0 }, + {1033,1033, 30, 0, 200, 40,0 }, + {1034,1034, 96, 0, 146, 73,0 }, + {1035,1035, 60, 0, 553, 186,0 }, + {1036,1036, 0, 0, 13193, 260,0 }, + {1037,1037, 0, 0, 40000, 100,0 }, + {1038,1038, 0, 0, 7980, 66,0 }, + {1039,1039, 0, 0, 40000, 0,0 }, + {1040,1040, 0, 0, 980, 340,0 }, + {1041,1041, 0, 0, 7413, 2480,0 }, + {1042,1042, 0, 0, 2906, 520,0 }, + {1043,1043, 0, 0, 40000, 73,0 }, + {1044,1044, 0, 0, 40000, 53,0 }, + {1045,1045, 0, 0, 40000, 113,0 }, + {1046,1046, 0, 0, 5380, 113,0 }, + {1047,1047, 0, 0, 40000, 0,0 }, + {1048,1048, 0, 0, 2366, 73,0 }, + {1049,1049, 0, 0, 40000, 0,0 }, + {1050,1050, 0, 0, 18293, 80,0 }, + {1051,1051, 0, 0, 18466, 146,0 }, + {1052,1052, 0, 0, 9220, 73,0 }, + {1053,1053, 0, 0, 40000, 240,0 }, + {1054,1054, 0, 0, 40000, 0,0 }, + {1055,1055, 0, 0, 1086, 126,0 }, + {1056,1056, 0, 0, 3766, 73,0 }, + {1057,1057, 0, 0, 1186, 226,0 }, + {1058,1058, 0, 0, 3373, 73,0 }, + {1059,1059, 0, 0, 40000, 246,0 }, + {1060,1060, 0, 0, 340, 220,0 }, + {1061,1061, 0, 0, 1186, 386,0 }, + {1062,1062, 0, 0, 40000, 253,0 }, + {1063,1063, 0, 0, 40000, 440,0 }, + {1064,1064, 0, 0, 40000, 46,0 }, + {1065,1065, 0, 0, 40000, 80,0 }, + {1066,1066, 0, 0, 40000, 126,0 }, + {1067,1067, 0, 0, 40000, 133,0 }, + {1068,1068, 0, 0, 40000, 93,0 }, + {1069,1069, 0, 0, 40000, 86,0 }, + {1070,1070, 0, 0, 40000, 93,0 }, + {1071,1071, 0, 0, 40000, 66,0 }, + {1072,1072, 0, 0, 40000, 93,0 }, + {1073,1073, 0, 0, 40000, 73,0 }, + {1074,1074, 0, 0, 40000, 173,0 }, + {1075,1075, 0, 0, 586, 193,0 }, + {1076,1076, 0, 0, 40000, 146,0 }, + {1077,1077, 0, 0, 18460, 73,0 }, + {1078,1078, 0, 0, 846, 93,0 }, + {1079,1079, 0, 0, 40000, 0,0 }, + {1080,1080, 0, 0, 40000, 86,0 }, + {1081,1081, 0, 0, 40000, 0,0 }, + {1082,1082, 0, 0, 40000, 353,0 }, + {1083,1083, 0, 0, 40000, 300,0 }, + {1084,1084, 0, 0, 40000, 320,0 }, + {1085,1085, 0, 0, 9920, 1553,0 }, + {1086,1086, 0, 0, 40000, 386,0 }, + {1087,1087, 0, 0, 40000, 0,0 }, + {1088,1088, 0, 0, 9980, 873,0 }, + {1089,1089, 0, 0, 40000, 386,0 }, + {1090,1090, 0, 0, 966, 126,0 }, + {1091,1091, 0, 0, 40000, 820,0 }, + {1092,1092, 0, 0, 8620, 366,0 }, + {1093,1093, 0, 0, 40000, 826,0 }, + {1094,1094, 0, 0, 40000, 433,0 }, + {1095,1095, 0, 0, 633, 73,0 }, + {1096,1096, 0, 0, 3693, 126,0 }, + {1097,1097, 0, 0, 40000, 0,0 }, + {1098,1098, 0, 0, 40000, 153,0 }, + {1099,1099, 0, 0, 40000, 0,0 }, + {1100,1100, 0, 0, 40000, 0,0 }, + {1101,1101, 0, 0, 40000, 306,0 }, + {1102,1102, 0, 0, 3666, 3093,0 }, + {1103,1103, 0, 0, 1873, 653,0 }, + {1104,1104, 0, 0, 40000, 0,0 }, + {1105,1105, 0, 0, 11293, 886,0 }, + {1106,1106, 0, 0, 40000, 546,0 }, + { 430, 430, 0, 0, 1146, 80,0 }, + {1107,1107, 35, 0, 580, 80,0 }, + {1090,1090, 77, 0, 280, 60,0 }, + {1090,1090, 72, 0, 280, 60,0 }, + {1108,1108, 0, 0, 10180, 600,0 }, + {1109,1109, 0, 0, 10053, 353,0 }, + {1110,1111, 0, 1, 9940, 480,0 }, + {1112,1113, 0, 1, 10620, 473,0.03125 }, + {1114,1114, 0, 0, 40000, 0,0 }, + {1115,1116, 0, 1, 9833, 220,0 }, + {1117,1117, 0, 0, 10286, 473,0 }, + {1118,1118, 0, 0, 7686, 93,0 }, + {1119,1119, 0, 0, 7220, 613,0 }, + {1120,1120, 0, 0, 11513, 1666,0 }, + {1121,1121, 0, 0, 5200, 1700,0 }, + {1122,1122, 0, 0, 10173, 626,0 }, + {1123,1123, 0, 0, 1206, 380,0 }, + {1124,1124, 0, 0, 1953, 866,0 }, + {1125,1125, 0, 0, 4686, 1586,0 }, + {1126,1126, 0, 0, 3786, 893,0 }, + {1127,1127, 0, 0, 40000, 126,0 }, + {1128,1128, 0, 0, 40000, 120,0 }, + {1129,1130, 0, 1, 40000, 146,0.15625 }, + {1131,1131, 0, 0, 40000, 433,0 }, + {1132,1132, 0, 0, 40000, 133,0 }, + {1133,1134, 0, 1, 40000, 126,-0.046875 }, + {1135,1135, 0, 0, 40000, 113,0 }, + {1136,1137, 0, 1, 40000, 253,2.5e-05 }, + {1138,1138, 0, 0, 18440, 240,0 }, + {1139,1139, 0, 0, 5213, 886,0 }, + {1140,1140, 0, 0, 1446, 113,0 }, + {1141,1141, 0, 0, 5233, 106,0 }, + {1142,1142, 0, 0, 5286, 266,0 }, + {1143,1143, 0, 0, 40000, 66,0 }, + {1144,1144, 0, 0, 40000, 66,0 }, + {1145,1145, 0, 0, 10593, 106,0 }, + {1146,1146, 0, 0, 2733, 160,0 }, + {1147,1147, 0, 0, 10313, 93,0 }, + {1148,1148, 0, 0, 40000, 0,0 }, + {1149,1150, 0, 1, 40000, 0,-0.03125 }, + {1151,1151, 0, 0, 40000, 53,0 }, + {1152,1152, 0, 0, 10560, 246,0 }, + {1153,1153, 0, 0, 2700, 153,0 }, + {1154,1154, 0, 1, 40000, 100,-0.15625 }, + {1155,1155, 0, 0, 40000, 73,0 }, + {1156,1156, 0, 0, 40000, 220,0 }, + {1157,1157, 0, 0, 40000, 140,0 }, + {1158,1158, 0, 0, 40000, 380,0 }, + {1159,1160, 0, 1, 40000, 400,0.171875 }, + {1161,1161, 0, 0, 40000, 0,0 }, + {1162,1162, 0, 0, 40000, 0,0 }, + {1163,1163, 0, 0, 4733, 906,0 }, + {1164,1165, 0, 1, 40000, 393,-0.125 }, + {1166,1167, 0, 1, 40000, 366,0.078125 }, + {1168,1168, 0, 1, 40000, 2453,-0.078125 }, + {1169,1170, 0, 1, 40000, 546,0.0625 }, + {1171,1172, 0, 1, 40000, 786,0.15625 }, + {1173,1173, 0, 0, 40000, 0,0 }, + {1174,1174, 0, 0, 40000, 513,0 }, + {1175,1176, 0, 1, 2300, 533,0 }, + {1177,1177, 0, 0, 40000, 80,0 }, + {1178,1178, 0, 0, 40000, 60,0 }, + {1179,1179, 0, 0, 40000, 0,0 }, + {1180,1180, 0, 0, 10653, 86,0 }, + {1181,1182, 0, 1, 40000, 0,2.5e-05 }, + {1183,1184, 0, 1, 40000, 86,0.046875 }, + {1185,1186, 0, 1, 40000, 0,0.09375 }, + {1187,1188, 0, 1, 40000, 0,0.09375 }, + {1189,1189, 0, 0, 40000, 133,0 }, + {1190,1190, 0, 0, 40000, 140,0 }, + {1191,1191, 0, 0, 40000, 73,0 }, + {1192,1192, 0, 0, 40000, 60,0 }, + {1193,1193, 0, 0, 40000, 106,0 }, + {1194,1194, 0, 0, 40000, 93,0 }, + {1195,1195, 0, 0, 40000, 66,0 }, + {1196,1196, 0, 0, 40000, 93,0 }, + {1197,1197, 0, 0, 40000, 60,0 }, + {1198,1198, 0, 0, 40000, 66,0 }, + {1199,1199, 0, 0, 40000, 120,0 }, + {1200,1200, 0, 0, 40000, 100,0 }, + {1201,1201, 0, 0, 40000, 86,0 }, + {1202,1202, 0, 0, 40000, 0,0 }, + {1203,1203, 0, 0, 40000, 233,0 }, + {1204,1204, 0, 0, 40000, 100,0 }, + {1205,1206, 0, 1, 40000, 266,0.03125 }, + {1207,1208, 0, 1, 40000, 260,-2.5e-05 }, + {1209,1209, 0, 0, 40000, 146,0 }, + {1210,1211, 0, 1, 40000, 60,0.03125 }, + {1212,1212, 0, 0, 40000, 53,0 }, + {1213,1214, 0, 1, 40000, 706,-0.09375 }, + {1215,1216, 0, 1, 40000, 660,-0.046875 }, + {1217,1217, 0, 0, 40000, 133,0 }, + {1218,1219, 0, 1, 40000, 426,0.03125 }, + {1220,1220, 0, 1, 40000, 0,0.03125 }, + {1221,1222, 0, 1, 40000, 260,0.171875 }, + {1223,1223, 0, 0, 40000, 0,0 }, + {1224,1224, 0, 0, 6100, 1580,0 }, + {1225,1150, 0, 1, 40000, 73,-0.03125 }, + {1226,1226, 0, 0, 40000, 1580,0 }, + {1227,1227, 0, 0, 40000, 40,0 }, + {1228,1229, 0, 1, 40000, 113,0.125 }, + {1230,1230, 0, 0, 2666, 846,0 }, + {1231,1232, 0, 1, 40000, 0,-0.03125 }, + {1233,1234, 0, 1, 9233, 2413,-0.1875 }, + {1235,1235, 0, 0, 40000, 1020,0 }, + {1236,1236, 0, 0, 40000, 0,0 }, + {1237,1237, 0, 0, 9633, 3073,0 }, + {1238,1238, 0, 0, 40000, 0,0 }, + {1239,1239, 0, 0, 2446, 386,0 }, + {1240,1241, 0, 1, 3113, 1133,0 }, + {1242,1242, 0, 0, 18473, 813,0 }, + {1243,1243, 0, 0, 1206, 660,0 }, + {1244,1244, 0, 0, 40000, 153,0 }, + {1245,1245, 0, 0, 40000, 160,0 }, + {1246,1246, 0, 0, 40000, 133,0 }, + {1247,1247, 0, 0, 8660, 2386,0 }, + {1248,1248, 0, 0, 293, 106,0 }, + {1249,1249, 0, 0, 40000, 433,0 }, + {1250,1250, 0, 0, 426, 80,0 }, + {1251,1251, 0, 0, 973, 360,0 }, + {1252,1252, 0, 0, 573, 153,0 }, + {1253,1253, 0, 0, 3746, 126,0 }, + {1254,1254, 0, 0, 2313, 73,0 }, + {1255,1255, 0, 0, 1473, 106,0 }, + {1256,1256, 0, 0, 1500, 320,0 }, + {1257,1257, 0, 0, 5280, 1593,0 }, + {1258,1258, 0, 0, 40000, 60,0 }, + {1259,1259, 0, 0, 40000, 146,0 }, + {1260,1260, 29, 0, 40000, 300,0 }, + {1261,1261, 65, 0, 40000, 2040,0 }, + {1262,1262, 0, 0, 626, 240,0 }, + {1263,1263, 25, 0, 626, 226,0 }, + {1264,1264, 83, 0, 180, 80,0 }, + {1265,1265, 32, 0, 260, 140,0 }, + {1266,1266, 60, 0, 40000, 0,0 }, + {1267,1267, 36, 0, 286, 40,0 }, + {1268,1268, 27, 0, 573, 80,0 }, + {1269,1269, 31, 0, 693, 106,0 }, + {1270,1270, 21, 0, 500, 146,0 }, + {1270,1270, 26, 0, 493, 140,0 }, + {1270,1270, 28, 0, 500, 146,0 }, + {1271,1271, 60, 0, 2420, 1080,0 }, + {1270,1270, 32, 0, 413, 126,0 }, + {1272,1272, 60, 0, 806, 300,0 }, + {1273,1273, 96, 0, 1146, 493,0 }, + {1274,1274, 72, 0, 1246, 586,0 }, + {1275,1275, 79, 0, 286, 106,0 }, + {1276,1276, 69, 0, 1193, 1046,0 }, + {1277,1277, 71, 0, 340, 93,0 }, + {1278,1278, 22, 0, 1880, 653,0 }, + {1279,1279, 55, 0, 246, 120,0 }, + {1279,1279, 48, 0, 286, 133,0 }, + {1280,1280, 0, 0, 40, 0,0 }, + {1281,1281, 49, 2, 40, 0,0 }, + {1282,1282, 73, 0, 166, 33,0 }, + {1282,1282, 68, 0, 166, 33,0 }, + {1282,1282, 61, 0, 200, 40,0 }, + {1283,1283, 0, 0, 40, 0,0 }, + {1284,1284, 0, 0, 40000, 100,0 }, + {1285,1285, 0, 0, 40000, 60,0 }, + {1286,1286, 0, 0, 40000, 0,0 }, + {1287,1287, 0, 0, 10460, 153,0 }, + {1288,1289, 0, 1, 40000, 0,0 }, + {1290,1290, 0, 0, 40000, 0,0 }, + {1291,1292, 36, 1, 353, 153,0 }, + {1293,1293, 69, 0, 1206, 1060,0 }, + {1294,1294, 0, 0, 40000, 0,0 }, + {1295,1295, 0, 0, 40000, 73,0 }, + {1296,1296, 0, 0, 40000, 0,0 }, + {1297,1297, 22, 0, 1880, 653,0 }, + {1298,1298, 0, 0, 40000, 73,0 }, + {1299,1299, 0, 0, 3913, 420,0 }, + {1300,1300, 0, 0, 9233, 240,0 }, + {1301,1301, 0, 0, 4660, 1573,0 }, + {1302,1302, 0, 0, 1166, 400,0 }, + {1303,1303, 0, 0, 40000, 126,0 }, + {1304,1304, 0, 0, 40000, 93,0 }, + {1305,1305, 0, 0, 40000, 93,0 }, + {1306,1306, 0, 0, 40000, 553,0 }, + {1307,1307, 0, 0, 40000, 660,0 }, + {1308,1308, 0, 0, 40000, 73,0 }, + {1309,1309, 0, 0, 40000, 146,0 }, + {1310,1310, 0, 0, 40000, 146,0 }, + {1311,1311, 0, 0, 4026, 100,0 }, + {1312,1312, 0, 0, 18226, 100,0 }, + {1313,1313, 0, 0, 40000, 0,0 }, + {1314,1314, 0, 0, 40000, 73,0 }, + {1315,1315, 0, 0, 40000, 140,0 }, + {1316,1316, 0, 0, 40000, 393,0 }, + {1317,1317, 0, 0, 40000, 406,0 }, + {1318,1318, 0, 0, 40000, 373,0 }, + {1319,1319, 0, 0, 40000, 0,0 }, + {1320,1320, 0, 0, 40000, 360,0 }, + {1321,1321, 0, 0, 1060, 380,0 }, + {1322,1322, 0, 0, 40000, 66,0 }, + {1323,1323, 0, 0, 40000, 66,0 }, + {1324,1324, 0, 0, 40000, 86,0 }, + {1325,1325, 0, 0, 40000, 73,0 }, + { 260, 260, 0, 0, 40000, 80,0 }, + {1326,1326, 0, 0, 40000, 80,0 }, + {1327,1327, 0, 0, 40000, 73,0 }, + {1328,1328, 0, 0, 40000, 73,0 }, + {1329,1329, 0, 0, 40000, 153,0 }, + {1330,1330, 0, 0, 40000, 153,0 }, + {1331,1331, 0, 0, 40000, 146,0 }, + {1332,1332, 0, 0, 40000, 146,0 }, + {1333,1333, 0, 0, 40000, 73,0 }, + {1334,1334, 0, 0, 40000, 153,0 }, + {1335,1335, 0, 0, 40000, 233,0 }, + {1336,1336, 0, 0, 40000, 400,0 }, + {1337,1337, 0, 0, 40000, 1373,0 }, + {1338,1338, 0, 0, 40000, 193,0 }, + {1339,1339, 0, 0, 40000, 1273,0 }, + {1340,1340, 0, 0, 40000, 186,0 }, + {1341,1341, 0, 0, 40000, 86,0 }, + {1342,1342, 0, 0, 7440, 2473,0 }, + {1343,1343, 0, 0, 40000, 160,0 }, + {1344,1344, 0, 0, 8966, 406,0 }, + {1345,1345, 0, 0, 40000, 1353,0 }, + {1346,1346, 0, 0, 14180, 4406,0 }, + { 378, 378, 84, 0, 1333, 460,0 }, + {1347,1347, 24, 0, 1893, 633,0 }, + {1348,1348, 44, 0, 206, 86,0 }, + {1349,1349, 40, 0, 586, 140,0 }, + {1350,1350, 60, 0, 1026, 320,0 }, + {1351,1351, 0, 0, 6560, 33,0 }, + {1352,1352, 0, 0, 7373, 2453,0 }, + {1353,1353, 0, 0, 4660, 1573,0 }, + {1354,1354, 0, 0, 40000, 346,0 }, + {1355,1355, 0, 0, 7126, 86,0 }, + {1356,1356, 0, 0, 40000, 213,0 }, + {1357,1357, 0, 0, 1180, 340,0 }, + {1358,1358, 0, 0, 3893, 1466,0 }, + {1359,1359, 0, 0, 2053, 1173,0 }, + {1360,1360, 0, 0, 40000, 200,0 }, + {1361,1361, 0, 0, 40000, 353,0 }, + {1362,1362, 0, 0, 40000, 273,0 }, + {1363,1363, 0, 0, 40000, 433,0 }, + {1364,1364, 0, 0, 1940, 426,0 }, + {1365,1365, 0, 0, 40000, 80,0 }, + {1366,1366, 0, 0, 40000, 106,0 }, + {1367,1367, 0, 0, 40000, 60,0 }, + {1368,1368, 0, 0, 40000, 140,0 }, + {1369,1369, 0, 0, 40000, 93,0 }, + {1370,1370, 0, 0, 40000, 73,0 }, + {1371,1371, 0, 0, 40000, 73,0 }, + {1372,1372, 0, 0, 40000, 93,0 }, + {1373,1373, 0, 0, 40000, 73,0 }, + {1374,1374, 0, 0, 40000, 80,0 }, + {1375,1375, 0, 0, 40000, 746,0 }, + {1376,1376, 0, 0, 2360, 813,0 }, + {1377,1377, 0, 0, 340, 146,0 }, + {1378,1378, 35, 0, 713, 273,0 }, + {1379,1379, 49, 0, 173, 93,0 }, + {1377,1377, 48, 0, 286, 126,0 }, + {1380,1380, 58, 0, 173, 100,0 }, + {1377,1377, 60, 0, 286, 133,0 }, + {1381,1381, 47, 0, 973, 360,0 }, + {1382,1382, 60, 0, 146, 86,0 }, + {1381,1381, 49, 0, 966, 333,0 }, + {1383,1383, 72, 0, 506, 206,0 }, + {1381,1381, 51, 0, 953, 340,0 }, + {1384,1384, 84, 0, 1340, 480,0 }, + {1381,1381, 54, 0, 986, 360,0 }, + {1381,1381, 57, 0, 980, 346,0 }, + {1385,1385, 72, 0, 1573, 440,0 }, + {1381,1381, 60, 0, 953, 340,0 }, + {1386,1386, 36, 0, 2673, 900,0 }, + {1387,1387, 93, 0, 233, 106,0 }, + {1388,1388, 72, 0, 966, 353,0 }, + {1389,1389, 84, 0, 1366, 473,0 }, + {1390,1390, 36, 0, 1326, 446,0 }, + {1391,1391, 64, 0, 220, 86,0 }, + {1392,1392, 68, 0, 126, 220,0 }, + {1393,1393, 0, 0, 4513, 640,0 }, + {1394,1394, 0, 0, 40000, 353,0 }, + {1395,1395, 0, 0, 40000, 73,0 }, + {1396,1396, 0, 0, 2040, 380,0 }, + {1397,1397, 0, 0, 40000, 240,0 }, + {1398,1398, 0, 0, 3246, 753,0 }, + {1399,1399, 0, 0, 40000, 66,0 }, + {1400,1400, 0, 0, 40000, 0,0 }, + {1401,1401, 0, 0, 40000, 0,0 }, + {1402,1402, 0, 0, 7720, 1260,0 }, + {1403,1403, 0, 0, 213, 6420,0 }, + {1404,1404, 0, 0, 40000, 66,0 }, + {1405,1405, 0, 0, 40000, 73,0 }, + {1406,1406, 0, 0, 40000, 93,0 }, + {1407,1407, 0, 0, 1606, 640,0 }, + {1408,1408, 0, 0, 15486, 1580,0 }, + {1409,1409, 0, 0, 40000, 353,0 }, + {1410,1410, 0, 0, 40000, 2066,0 }, + {1411,1411, 0, 0, 40000, 0,0 }, + {1412,1412, 0, 0, 15453, 73,0 }, + {1413,1413, 0, 0, 3726, 1240,0 }, + {1414,1414, 0, 0, 40000, 86,0 }, + {1415,1415, 0, 0, 40000, 200,0 }, + {1416,1416, 0, 0, 40000, 53,0 }, + {1417,1417, 0, 0, 40000, 73,0 }, + {1418,1418, 0, 0, 40000, 66,0 }, + {1419,1419, 0, 0, 40000, 26,0 }, + {1420,1420, 0, 0, 40000, 53,0 }, + {1421,1421, 0, 0, 40000, 40,0 }, + {1422,1422, 0, 0, 40000, 126,0 }, + {1423,1423, 0, 0, 40000, 0,0 }, + {1424,1424, 0, 0, 13653, 0,0 }, + {1425,1425, 0, 0, 12533, 1953,0 }, + {1426,1426, 0, 0, 973, 1280,0 }, + {1427,1427, 0, 0, 40000, 426,0 }, + {1428,1428, 0, 0, 40000, 53,0 }, + {1429,1429, 0, 0, 40000, 66,0 }, + {1430,1430, 0, 0, 526, 840,0 }, + {1431,1431, 0, 0, 286, 1293,0 }, + {1432,1432, 0, 0, 14726, 4920,0 }, + {1433,1433, 0, 0, 5233, 633,0 }, + {1434,1434, 0, 0, 13226, 2500,0 }, + { 740, 740, 0, 0, 513, 200,0 }, + {1435,1435, 0, 0, 40000, 5666,0 }, + { 739, 739, 48, 0, 213, 20,0 }, + { 500, 500, 55, 0, 100, 0,0 }, + { 740, 740, 60, 0, 226, 113,0 }, + { 500, 500, 41, 0, 106, 0,0 }, + {1436,1436, 84, 0, 160, 26,0 }, + {1437,1437, 84, 0, 386, 493,0 }, + { 500, 500, 48, 0, 100, 0,0 }, + {1438,1438, 15, 0, 340, 140,0 }, + { 752, 752, 49, 0, 173, 20,0 }, + {1438,1438, 16, 0, 346, 146,0 }, + {1438,1438, 12, 0, 340, 140,0 }, + { 740, 740, 55, 0, 220, 113,0 }, + { 752, 752, 18, 0, 206, 20,0 }, + { 752, 752, 15, 0, 200, 20,0 }, + { 752, 752, 17, 0, 206, 20,0 }, + {1439,1440, 0, 4, 40000, 0,0 }, + {1441,1442, 0, 4, 7360, 200,0 }, + {1443,1444, 0, 4, 11840, 320,0 }, + {1445,1446, 0, 4, 9920, 326,0 }, + {1447,1448, 0, 4, 10213, 0,0 }, + {1449,1450, 0, 4, 7440, 2486,0 }, + { 181,1451, 0, 4, 2360, 733,0 }, + {1452,1453, 0, 4, 9260, 240,0 }, + {1454,1455, 0, 4, 40000, 0,0 }, + {1456,1457, 0, 4, 660, 126,0 }, + {1458,1459, 0, 4, 40000, 66,0 }, + { 190,1460, 0, 4, 40000, 60,0 }, + { 192,1461, 0, 4, 40000, 73,0 }, + {1462,1463, 0, 4, 40000, 353,0 }, + {1464,1465, 0, 4, 40000, 353,0 }, + {1466,1467, 0, 4, 40000, 66,0 }, + {1468,1469, 0, 4, 40000, 46,0 }, + { 35,1470, 0, 4, 40000, 46,0 }, + { 36,1471, 0, 4, 320, 0,0 }, + {1472,1473, 0, 4, 320, 0,0 }, + {1474,1475, 0, 4, 7986, 93,0 }, + { 39,1476, 0, 4, 1053, 226,0 }, + {1477,1476, 0, 4, 1060, 226,0 }, + {1478,1479, 0, 4, 40000, 453,0 }, + { 50,1480, 0, 4, 40000, 400,0 }, + {1481,1482, 0, 4, 40000, 133,0 }, + {1483,1484, 0, 4, 40000, 0,0 }, + {1485,1486, 0, 4, 40000, 226,0 }, + { 55,1487, 0, 4, 40000, 100,0 }, + {1488,1489, 0, 4, 40000, 93,0 }, + {1490,1491, 0, 4, 40000, 73,0 }, + {1492,1493, 0, 4, 40000, 73,0 }, + {1494,1495, 0, 4, 40000, 73,0 }, + {1496,1497, 0, 4, 40000, 80,0 }, + {1496,1498, 0, 4, 40000, 73,0 }, + {1499,1500, 0, 4, 40000, 66,0 }, + {1501,1502, 0, 4, 40000, 146,0 }, + {1503,1504, 0, 4, 40000, 93,0 }, + {1505,1506, 0, 4, 40000, 73,0 }, + { 86,1507, 0, 4, 40000, 80,0 }, + {1508,1509, 0, 4, 40000, 0,0 }, + {1510,1511, 0, 4, 40000, 60,0 }, + {1512,1513, 0, 4, 40000, 0,0 }, + {1514,1515, 0, 4, 40000, 0,0 }, + {1516,1517, 0, 4, 40000, 773,0 }, + {1518,1519, 0, 4, 5346, 2973,0 }, + {1520,1521, 0, 4, 40000, 406,0 }, + {1522,1523, 0, 4, 9080, 360,0 }, + {1524,1525, 0, 4, 40000, 1200,0 }, + {1526,1527, 0, 4, 40000, 800,0 }, + {1528,1529, 0, 4, 40000, 960,0 }, + { 111,1530, 0, 4, 1200, 433,0 }, + {1531,1532, 0, 4, 226, 386,0 }, + { 115,1533, 0, 4, 2433, 0,0 }, + {1534,1535, 0, 4, 1873, 646,0 }, + {1536,1537, 0, 4, 3013, 53,0 }, + {1538,1539, 0, 4, 1560, 720,0 }, + {1540, 339, 0, 6, 6, 0,0 }, + {1541, 339, 0, 6, 6, 0,0 }, + {1542,1543, 0, 4, 993, 93,0 }, + {1544,1545, 0, 4, 293, 86,0 }, + {1546,1547, 0, 4, 40000, 153,0 }, + { 364, 365, 44, 4, 120, 0,0 }, + { 129,1548, 48, 4, 173, 0,0 }, + { 367, 368, 58, 4, 173, 0,0 }, + { 129,1549, 60, 4, 173, 0,0 }, + {1550,1551, 48, 4, 520, 200,0 }, + { 132,1552, 43, 4, 173, 0,0 }, + {1550,1551, 49, 4, 520, 200,0 }, + {1553,1554, 43, 4, 160, 80,0 }, + {1550,1551, 51, 4, 513, 0,0 }, + { 134,1555, 43, 4, 1733, 0,0 }, + {1550,1551, 54, 4, 506, 0,0 }, + {1550,1551, 57, 4, 506, 0,0 }, + { 380, 381, 72, 4, 1580, 0,0 }, + {1550,1551, 60, 4, 520, 0,0 }, + {1556,1557, 70, 4, 826, 306,0 }, + { 374, 375, 60, 4, 973, 0,0 }, + {1558,1559, 36, 4, 1233, 0,0 }, + {1560,1561, 65, 4, 293, 133,0 }, + {1562,1563, 84, 4, 1360, 0,0 }, + {1564,1565, 59, 4, 380, 0,0 }, + {1566,1567, 84, 4, 1593, 566,0 }, + {1568,1569, 35, 4, 1353, 473,0 }, + {1570,1571, 44, 4, 413, 0,0 }, + {1572,1573, 67, 4, 246, 0,0 }, + {1574,1575, 66, 4, 293, 0,0 }, + { 145,1576, 59, 4, 146, 0,0 }, + {1577,1578, 51, 4, 360, 0,0 }, + {1579,1580, 45, 4, 246, 0,0 }, + {1581,1582, 71, 4, 433, 0,0 }, + { 149,1583, 60, 4, 280, 0,0 }, + {1584,1585, 58, 4, 173, 0,0 }, + {1586,1587, 53, 4, 173, 0,0 }, + { 397,1588, 64, 4, 220, 80,0 }, + {1589,1590, 71, 4, 106, 53,0 }, + {1591,1592, 61, 4, 1000, 340,0 }, + {1593,1594, 61, 4, 1000, 340,0 }, + { 391, 392, 48, 4, 160, 46,0 }, + { 391, 393, 48, 4, 380, 60,0 }, + {1595,1596, 69, 4, 120, 0,0 }, + { 159,1597, 68, 4, 120, 0,0 }, + { 159,1597, 63, 4, 140, 0,0 }, + {1598,1599, 74, 4, 893, 273,0 }, + {1600,1601, 60, 4, 1013, 306,0 }, + {1602,1603, 80, 4, 220, 0,0 }, + {1604,1605, 64, 4, 1366, 0,0 }, + {1606,1607, 69, 4, 120, 73,0 }, + { 398, 399, 55, 4, 1540, 193,0 }, + {1608,1609, 75, 4, 1573, 0,0 }, + {1610,1611, 68, 4, 120, 0,0 }, + {1612,1613, 48, 4, 360, 0,0 }, + {1614,1615, 53, 4, 606, 0,0 }, + {1616,1616, 0, 0, 40000, 1586,0 }, + {1617,1617, 0, 0, 40000, 1226,0 }, + {1618,1618, 0, 0, 4546, 766,0 }, + {1619,1619, 0, 0, 40000, 420,0 }, + {1620,1620, 0, 0, 40000, 1573,0 }, + {1621,1621, 0, 0, 3326, 806,0 }, + {1622,1622, 0, 0, 40000, 746,0 }, + {1623,1623, 0, 0, 40000, 900,0 }, + {1624,1624, 0, 0, 12166, 1573,0 }, + {1625,1625, 0, 0, 40000, 80,0 }, + {1626,1626, 0, 0, 40000, 80,0 }, + {1627,1627, 0, 0, 40000, 80,0 }, + {1628,1628, 0, 0, 40000, 2713,0 }, + {1629,1629, 0, 0, 40000, 86,0 }, + {1630,1630, 0, 0, 40000, 80,0 }, + {1631,1631, 0, 0, 40000, 80,0 }, + {1632,1632, 0, 0, 40000, 813,0 }, + {1633,1633, 0, 0, 40000, 80,0 }, + {1634,1634, 0, 0, 40000, 80,0 }, + {1635,1635, 0, 0, 40000, 80,0 }, + {1636,1636, 0, 0, 40000, 193,0 }, + {1637,1637, 0, 0, 2920, 733,0 }, + {1638,1638, 0, 0, 40000, 373,0 }, + {1639,1639, 0, 0, 2286, 226,0 }, + {1640,1640, 0, 0, 40000, 226,0 }, + {1641,1641, 0, 0, 40000, 226,0 }, + {1642,1642, 0, 0, 40000, 433,0 }, + {1643,1643, 0, 0, 40000, 813,0 }, + {1644,1644, 0, 0, 40000, 80,0 }, + {1645,1645, 0, 0, 40000, 80,0 }, + {1646,1646, 0, 0, 40000, 80,0 }, + {1647,1647, 0, 0, 40000, 80,0 }, + {1648,1648, 0, 0, 40000, 80,0 }, + {1649,1649, 0, 0, 40000, 80,0 }, + {1650,1650, 0, 0, 40000, 146,0 }, + {1651,1651, 0, 0, 40000, 1280,0 }, + {1652,1652, 0, 0, 40000, 513,0 }, + {1653,1653, 0, 0, 40000, 313,0 }, + {1654,1654, 0, 0, 40000, 773,0 }, + {1655,1655, 0, 0, 7400, 2480,0 }, + {1656,1656, 0, 0, 3760, 1253,0 }, + {1657,1657, 0, 0, 40000, 380,0 }, + {1658,1658, 0, 0, 40000, 333,0 }, + {1659,1659, 0, 0, 40000, 2926,0 }, + {1660,1660, 0, 0, 40000, 5666,0 }, + {1661,1661, 0, 0, 40000, 1613,0 }, + {1662,1662, 0, 0, 3746, 1273,0 }, + {1663,1663, 0, 0, 13653, 0,0 }, + {1664,1664, 0, 0, 4640, 1553,0 }, + {1665,1665, 0, 0, 40000, 680,0 }, + {1666,1666, 0, 0, 6393, 426,0 }, + {1667,1667, 0, 0, 40000, 713,0 }, + {1668,1668, 12, 0, 166, 20,0 }, + {1669,1669, 48, 0, 460, 193,0 }, + { 736, 736, 52, 0, 286, 20,0 }, + {1670,1670, 48, 0, 506, 200,0 }, + {1670,1670, 36, 0, 713, 260,0 }, + { 377, 377, 84, 0, 386, 493,0 }, + { 730, 730, 95, 0, 1886, 653,0 }, + {1669,1669, 84, 0, 386, 166,0 }, + { 755, 755, 20, 0, 633, 240,0 }, + { 755, 755, 22, 0, 626, 240,0 }, + { 755, 755, 24, 0, 633, 246,0 }, + {1671,1671, 0, 0, 2233, 220,0 }, + {1672,1672, 0, 0, 2233, 240,0 }, + {1673,1673, 0, 0, 2233, 206,0 }, + {1674,1674, 0, 0, 2126, 173,0 }, + {1675,1675, 0, 0, 7473, 73,0 }, + {1676,1676, 0, 0, 40000, 0,0 }, + {1677,1677, 0, 0, 3493, 193,0 }, + {1678,1678, 0, 0, 1746, 73,0 }, + {1679,1679, 0, 0, 1013, 400,0 }, + {1680,1680, 0, 0, 3473, 1560,0 }, + {1681,1681, 0, 0, 1073, 40,0 }, + {1682,1682, 0, 0, 40000, 380,0 }, + {1683,1683, 0, 0, 1166, 400,0 }, + {1684,1684, 0, 0, 606, 146,0 }, + {1685,1685, 0, 0, 4553, 1486,0 }, + {1686,1686, 0, 0, 1126, 80,0 }, + {1687,1687, 0, 0, 40000, 73,0 }, + {1688,1688, 0, 0, 40000, 60,0 }, + {1689,1689, 0, 0, 40000, 66,0 }, + {1690,1690, 0, 0, 40000, 73,0 }, + {1691,1691, 0, 0, 40000, 73,0 }, + {1692,1692, 0, 0, 40000, 73,0 }, + {1693,1693, 0, 0, 40000, 73,0 }, + {1694,1694, 0, 0, 6380, 53,0 }, + {1695,1695, 0, 0, 6380, 60,0 }, + {1696,1696, 0, 0, 40000, 53,0 }, + {1697,1697, 0, 0, 40000, 0,0 }, + {1698,1698, 0, 0, 1880, 80,0 }, + {1699,1699, 0, 0, 40000, 60,0 }, + {1700,1700, 0, 0, 40000, 60,0 }, + {1701,1701, 0, 0, 1460, 80,0 }, + {1702,1702, 0, 0, 40000, 73,0 }, + {1703,1703, 0, 0, 40000, 0,0 }, + {1704,1704, 0, 0, 40000, 146,0 }, + {1705,1705, 0, 0, 40000, 66,0 }, + {1706,1706, 0, 0, 40000, 73,0 }, + {1707,1707, 0, 0, 40000, 160,0 }, + {1708,1708, 0, 0, 40000, 73,0 }, + {1709,1709, 0, 0, 40000, 193,0 }, + {1710,1710, 0, 0, 3740, 1260,0 }, + {1711,1711, 0, 0, 40000, 180,0 }, + {1712,1712, 0, 0, 40000, 173,0 }, + {1713,1713, 0, 0, 40000, 113,0 }, + {1714,1714, 0, 0, 40000, 86,0 }, + {1715,1715, 0, 0, 1853, 633,0 }, + {1716,1716, 0, 0, 40000, 0,0 }, + {1717,1717, 0, 0, 1066, 306,0 }, + {1718,1718, 0, 0, 40000, 86,0 }, + {1719,1719, 0, 0, 40000, 586,0 }, + {1720,1720, 0, 0, 40000, 86,0 }, + {1721,1721, 0, 0, 40000, 93,0 }, + {1722,1722, 0, 0, 40000, 373,0 }, + {1723,1723, 0, 0, 40000, 113,0 }, + {1724,1724, 0, 0, 40000, 353,0 }, + {1725,1725, 0, 0, 420, 73,0 }, + {1726,1726, 0, 0, 40000, 66,0 }, + {1727,1727, 0, 0, 40000, 53,0 }, + {1728,1728, 0, 0, 40000, 66,0 }, + {1729,1729, 0, 0, 40000, 100,0 }, + {1730,1730, 0, 0, 40000, 93,0 }, + {1731,1731, 0, 0, 40000, 0,0 }, + {1732,1732, 0, 0, 40000, 73,0 }, + {1733,1733, 0, 0, 40000, 80,0 }, + {1734,1734, 0, 0, 40000, 80,0 }, + {1735,1735, 0, 0, 40000, 80,0 }, + {1736,1736, 0, 0, 40000, 80,0 }, + {1737,1737, 0, 0, 40000, 80,0 }, + {1738,1738, 0, 0, 40000, 73,0 }, + {1739,1739, 0, 0, 40000, 73,0 }, + {1740,1740, 0, 0, 40000, 106,0 }, + {1741,1741, 0, 0, 40000, 73,0 }, + {1742,1742, 0, 0, 40000, 73,0 }, + {1743,1743, 0, 0, 40000, 80,0 }, + {1744,1744, 0, 0, 40000, 0,0 }, + {1745,1745, 0, 0, 40000, 80,0 }, + {1746,1746, 0, 0, 40000, 66,0 }, + {1747,1747, 0, 0, 40000, 73,0 }, + {1748,1748, 0, 0, 40000, 0,0 }, + {1749,1749, 0, 0, 40000, 80,0 }, + {1750,1750, 0, 0, 40000, 66,0 }, + {1751,1751, 0, 0, 40000, 73,0 }, + {1752,1752, 0, 0, 40000, 80,0 }, + {1753,1753, 0, 0, 40000, 33,0 }, + {1754,1754, 0, 0, 40000, 0,0 }, + {1755,1755, 0, 0, 40000, 266,0 }, + {1756,1756, 0, 0, 40000, 160,0 }, + {1757,1757, 0, 0, 40000, 93,0 }, + {1758,1758, 0, 0, 40000, 660,0 }, + {1759,1759, 0, 0, 40000, 1453,0 }, + {1760,1760, 0, 0, 40000, 660,0 }, + {1761,1761, 0, 0, 40000, 120,0 }, + {1762,1762, 0, 0, 40000, 140,0 }, + {1763,1763, 0, 0, 9820, 393,0 }, + {1764,1764, 0, 0, 40000, 73,0 }, + {1765,1765, 0, 0, 3620, 1166,0 }, + {1766,1766, 0, 0, 40000, 0,0 }, + {1767,1767, 0, 0, 40000, 0,0 }, + {1768,1768, 0, 0, 40000, 813,0 }, + {1769,1769, 0, 0, 40000, 0,0 }, + {1770,1770, 0, 0, 40000, 2386,0 }, + {1771,1771, 0, 0, 4380, 400,0 }, + {1772,1772, 0, 0, 853, 0,0 }, + {1773,1773, 0, 0, 3700, 93,0 }, + {1774,1774, 0, 0, 1580, 300,0 }, + {1775,1775, 0, 0, 453, 140,0 }, + {1776,1776, 0, 0, 40000, 66,0 }, + {1777,1777, 0, 0, 40000, 73,0 }, + {1778,1778, 0, 0, 40000, 206,0 }, + {1779,1779, 0, 0, 4646, 1560,0 }, + {1780,1780, 0, 0, 353, 146,0 }, + {1781,1781, 0, 0, 1300, 400,0 }, + {1782,1782, 0, 0, 4593, 1546,0 }, + {1783,1783, 0, 0, 613, 226,0 }, + {1784,1784, 0, 0, 626, 233,0 }, + {1785,1785, 0, 0, 3020, 66,0 }, + {1786,1786, 0, 0, 1093, 186,0 }, + {1787,1787, 0, 0, 6053, 1240,0 }, + {1788,1788, 0, 0, 633, 126,0 }, + {1789,1789, 0, 0, 40000, 66,0 }, + {1790,1790, 0, 0, 40000, 73,0 }, + {1791,1791, 0, 0, 40000, 1253,0 }, + {1792,1792, 0, 0, 626, 246,0 }, + {1793,1793, 48, 0, 293, 120,0 }, + {1794,1794, 48, 0, 100, 0,0 }, + {1795,1795, 60, 0, 240, 133,0 }, + {1796,1796, 60, 0, 160, 66,0 }, + {1797,1797, 70, 0, 140, 33,0 }, + {1798,1798, 51, 0, 526, 206,0 }, + {1799,1799, 60, 0, 173, 93,0 }, + {1798,1798, 54, 0, 520, 200,0 }, + {1800,1800, 60, 0, 153, 80,0 }, + {1798,1798, 56, 0, 520, 206,0 }, + {1801,1801, 60, 0, 673, 206,0 }, + {1798,1798, 61, 0, 506, 200,0 }, + {1798,1798, 63, 0, 513, 206,0 }, + {1802,1802, 48, 0, 673, 200,0 }, + {1798,1798, 68, 0, 440, 180,0 }, + {1803,1803, 60, 0, 1873, 653,0 }, + {1804,1804, 60, 0, 673, 200,0 }, + {1805,1805, 66, 0, 306, 120,0 }, + {1806,1806, 60, 0, 673, 200,0 }, + { 379, 379, 59, 0, 173, 93,0 }, + {1802,1802, 64, 0, 673, 206,0 }, + {1807,1807, 48, 0, 1006, 20,0 }, + {1808,1808, 56, 0, 120, 40,0 }, + {1809,1809, 53, 0, 286, 133,0 }, + {1810,1810, 65, 0, 106, 0,0 }, + {1811,1811, 49, 0, 293, 133,0 }, + {1811,1811, 43, 0, 293, 133,0 }, + { 386, 386, 65, 0, 1013, 673,0 }, + { 386, 386, 60, 0, 1000, 660,0 }, + {1812,1812, 70, 0, 260, 113,0 }, + {1812,1812, 65, 0, 306, 120,0 }, + {1813,1813, 60, 0, 246, 106,0 }, + {1814,1814, 60, 0, 193, 120,0 }, + {1815,1815, 56, 0, 206, 13,0 }, + {1816,1816, 53, 0, 433, 73,0 }, + {1817,1817, 60, 0, 220, 113,0 }, + {1818,1818, 48, 0, 300, 66,0 }, + {1819,1819, 69, 0, 126, 0,0 }, + { 328, 328, 67, 0, 140, 93,0 }, + { 328, 328, 62, 0, 153, 100,0 }, + {1820,1820, 65, 0, 433, 100,0 }, + {1821,1821, 60, 0, 426, 100,0 }, + {1822,1822, 63, 0, 113, 46,0 }, + {1823,1823, 63, 0, 1866, 653,0 }, + {1824,1824, 67, 0, 273, 60,0 }, + {1825,1825, 60, 0, 973, 360,0 }, + {1825,1825, 72, 0, 806, 273,0 }, + { 401, 401, 62, 0, 46, 0,0 }, + {1826,1826, 48, 0, 126, 66,0 }, + {1827,1827, 53, 0, 980, 353,0 }, + {1828,1828, 60, 0, 293, 133,0 }, + {1829,1829, 60, 0, 160, 20,0 }, + {1830,1830, 60, 0, 126, 86,0 }, + {1831,1831, 60, 0, 173, 93,0 }, + {1832,1832, 0, 0, 40000, 106,0 }, + {1833,1833, 0, 0, 3780, 73,0 }, + {1834,1834, 0, 0, 3820, 1666,0 }, + {1835,1835, 0, 0, 40000, 73,0 }, + {1836,1836, 0, 0, 40000, 333,0 }, + {1837,1837, 0, 0, 40000, 220,0 }, + {1838,1838, 0, 0, 40000, 0,0 }, + {1839,1839, 0, 0, 40000, 53,0 }, + {1840,1840, 0, 0, 40000, 60,0 }, + {1841,1841, 0, 0, 5913, 2306,0 }, + {1842,1842, 0, 0, 7713, 2466,0 }, + { 525, 525, 0, 0, 4660, 660,0 }, + {1843,1843, 0, 0, 40000, 313,0 }, + {1844,1844, 0, 0, 40000, 0,0 }, + {1845,1845, 0, 0, 40000, 0,0 }, + {1846,1846, 0, 0, 1246, 453,0 }, + {1847,1847, 0, 0, 9600, 1580,0 }, + {1848,1848, 0, 0, 40000, 106,0 }, + {1849,1849, 0, 0, 2040, 400,0 }, + {1850,1850, 0, 0, 40000, 73,0 }, + {1851,1851, 0, 0, 4220, 620,0 }, + {1852,1852, 0, 0, 40000, 0,0 }, + {1853,1853, 0, 0, 40000, 433,0 }, + {1854,1854, 0, 0, 40000, 66,0 }, + {1855,1855, 0, 0, 40000, 46,0 }, + {1856,1856, 0, 0, 40000, 240,0 }, + {1857,1857, 0, 0, 40000, 313,0 }, + {1858,1858, 0, 0, 40000, 26,0 }, + {1859,1859, 0, 0, 40000, 0,0 }, + {1860,1860, 0, 0, 40000, 73,0 }, + {1861,1861, 0, 0, 6940, 66,0 }, + {1862,1862, 0, 0, 40000, 0,0 }, + {1863,1863, 0, 0, 40000, 60,0 }, + {1864,1864, 0, 0, 8140, 1440,0 }, + {1865,1865, 0, 0, 40000, 0,0 }, + {1866,1866, 0, 0, 40000, 613,0 }, + {1867,1867, 0, 0, 40000, 0,0 }, + {1868,1868, 0, 0, 633, 233,0 }, + {1869,1869, 0, 0, 40000, 226,0 }, + {1870,1870, 0, 0, 2280, 746,0 }, + {1871,1871, 0, 0, 1940, 633,0 }, + {1872,1872, 0, 0, 4220, 620,0 }, + {1873,1873, 0, 0, 40000, 133,0 }, + {1874,1874, 41, 0, 380, 153,0 }, + {1875,1875, 70, 0, 106, 0,0 }, + {1876,1876, 60, 0, 380, 206,0 }, + {1877,1877, 80, 0, 100, 0,0 }, + {1878,1878, 84, 0, 120, 0,0 }, + {1879,1879, 72, 0, 500, 433,0 }, + {1880,1880, 84, 0, 860, 553,0 }, + { 128, 128, 70, 0, 106, 0,0 }, + { 132, 132, 60, 0, 146, 86,0 }, + {1881,1882, 0, 4, 40000, 260,0 }, + {1883,1883, 0, 0, 40000, 0,0 }, + {1884,1885, 0, 4, 40000, 73,0 }, + {1886,1887, 0, 4, 40000, 86,0 }, + {1888,1889, 0, 4, 40000, 73,0 }, + {1890,1890, 0, 0, 40000, 300,0 }, + {1891,1891, 0, 0, 40000, 693,0 }, + {1892,1892, 0, 0, 40000, 586,0 }, + {1893,1893, 0, 0, 40000, 286,0 }, + {1894,1894, 0, 0, 1620, 773,0 }, + {1895,1895, 0, 0, 40000, 0,0 }, + {1896,1896, 0, 0, 40000, 193,0 }, + {1897,1897, 0, 0, 1873, 820,0 }, + {1898,1898, 0, 0, 4520, 753,0 }, + {1899,1899, 0, 0, 40000, 0,0 }, + {1900,1900, 0, 0, 40000, 220,0 }, + {1901,1901, 0, 0, 40000, 133,0 }, + {1902,1902, 0, 0, 40000, 73,0 }, + {1903,1903, 0, 0, 40000, 0,0 }, + {1904,1904, 0, 0, 7326, 2420,0 }, + {1905,1905, 0, 0, 1186, 446,0 }, + {1906,1906, 0, 0, 40000, 553,0 }, + {1907,1907, 0, 0, 40000, 293,0 }, + {1908,1908, 0, 0, 40000, 586,0 }, + {1909,1909, 0, 0, 2326, 793,0 }, + { 501, 501, 0, 0, 480, 226,0 }, + {1910,1910, 0, 0, 40000, 93,0 }, + {1911,1911, 0, 0, 620, 226,0 }, + {1912,1912, 0, 0, 2373, 800,0 }, + {1913,1913, 0, 0, 40000, 4986,0 }, + {1914,1914, 0, 0, 626, 240,0 }, + { 511, 511, 0, 0, 2326, 800,0 }, + {1915,1915, 0, 0, 340, 146,0 }, + {1910,1910, 60, 0, 40000, 93,0 }, + { 511, 511, 72, 0, 1566, 546,0 }, + {1915,1915, 84, 0, 246, 120,0 }, + {1916,1916, 0, 0, 40000, 0,0 }, + {1917,1917, 0, 0, 2713, 666,0 }, + {1918,1918, 0, 0, 40000, 0,0 }, + {1919,1919, 0, 0, 40000, 46,0 }, + {1920,1920, 0, 0, 40000, 0,0 }, + {1921,1921, 0, 0, 40000, 53,0 }, + {1922,1922, 0, 0, 40000, 33,0 }, + {1923,1923, 0, 0, 2073, 193,0 }, + {1924,1924, 0, 0, 40000, 146,0 }, + {1925,1925, 0, 0, 40000, 100,0 }, + {1926,1926, 0, 0, 40000, 93,0 }, + {1927,1927, 0, 0, 40000, 73,0 }, + {1928,1928, 0, 0, 40000, 540,0 }, + {1929,1929, 0, 0, 40000, 520,0 }, + {1930,1930, 0, 0, 40000, 506,0 }, + {1931,1931, 0, 0, 7406, 200,0 }, + {1932,1932, 0, 0, 5906, 133,0 }, + {1933,1933, 0, 0, 7426, 240,0 }, + {1934,1934, 0, 0, 7426, 240,0 }, + {1935,1935, 0, 0, 40000, 66,0 }, + {1936,1936, 0, 0, 40000, 66,0 }, + {1937,1937, 0, 0, 40000, 53,0 }, + {1938,1938, 0, 0, 40000, 66,0 }, + {1939,1939, 0, 0, 40000, 66,0 }, + {1940,1940, 0, 0, 40000, 53,0 }, + {1941,1941, 0, 0, 40000, 2146,0 }, + {1942,1942, 0, 0, 40000, 1126,0 }, + {1943,1943, 0, 0, 40000, 1020,0 }, + {1944,1944, 0, 0, 40000, 433,0 }, + {1945,1945, 0, 0, 40000, 0,0 }, + {1946,1946, 0, 0, 40000, 140,0 }, + {1947,1947, 0, 0, 4660, 660,0 }, + {1948,1948, 0, 0, 40000, 66,0 }, + {1949,1949, 0, 0, 40000, 4193,0 }, + {1950,1950, 0, 0, 7713, 2466,0 }, + {1951,1951, 0, 0, 40000, 73,0 }, + {1952,1952, 0, 0, 8100, 2093,0 }, + {1953,1953, 0, 0, 40000, 86,0 }, + {1954,1954, 0, 0, 40000, 80,0 }, + {1955,1955, 0, 0, 4113, 1526,0 }, + {1956,1956, 0, 0, 40000, 66,0 }, + {1957,1957, 0, 0, 40000, 100,0 }, + {1958,1958, 0, 0, 40000, 213,0 }, + {1959,1959, 0, 0, 40000, 100,0 }, + {1960,1960, 0, 0, 1186, 100,0 }, + {1961,1961, 0, 0, 40000, 433,0 }, + {1962,1962, 0, 0, 40000, 146,0 }, + {1963,1963, 0, 0, 40000, 400,0 }, + {1964,1964, 0, 0, 40000, 66,0 }, + {1965,1965, 0, 0, 40000, 193,0 }, + {1966,1966, 0, 0, 1153, 100,0 }, + {1967,1967, 0, 0, 4800, 1400,0 }, + {1968,1968, 0, 0, 2906, 713,0 }, + {1969,1969, 0, 0, 40000, 73,0 }, + {1970,1970, 0, 0, 2280, 746,0 }, + {1971,1971, 0, 0, 40000, 66,0 }, + {1972,1972, 0, 0, 40000, 86,0 }, + {1973,1973, 0, 0, 40000, 86,0 }, + {1974,1974, 0, 0, 40000, 66,0 }, + {1975,1975, 0, 0, 40000, 66,0 }, + {1976,1976, 0, 0, 40000, 66,0 }, + {1977,1977, 0, 0, 40000, 46,0 }, + {1978,1978, 0, 0, 40000, 73,0 }, + {1979,1979, 0, 0, 40000, 73,0 }, + {1980,1980, 0, 0, 40000, 66,0 }, + {1981,1981, 0, 0, 40000, 66,0 }, + {1982,1982, 0, 0, 40000, 66,0 }, + {1983,1983, 0, 0, 40000, 73,0 }, + {1984,1984, 0, 0, 40000, 73,0 }, + {1985,1985, 0, 0, 40000, 253,0 }, + {1986,1986, 0, 0, 40000, 126,0 }, + {1987,1987, 0, 0, 40000, 126,0 }, + {1988,1988, 0, 0, 40000, 66,0 }, + {1989,1989, 0, 0, 40000, 66,0 }, + {1990,1990, 0, 0, 40000, 53,0 }, + {1991,1991, 0, 0, 40000, 140,0 }, + {1992,1992, 0, 0, 40000, 40,0 }, + {1993,1993, 0, 0, 40000, 73,0 }, + {1994,1994, 0, 0, 40000, 66,0 }, + {1995,1995, 0, 0, 40000, 73,0 }, + {1996,1996, 0, 0, 40000, 73,0 }, + {1997,1997, 0, 0, 40000, 73,0 }, + {1998,1998, 0, 0, 40000, 73,0 }, + {1999,1999, 0, 0, 40000, 66,0 }, + {2000,2000, 0, 0, 40000, 433,0 }, + {2001,2001, 0, 0, 40000, 433,0 }, + {2002,2002, 0, 0, 2440, 706,0 }, + {2003,2003, 0, 0, 13960, 4800,0 }, + {2004,2004, 0, 0, 7393, 2480,0 }, + {2005,2005, 0, 0, 7220, 2073,0 }, + {2006,2006, 0, 0, 633, 233,0 }, + {2007,2007, 0, 0, 2326, 780,0 }, + {2008,2008, 0, 0, 40000, 73,0 }, + {2009,2009, 0, 0, 40000, 106,0 }, + {2010,2010, 0, 0, 40000, 126,0 }, + {2011,2011, 0, 0, 40000, 386,0 }, + {2012,2012, 0, 0, 40000, 66,0 }, + {2013,2013, 0, 0, 6893, 1273,0 }, + {2014,2014, 0, 0, 2546, 633,0 }, + {2015,2015, 0, 0, 206, 106,0 }, + {2016,2016, 0, 0, 213, 113,0 }, + {2017,2017, 0, 0, 360, 140,0 }, + {2018,2018, 0, 0, 1013, 193,0 }, + {2019,2019, 0, 0, 266, 66,0 }, + {2020,2020, 0, 0, 1880, 660,0 }, + {2021,2021, 0, 0, 286, 206,0 }, + {2022,2022, 0, 0, 3706, 1353,0 }, + {2023,2023, 0, 0, 1106, 380,0 }, + {2024,2024, 0, 0, 13220, 2466,0 }, + {2025,2025, 0, 0, 333, 26,0 }, + {2026,2026, 0, 0, 7346, 2440,0 }, + {2027,2027, 0, 0, 1273, 453,0 }, + { 352, 352, 51, 2, 6, 0,0 }, + {2028,2028, 35, 0, 700, 253,0 }, + {2028,2028, 36, 0, 706, 266,0 }, + {2029,2029, 47, 0, 100, 0,0 }, + {2030,2030, 38, 0, 346, 140,0 }, + {2019,2019, 39, 0, 220, 106,0 }, + {2031,2031, 45, 0, 286, 133,0 }, + { 492, 492, 41, 0, 1040, 406,0 }, + {2032,2032, 42, 0, 220, 106,0 }, + {2033,2033, 44, 0, 500, 193,0 }, + { 492, 492, 48, 0, 833, 346,0 }, + {2034,2034, 46, 0, 1866, 646,0 }, + { 492, 492, 53, 0, 873, 386,0 }, + { 167, 167, 56, 0, 646, 353,0 }, + {2035,2035, 61, 0, 366, 146,0 }, + {2036,2036, 56, 0, 1346, 473,0 }, + {2037,2037, 60, 0, 213, 126,0 }, + { 144, 144, 59, 0, 213, 0,0 }, + {2038,2038, 59, 0, 106, 0,0 }, + { 169, 169, 51, 0, 380, 366,0 }, + { 169, 169, 45, 0, 380, 366,0 }, + {2039,2039, 72, 0, 246, 20,0 }, + {2040,2040, 60, 0, 280, 20,0 }, + {2041,2041, 58, 0, 373, 360,0 }, + {2042,2042, 53, 0, 380, 366,0 }, + {2043,2043, 73, 0, 120, 26,0 }, + { 158, 158, 75, 0, 126, 140,0 }, + {2044,2044, 0, 0, 6786, 1073,0 }, + {2045,2045, 0, 0, 2046, 473,0 }, + {2046,2046, 0, 0, 3746, 1273,0 }, + {2047,2047, 0, 0, 1200, 3086,0 }, + {2048,2048, 0, 0, 1200, 3080,0 }, + {2049,2049, 0, 0, 40000, 2453,0 }, + {2050,2050, 0, 0, 40000, 413,0 }, + {2051,2051, 0, 0, 980, 2553,0 }, + {2052,2052, 0, 0, 40000, 2420,0 }, + {2053,2053, 0, 0, 40000, 2506,0 }, + {2054,2054, 0, 0, 40000, 380,0 }, + {2055,2055, 0, 0, 40000, 660,0 }, + {2056,2056, 0, 0, 40000, 73,0 }, + {2057,2057, 0, 0, 40000, 333,0 }, + {2058,2058, 0, 0, 833, 146,0 }, + {2059,2059, 0, 0, 1686, 620,0 }, + {2060,2060, 0, 0, 40000, 73,0 }, + {2061,2061, 0, 0, 40000, 0,0 }, + {2062,2062, 0, 0, 1873, 633,0 }, + {2063,2063, 0, 0, 40000, 380,0 }, + {2064,2064, 0, 0, 366, 286,0 }, + {2065,2065, 0, 0, 8866, 1366,0 }, + {2066,2066, 0, 0, 40000, 1513,0 }, + {2067,2067, 0, 0, 40000, 333,0 }, + {2068,2068, 0, 0, 9600, 1573,0 }, + {2069,2069, 0, 0, 3293, 746,0 }, + {2070,2070, 0, 0, 40000, 53,0 }, + {2071,2071, 0, 0, 40000, 73,0 }, + {2072,2072, 0, 0, 40000, 73,0 }, + {2073,2073, 0, 0, 40000, 240,0 }, + {2074,2074, 0, 0, 40000, 240,0 }, + {2075,2075, 0, 0, 40000, 140,0 }, + {2076,2076, 0, 0, 40000, 113,0 }, + {2077,2077, 0, 0, 40000, 240,0 }, + {2078,2078, 0, 0, 3613, 1146,0 }, + {2079,2079, 0, 0, 40000, 126,0 }, + {2080,2080, 0, 0, 40000, 0,0 }, + {2081,2081, 0, 0, 40000, 633,0 }, + {2082,2082, 0, 0, 40000, 453,0 }, + {2083,2083, 0, 0, 40000, 1146,0 }, + {2084,2084, 0, 0, 40000, 3600,0 }, + {2085,2085, 0, 0, 40000, 1586,0 }, + {2086,2086, 0, 0, 40000, 1586,0 }, + {2087,2087, 0, 0, 40000, 1586,0 }, + {2088,2088, 0, 0, 40000, 1646,0 }, + {2089,2089, 0, 0, 40000, 1580,0 }, + {2090,2090, 0, 0, 40000, 4393,0 }, + {2091,2091, 0, 0, 40000, 4540,0 }, + {2092,2092, 0, 0, 21373, 6160,0 }, + {2093,2093, 0, 0, 40000, 633,0 }, + {2094,2094, 0, 0, 18420, 6146,0 }, + {2095,2095, 0, 0, 2306, 813,0 }, + {2096,2096, 0, 0, 2813, 333,0 }, + {2097,2097, 0, 0, 3106, 600,0 }, + {2098,2098, 0, 0, 1026, 1580,0 }, + {2099,2099, 0, 0, 1873, 346,0 }, + {2100,2100, 0, 0, 40000, 73,0 }, + {2101,2101, 0, 0, 40000, 73,0 }, + {2102,2102, 0, 0, 1200, 1906,0 }, + {2103,2103, 0, 0, 980, 1313,0 }, + {2104,2104, 0, 0, 200, 20,0 }, + {2105,2105, 0, 0, 640, 253,0 }, + {2106,2106, 0, 0, 3120, 240,0 }, + {2107,2107, 0, 0, 753, 146,0 }, + {2108,2108, 0, 0, 40000, 3060,0 }, + {2109,2109, 0, 0, 40000, 233,0 }, + {2110,2110, 0, 0, 40000, 246,0 }, + {2111,2111, 0, 0, 40000, 240,0 }, + { 752, 752, 60, 0, 173, 20,0 }, + { 755, 755, 12, 0, 626, 240,0 }, + {2112,2112, 89, 0, 113, 0,0 }, + {2113,2113, 89, 0, 700, 266,0 }, + { 755, 755, 14, 0, 626, 240,0 }, + { 755, 755, 16, 0, 626, 246,0 }, + {2114,2114, 84, 0, 1593, 553,0 }, + { 755, 755, 19, 0, 626, 240,0 }, + {2115,2115, 38, 0, 220, 166,0 }, + {2116,2116, 36, 0, 1686, 760,0 }, + { 755, 755, 28, 0, 626, 240,0 }, + { 755, 755, 26, 0, 626, 240,0 }, + { 755, 755, 35, 0, 633, 246,0 }, + { 755, 755, 30, 0, 626, 240,0 }, + {2117,2117, 60, 0, 180, 53,0 }, + {2104,2104, 60, 0, 173, 20,0 }, + {2104,2104, 55, 0, 173, 20,0 }, + { 730, 730, 94, 0, 1886, 660,0 }, + {2118,2118, 0, 0, 1226, 73,0 }, + {2119,2119, 0, 0, 40000, 0,0 }, + {2120,2120, 0, 0, 40000, 146,0 }, + {2121,2121, 0, 0, 40000, 80,0 }, + {2122,2122, 0, 0, 40000, 80,0 }, + {2123,2123, 0, 0, 40000, 0,0 }, + {2124,2124, 0, 0, 40000, 126,0 }, + {2125,2125, 0, 0, 40000, 213,0 }, + {2126,2126, 0, 0, 40000, 80,0 }, + {2127,2127, 0, 0, 40000, 73,0 }, + {2128,2128, 0, 0, 40000, 73,0 }, + {2129,2129, 0, 0, 40000, 73,0 }, + {2130,2130, 0, 0, 40000, 80,0 }, + {2131,2131, 0, 0, 40000, 73,0 }, + {2132,2132, 0, 0, 40000, 73,0 }, + {2133,2133, 0, 0, 40000, 66,0 }, + {2134,2134, 0, 0, 40000, 186,0 }, + {2135,2135, 0, 0, 9966, 426,0 }, + {2136,2136, 0, 0, 40000, 400,0 }, + {2137,2137, 0, 0, 40000, 326,0 }, + {2138,2138, 0, 0, 386, 80,0 }, + {2139,2139, 0, 0, 40000, 246,0 }, + {2140,2140, 0, 0, 3473, 73,0 }, + {2141,2141, 60, 0, 160, 66,0 }, + {2141,2141, 44, 0, 160, 60,0 }, + {2142,2142, 47, 0, 173, 93,0 }, + {2143,2143, 47, 0, 186, 80,0 }, + {2144,2144, 62, 0, 1933, 93,0 }, + {2145,2145, 93, 0, 1146, 473,0 }, + {2146,2146, 50, 0, 286, 93,0 }, + {2145,2145, 40, 0, 2013, 840,0 }, + {2147,2147, 60, 0, 106, 73,0 }, + { 898, 898, 60, 0, 173, 133,0 }, + {2147,2147, 57, 0, 106, 73,0 }, + { 900, 900, 42, 0, 620, 240,0 }, + { 900, 900, 38, 0, 626, 240,0 }, + { 908, 908, 88, 0, 160, 26,0 }, + {2148,2148, 0, 0, 9440, 140,0 }, + {2149,2149, 0, 0, 40000, 73,0 }, + {2150,2150, 0, 0, 4613, 420,0 }, + {2151,2151, 0, 0, 40000, 86,0 }, + {2152,2152, 0, 0, 40000, 406,0 }, + {2153,2153, 0, 0, 40000, 440,0 }, + {2154,2154, 0, 0, 4340, 133,0 }, + {2155,2155, 0, 0, 4460, 706,0 }, + {2156,2156, 0, 0, 40000, 73,0 }, + {2157,2157, 0, 0, 4660, 1573,0 }, + {2158,2158, 0, 0, 966, 333,0 }, + {2159,2159, 0, 0, 1933, 640,0 }, + { 136, 136, 0, 0, 2326, 786,0 }, + { 168, 168, 0, 0, 286, 366,0 }, + { 164, 164, 0, 0, 7373, 2460,0 }, + { 167, 167, 0, 0, 793, 426,0 }, + {2160,2160, 65, 0, 166, 73,0 }, + {2161,2161, 21, 0, 480, 146,0 }, + {2162, 173, 0, 4, 4220, 80,0 }, + {2163,2164, 0, 4, 4640, 3066,0 }, + {2165,2166, 0, 4, 7273, 3920,0 }, + {2167,2168, 0, 4, 3766, 1253,0 }, + {2169,2170, 0, 4, 6266, 2400,0 }, + {2171,2172, 0, 4, 18213, 0,0 }, + {2173,2174, 0, 4, 40000, 713,0 }, + {2175,2174, 0, 4, 40000, 733,0 }, + {2176, 299, 0, 4, 40000, 273,0 }, + {2177,2178, 0, 4, 40000, 66,0 }, + {2179,2180, 0, 4, 40000, 393,0 }, + {2181,2182, 0, 4, 40000, 413,0 }, + {2183,2184, 0, 4, 7406, 200,0 }, + { 127, 127, 65, 0, 226, 120,0 }, + { 127, 127, 72, 0, 180, 100,0 }, + { 364, 365, 52, 4, 120, 0,0 }, + {2185,2186, 60, 4, 173, 0,0 }, + {1550,1551, 47, 4, 520, 0,0 }, + {1556,1557, 76, 4, 833, 0,0 }, + { 374, 375, 84, 4, 813, 0,0 }, + {1564,1565, 83, 4, 220, 0,0 }, + {1568,1569, 24, 4, 1840, 620,0 }, + {1556,1557, 77, 4, 820, 300,0 }, + {1572,1573, 60, 4, 286, 0,0 }, + {1574,1575, 65, 4, 293, 0,0 }, + { 391, 392, 44, 4, 160, 53,0 }, + { 391, 393, 40, 4, 460, 66,0 }, + {1606,1607, 72, 4, 120, 73,0 }, + { 398, 399, 73, 4, 1293, 173,0 }, + {1608,1609, 70, 4, 1580, 0,0 }, + {2187,2187, 0, 0, 40000, 353,0 }, + {2188,2188, 0, 0, 40000, 333,0 }, + {2189,2189, 0, 0, 5913, 2306,0 }, + {2190,2190, 0, 0, 7720, 1260,0 }, + {2191,2191, 0, 0, 213, 6420,0 }, + {2192,2192, 0, 0, 40000, 380,0 }, + {2193,2193, 0, 0, 1153, 760,0 }, + {2194,2194, 0, 0, 40000, 66,0 }, + {2195,2195, 0, 0, 4440, 66,0 }, + {2196,2196, 0, 0, 40000, 73,0 }, + {2197,2197, 0, 0, 40000, 53,0 }, + {2198,2198, 0, 0, 40000, 60,0 }, + {2199,2199, 0, 0, 40000, 60,0 }, + {2200,2200, 0, 0, 8133, 1433,0 }, + { 528, 528, 0, 0, 966, 346,0 }, + {2201,2201, 0, 0, 40000, 126,0 }, + {2202,2202, 0, 0, 286, 1293,0 }, + {2203,2203, 0, 0, 40000, 0,0 }, + {2204,2204, 41, 0, 246, 20,0 }, + {2205,2205, 84, 0, 160, 26,0 }, + {2206,2206, 72, 0, 440, 180,0 }, + { 741, 741, 48, 0, 220, 26,0 }, + {2207,2207, 0, 0, 2126, 173,0 }, + {2208,2208, 0, 0, 40000, 0,0 }, + {2209,2209, 0, 0, 40000, 380,0 }, + {2210,2210, 0, 0, 4553, 1486,0 }, + {2211,2211, 0, 0, 40000, 73,0 }, + {2212,2212, 0, 0, 40000, 73,0 }, + {2213,2213, 0, 0, 1460, 80,0 }, + {2214,2214, 0, 0, 40000, 66,0 }, + {2215,2215, 0, 0, 40000, 186,0 }, + {2216,2216, 0, 0, 40000, 180,0 }, + {2217,2217, 0, 0, 40000, 173,0 }, + {2218,2218, 0, 0, 40000, 113,0 }, + {2219,2219, 0, 0, 40000, 86,0 }, + {2220,2220, 0, 0, 40000, 373,0 }, + {2221,2221, 0, 0, 40000, 113,0 }, + {2222,2222, 0, 0, 40000, 353,0 }, + {2223,2223, 0, 0, 40000, 66,0 }, + {2224,2224, 0, 0, 40000, 53,0 }, + {2225,2225, 0, 0, 40000, 66,0 }, + {2226,2226, 0, 0, 40000, 100,0 }, + {2227,2227, 0, 0, 40000, 73,0 }, + {2228,2228, 0, 0, 40000, 73,0 }, + {2229,2229, 0, 0, 40000, 66,0 }, + {2230,2230, 0, 0, 40000, 66,0 }, + {2231,2231, 0, 0, 40000, 80,0 }, + {2232,2232, 0, 0, 40000, 66,0 }, + {2233,2233, 0, 0, 40000, 80,0 }, + {2234,2234, 0, 0, 40000, 660,0 }, + {2235,2235, 0, 0, 40000, 120,0 }, + {2236,2236, 0, 0, 9820, 393,0 }, + {2237,2237, 0, 0, 40000, 73,0 }, + {2238,2238, 0, 0, 3620, 1166,0 }, + {2239,2239, 0, 0, 40000, 0,0 }, + {2240,2240, 0, 0, 40000, 0,0 }, + {2241,2241, 0, 0, 3020, 66,0 }, + {2242,2242, 0, 0, 6053, 1240,0 }, + {2243,2243, 0, 0, 633, 126,0 }, + {2244,2244, 0, 0, 40000, 66,0 }, + {2245,2245, 0, 0, 40000, 73,0 }, + {2246,2246, 0, 0, 626, 246,0 }, + {2247,2247, 60, 0, 173, 93,0 }, + {2248,2248, 60, 0, 673, 206,0 }, + {2249,2249, 48, 0, 673, 200,0 }, + {2250,2250, 60, 0, 1873, 653,0 }, + {2251,2251, 60, 0, 673, 200,0 }, + {2252,2252, 66, 0, 306, 120,0 }, + {2253,2253, 60, 0, 673, 200,0 }, + {2249,2249, 64, 0, 673, 206,0 }, + {2254,2254, 60, 0, 246, 106,0 }, + {2255,2255, 60, 0, 193, 120,0 }, + {2256,2256, 56, 0, 206, 13,0 }, + {2257,2257, 53, 0, 433, 73,0 }, + {2258,2258, 60, 0, 220, 113,0 }, + {2259,2259, 48, 0, 300, 66,0 }, + {2260,2260, 67, 0, 273, 60,0 }, + {2261,2261, 60, 0, 973, 360,0 }, + {2261,2261, 72, 0, 806, 273,0 }, + {2262,2262, 60, 0, 173, 93,0 }, + {2263,2263, 0, 0, 2493, 866,0 }, + {2264,2264, 24, 0, 173, 93,0 }, + {2265,2265, 36, 0, 140, 0,0 }, + { 343, 343, 36, 0, 146, 80,0 }, + { 347, 347, 0, 0, 353, 133,0 }, + { 347, 347, 12, 0, 420, 146,0 }, + {2266,2266, 12, 0, 346, 100,0 }, + {2267,2267, 24, 0, 106, 46,0 }, + {2267,2267, 36, 0, 100, 0,0 }, + {2268,2268, 0, 0, 1006, 293,0 }, + {2266,2266, 24, 0, 293, 93,0 }, + {2269,2269, 88, 0, 1106, 120,0 }, + {2270,2270, 88, 0, 666, 120,0 }, + {2271,2271, 13, 0, 760, 360,0 }, + { 351, 351, 0, 0, 966, 346,0 }, + {2271,2271, 15, 0, 760, 420,0 }, + {2272,2272, 0, 0, 4513, 640,0 }, + {2273,2273, 0, 0, 15486, 1580,0 }, + {2274,2274, 0, 0, 6940, 66,0 }, + {2275,2275, 0, 0, 6866, 2380,0 }, + {2276,2276, 0, 0, 7613, 1566,0 }, + {2277,2277, 0, 0, 1186, 420,0 }, + {2278,2278, 0, 0, 1166, 400,0 }, + {2279,2279, 0, 0, 40000, 2940,0 }, + {2280,2280, 0, 0, 40000, 0,0 }, + {2281,2281, 0, 0, 18226, 786,0 }, + {2282,2282, 0, 0, 40000, 0,0 }, + {2283,2283, 0, 0, 713, 200,0 }, + {2284,2284, 0, 0, 40000, 126,0 }, + {2285,2285, 0, 0, 40000, 353,0 }, + {2286,2286, 0, 0, 40000, 333,0 }, + {2287,2287, 0, 0, 40000, 0,0 }, + {2288,2288, 0, 0, 40000, 0,0 }, + {2289,2289, 0, 0, 40000, 0,0 }, + {2290,2290, 0, 0, 40000, 0,0 }, + {2291,2291, 0, 0, 40000, 73,0 }, + {2292,2292, 0, 0, 40000, 66,0 }, + {2293,2293, 0, 0, 15893, 153,0 }, + {2294,2294, 0, 0, 40000, 253,0 }, + {2295,2295, 0, 0, 2813, 333,0 }, + {2296,2296, 0, 0, 40000, 3920,0 }, + {2297,2297, 79, 0, 113, 0,0 }, + {2297,2297, 72, 0, 126, 140,0 }, + {2298,2298, 72, 0, 100, 26,0 }, + {2298,2298, 79, 0, 100, 0,0 }, + { 554, 554, 60, 0, 400, 126,0 }, + {2299,2299, 72, 0, 793, 173,0 }, + {2300,2300, 84, 0, 226, 66,0 }, + { 555, 555, 66, 0, 113, 0,0 }, + {2301,2302, 35, 4, 2333, 800,0 }, + {2303,2304, 52, 4, 120, 0,0 }, + {2305,1548, 48, 4, 173, 0,0 }, + {1595,1595, 58, 0, 146, 166,0 }, + {2305,1548, 60, 4, 173, 0,0 }, + {2306,2307, 47, 4, 1893, 700,0 }, + {2306,2307, 43, 4, 1953, 740,0 }, + {2306,2307, 49, 4, 1880, 686,0 }, + {2306,2307, 51, 4, 1886, 706,0 }, + {2306,2307, 54, 4, 1906, 720,0 }, + {2306,2307, 57, 4, 1900, 720,0 }, + {2306,2307, 72, 4, 1593, 606,0 }, + {2306,2307, 60, 4, 1900, 720,0 }, + {2306,2307, 76, 4, 1593, 606,0 }, + {2306,2307, 84, 4, 1593, 613,0 }, + {2306,2307, 36, 4, 2386, 920,0 }, + {1560,2308, 65, 4, 293, 213,0 }, + {2309,2310, 84, 4, 1373, 306,0 }, + {1564,1564, 83, 0, 220, 113,0 }, + { 380, 381, 84, 4, 1593, 566,0 }, + {1568,1568, 24, 0, 1833, 613,0 }, + {2306,2307, 77, 4, 1593, 606,0 }, + {2311,2312, 60, 4, 286, 0,0 }, + {2313,2314, 65, 4, 513, 0,0 }, + {2315,2315, 59, 0, 106, 0,0 }, + {2316,2316, 51, 0, 386, 373,0 }, + {1612,1612, 45, 0, 393, 380,0 }, + {2317,2317, 71, 0, 446, 180,0 }, + {2318,2318, 60, 0, 280, 20,0 }, + {2319,2319, 58, 0, 393, 373,0 }, + {2320,2320, 53, 0, 393, 380,0 }, + { 397, 397, 64, 0, 220, 86,0 }, + {2321,2321, 71, 0, 106, 46,0 }, + {2322,2322, 61, 0, 986, 340,0 }, + {2323,2323, 61, 0, 1893, 633,0 }, + {2324, 392, 44, 4, 166, 46,0 }, + {2324, 393, 40, 4, 460, 60,0 }, + {1595,1595, 69, 0, 126, 140,0 }, + {1595,1595, 68, 0, 126, 140,0 }, + {1595,1595, 63, 0, 146, 166,0 }, + {2325,2326, 74, 4, 380, 106,0 }, + {2327,2328, 60, 4, 1026, 333,0 }, + {2329,2330, 80, 4, 40000, 0,0 }, + {2331,2332, 64, 4, 1900, 640,0 }, + { 397, 397, 72, 0, 193, 80,0 }, + {2333,2334, 78, 4, 820, 0,0 }, + {1608,1609, 82, 4, 1580, 0,0 }, + {2315,2315, 48, 0, 106, 0,0 }, + {2316,2316, 53, 0, 386, 373,0 }, + {2335,2335, 0, 0, 3586, 1133,0 }, + {2336,2337, 0, 4, 1186, 420,0 }, + {2338,2339, 0, 4, 40000, 320,0 }, + {2340,2340, 0, 0, 8826, 1346,0 }, + {2341,2341, 0, 0, 3440, 753,0 }, + {2342,2342, 0, 0, 40000, 360,0 }, + {2343,2343, 0, 0, 40000, 413,0 }, + {2344,2345, 0, 4, 40000, 60,0 }, + {2346,2346, 0, 0, 40000, 60,0 }, + {2347,2348, 0, 4, 40000, 126,0 }, + {2349,2350, 0, 4, 40000, 73,0 }, + {2351,2352, 0, 4, 40000, 73,0 }, + {2353,2354, 0, 4, 40000, 86,0 }, + {2355,2356, 0, 4, 40000, 453,0 }, + {2357,2357, 14, 0, 186, 20,0 }, + {2358,2358, 35, 0, 246, 73,0 }, + {2357,2357, 19, 0, 166, 26,0 }, + {2359,2359, 43, 0, 286, 133,0 }, + {2360,2360, 41, 0, 300, 113,0 }, + {2360,2360, 43, 0, 253, 106,0 }, + {2360,2360, 45, 0, 240, 100,0 }, + {2360,2360, 47, 0, 240, 100,0 }, + {2361,2362, 0, 4, 14720, 333,0 }, + {2363,2363, 0, 0, 7373, 1246,0 }, + {2364,2364, 0, 0, 4900, 233,0 }, + {2365,2365, 0, 0, 5106, 606,0 }, + {2366,2366, 0, 0, 1333, 153,0 }, + {2367,2367, 0, 0, 2093, 840,0 }, + {2368,2368, 0, 0, 3700, 226,0 }, + {2369,2369, 0, 0, 3546, 0,0 }, + {2370,2370, 0, 0, 4606, 420,0 }, + {2371,2371, 0, 0, 14366, 606,0 }, + {2372,2372, 0, 0, 40000, 426,0 }, + {2373,2373, 0, 0, 3700, 200,0 }, + {2374,2374, 0, 0, 880, 440,0 }, + {2375,2375, 0, 0, 4660, 660,0 }, + {2376,2376, 0, 0, 3600, 1153,0 }, + {2377,2377, 0, 0, 40000, 73,0 }, + {2378,2378, 0, 0, 40000, 53,0 }, + {2379,2379, 0, 0, 40000, 333,0 }, + {2380,2380, 0, 0, 40000, 73,0 }, + {2381,2381, 0, 0, 40000, 73,0 }, + {2382,2382, 0, 0, 40000, 66,0 }, + {2383,2383, 0, 0, 40000, 73,0 }, + {2384,2384, 0, 0, 40000, 73,0 }, + {2385,2385, 0, 0, 840, 226,0 }, + {2386,2386, 0, 0, 2093, 86,0 }, + {2387,2387, 0, 0, 906, 73,0 }, + { 402, 402, 0, 0, 273, 60,0 }, + {2388,2388, 0, 0, 40000, 820,0 }, + {2389,2389, 0, 0, 4740, 93,0 }, + {2390,2390, 0, 0, 706, 106,0 }, + {2391,2391, 0, 0, 40000, 0,0 }, + {2392,2392, 0, 0, 3840, 2306,0 }, + {2393,2393, 0, 0, 3400, 493,0 }, + {2394,2394, 0, 0, 40000, 53,0 }, + {2395,2395, 0, 0, 40000, 133,0 }, + {2396,2397, 0, 4, 3093, 1400,0 }, + {2398,2398, 0, 0, 1080, 580,0 }, + {2399,2400, 0, 4, 2220, 400,0 }, + {2401,2401, 0, 0, 40000, 193,0 }, + {2402,2402, 0, 0, 40000, 60,0 }, + {2403,2404, 0, 4, 40000, 146,0 }, + {2405,2406, 0, 4, 40000, 133,0 }, + {2407,2408, 0, 4, 40000, 66,0 }, + {2409,2409, 0, 0, 40000, 0,0 }, + {2410,2410, 0, 0, 40000, 73,0 }, + {2411,2411, 0, 0, 40000, 66,0 }, + {2412,2413, 0, 4, 40000, 153,0 }, + {2414,2414, 0, 0, 40000, 126,0 }, + {2415,2416, 0, 4, 40000, 466,0 }, + {2417,2418, 0, 4, 40000, 113,0 }, + {2419,2420, 0, 4, 1280, 73,0 }, + {2421,2422, 0, 4, 1113, 146,0 }, + {2423,2424, 0, 4, 3660, 113,0 }, + {2425,2426, 0, 4, 40000, 80,0 }, + {2427,2427, 33, 0, 300, 246,0 }, + {2428,2429, 38, 4, 53, 0,0 }, + {2430,2430, 38, 0, 106, 0,0 }, + {2431,2431, 38, 0, 340, 20,0 }, + {2432,2432, 40, 0, 73, 0,0 }, + {2433,2434, 41, 4, 300, 0,0 }, + {2435,2435, 0, 0, 133, 73,0 }, + {2435,2435, 41, 0, 133, 73,0 }, + {2360,2360, 48, 0, 240, 100,0 }, + {2436,2436, 17, 0, 4620, 1553,0 }, + {2360,2360, 50, 0, 240, 100,0 }, + {2435,2435, 45, 0, 126, 66,0 }, + {2437,2437,254, 2, 6, 0,0 }, + {2438,2438, 60, 0, 226, 93,0 }, + {2439,2439, 56, 0, 233, 93,0 }, + {2440,2440, 60, 0, 140, 66,0 }, + {2440,2440, 55, 0, 140, 60,0 }, + {2441,2441, 63, 0, 286, 126,0 }, + {2442,2442, 57, 0, 173, 93,0 }, + {2443,2443, 0, 0, 40000, 280,0 }, + {2444,2444, 0, 0, 40000, 0,0 }, + {2445,2445, 0, 0, 40000, 746,0 }, + {2446,2446, 0, 0, 40000, 353,0 }, + {2447,2447, 0, 0, 40000, 1173,0 }, + {2448,2448, 0, 0, 40000, 146,0 }, + {2449,2449, 0, 0, 40000, 1160,0 }, + {2450,2450, 0, 0, 40000, 353,0 }, + {2451,2451, 0, 0, 18313, 6046,0 }, + {2452,2452, 0, 0, 1206, 420,0 }, + { 752, 752, 55, 0, 173, 20,0 }, + {2453,2453, 0, 0, 2860, 806,0 }, + {2454,2454, 0, 0, 2506, 126,0 }, + {2455,2455, 0, 0, 520, 93,0 }, + {2456,2456, 0, 0, 1420, 160,0 }, + {2457,2457, 0, 0, 40000, 53,0 }, + {2458,2458, 0, 0, 9106, 100,0 }, + {2459,2459, 0, 0, 3706, 100,0 }, + {2460,2460, 0, 0, 17933, 100,0 }, + {2461,2461, 0, 0, 40000, 0,0 }, + {2462,2462, 0, 0, 40000, 66,0 }, + {2463,2463, 0, 0, 40000, 0,0 }, + { 884, 884, 0, 0, 306, 73,0 }, + { 884, 884, 28, 0, 306, 73,0 }, + {2464,2464, 29, 0, 226, 93,0 }, + { 886, 886, 31, 0, 113, 0,0 }, + { 360, 360, 32, 0, 133, 40,0 }, + { 361, 361, 33, 0, 286, 80,0 }, + {2453,2453, 34, 0, 2873, 813,0 }, + { 888, 888, 29, 0, 246, 46,0 }, + { 886, 886, 55, 0, 100, 0,0 }, + { 890, 890, 48, 0, 240, 60,0 }, + { 884, 884, 58, 0, 146, 26,0 }, + {2465,2465, 45, 0, 173, 93,0 }, + {2465,2465, 43, 0, 173, 93,0 }, + {2466,2466, 73, 0, 1633, 86,0 }, + {2467,2467, 72, 0, 866, 553,0 }, + {2468,2468, 76, 0, 1380, 0,0 }, + {2467,2467, 84, 0, 873, 560,0 }, + {2468,2468, 36, 0, 1933, 880,0 }, + {2469,2469, 65, 0, 300, 120,0 }, + {2470,2470, 83, 0, 193, 86,0 }, + {2471,2471, 50, 0, 966, 126,0 }, + {2468,2468, 77, 0, 1373, 620,0 }, + { 897, 897, 55, 0, 126, 40,0 }, + {2472,2472, 60, 0, 180, 140,0 }, + { 897, 897, 50, 0, 126, 40,0 }, + {2473,2473, 42, 0, 633, 240,0 }, + {2473,2473, 46, 0, 513, 200,0 }, + {2474,2474, 71, 0, 433, 180,0 }, + {2474,2474, 60, 0, 513, 206,0 }, + {2455,2455, 58, 0, 220, 46,0 }, + {2455,2455, 53, 0, 286, 60,0 }, + {2475,2475, 91, 0, 186, 100,0 }, + {2476,2476, 61, 0, 226, 26,0 }, + {2477,2477, 61, 0, 886, 73,0 }, + {2478,2478, 44, 0, 120, 73,0 }, + {2479,2479, 40, 0, 933, 73,0 }, + {2480,2480, 69, 0, 146, 33,0 }, + { 361, 361, 68, 0, 153, 26,0 }, + { 361, 361, 63, 0, 180, 26,0 }, + {2481,2481, 74, 0, 153, 73,0 }, + {2482,2482, 60, 0, 280, 100,0 }, + { 908, 908, 80, 0, 160, 26,0 }, + {2483,2483, 64, 0, 986, 353,0 }, + {2483,2483, 73, 0, 813, 306,0 }, + {2483,2483, 70, 0, 820, 306,0 }, + { 886, 886, 68, 0, 93, 0,0 }, + { 886, 886, 48, 0, 106, 0,0 }, + {2484,2484, 0, 0, 40000, 0,0 }, + {2485,2485, 0, 0, 3226, 753,0 }, + {2486,2486, 0, 0, 1773, 553,0 }, + {2487,2487, 0, 0, 7473, 2460,0 }, + {2488,2488, 0, 0, 40000, 0,0 }, + {2489,2489, 0, 0, 40000, 353,0 }, + {2490,2490, 0, 0, 40000, 206,0 }, + {2491,2491, 0, 0, 40000, 86,0 }, + {2492,2492, 0, 0, 4740, 86,0 }, + {2493,2493, 0, 0, 6193, 193,0 }, + {2494,2494, 0, 0, 6200, 240,0 }, + {2495,2495, 0, 0, 40000, 0,0 }, + {2496,2496, 0, 0, 1586, 73,0 }, + {2497,2497, 0, 0, 560, 73,0 }, + {2498,2498, 0, 0, 40000, 480,0 }, + {2499,2499, 0, 0, 40000, 80,0 }, + {2500,2500, 0, 0, 40000, 66,0 }, + {2501,2501, 0, 0, 40000, 380,0 }, + {2502,2502, 0, 0, 280, 100,0 }, + {2503,2503, 0, 0, 6193, 233,0 }, + {2504,2504, 0, 0, 40000, 380,0 }, + {2505,2505, 0, 0, 40000, 0,0 }, + {2506,2506, 0, 0, 40000, 380,0 }, + {2507,2507, 0, 0, 40000, 200,0 }, + {2508,2508, 0, 0, 40000, 320,0 }, + {2509,2509, 0, 0, 40000, 126,0 }, + {2510,2510, 0, 0, 40000, 293,0 }, + {2511,2511, 0, 0, 40000, 0,0 }, + {2512,2512, 0, 0, 40000, 40,0 }, + {2513,2513, 0, 0, 40000, 106,0 }, + {2514,2514, 0, 0, 3846, 73,0 }, + {2515,2515, 0, 0, 40000, 0,0 }, + {2516,2516, 0, 0, 40000, 73,0 }, + {2517,2517, 0, 0, 40000, 533,0 }, + {2518,2518, 0, 0, 40000, 1020,0 }, + {2519,2519, 0, 0, 40000, 73,0 }, + {2520,2520, 0, 0, 40000, 53,0 }, + {2521,2521, 0, 0, 6153, 1433,0 }, + {2522,2522, 0, 0, 18813, 773,0 }, + {2523,2523, 0, 0, 40000, 433,0 }, + {2524,2524, 0, 0, 40000, 0,0 }, + {2525,2525, 0, 0, 40000, 133,0 }, + {2526,2526, 0, 0, 4486, 73,0 }, + { 346, 346, 30, 0, 540, 33,0 }, + { 346, 346, 31, 0, 406, 20,0 }, + { 346, 346, 32, 0, 406, 20,0 }, + { 346, 346, 33, 0, 406, 73,0 }, + { 346, 346, 34, 0, 406, 20,0 }, + { 346, 346, 35, 0, 406, 20,0 }, + { 346, 346, 37, 0, 406, 73,0 }, + { 346, 346, 39, 0, 406, 73,0 }, + { 346, 346, 41, 0, 406, 20,0 }, + { 346, 346, 43, 0, 306, 20,0 }, + { 346, 346, 45, 0, 306, 20,0 }, + { 346, 346, 47, 0, 306, 20,0 }, + { 346, 346, 48, 0, 306, 20,0 }, + { 346, 346, 49, 0, 306, 20,0 }, + { 512, 512, 84, 0, 353, 466,0 }, + {2206,2206, 84, 0, 440, 180,0 }, + {2527,2527, 55, 0, 100, 0,0 }, + {2528,2528, 36, 0, 400, 160,0 }, + {2529,2529, 38, 0, 313, 226,0 }, + {2530,2530, 60, 0, 286, 133,0 }, + {2531,2531, 38, 0, 200, 100,0 }, + {2532,2532, 17, 0, 6186, 240,0 }, + {2532,2532, 18, 0, 6186, 240,0 }, + {2532,2532, 19, 0, 6193, 233,0 }, + {2532,2532, 20, 0, 6193, 193,0 }, + {2532,2532, 21, 0, 6193, 193,0 }, + {2532,2532, 22, 0, 6193, 193,0 }, + {2532,2532, 23, 0, 6193, 193,0 }, + {2532,2532, 24, 0, 6193, 193,0 }, + {2532,2532, 25, 0, 6193, 193,0 }, + {2532,2532, 26, 0, 6193, 193,0 }, + {2532,2532, 27, 0, 6193, 253,0 }, + {2532,2532, 28, 0, 6193, 246,0 }, + {2532,2532, 29, 0, 6193, 246,0 }, + {2533,2533, 84, 0, 433, 180,0 }, + {2534,2534, 48, 0, 280, 93,0 }, + {2535,2535, 65, 0, 1166, 360,0 }, + {2536,2536, 65, 0, 1853, 633,0 }, + {2537,2537, 55, 0, 453, 366,0 }, + {2537,2537, 41, 0, 540, 433,0 }, + { 346, 346, 63, 0, 240, 66,0 }, + { 346, 346, 55, 0, 240, 66,0 }, + {2538,2538, 55, 0, 2586, 200,0 }, + {2538,2538, 53, 0, 2586, 200,0 }, + {2534,2534, 50, 0, 280, 93,0 }, + { 506, 506, 84, 0, 693, 566,0 }, + { 506, 506, 74, 0, 693, 560,0 }, + { 504, 504, 84, 0, 1566, 546,0 }, + { 504, 504, 74, 0, 1586, 560,0 }, + {2539,2539, 84, 0, 440, 20,0 }, + {2540,2540, 74, 0, 126, 26,0 }, + {1911,1911, 48, 0, 500, 180,0 }, + {1911,1911, 36, 0, 606, 220,0 }, + {2541,2541, 74, 0, 686, 560,0 }, + {2542,2542, 0, 0, 7313, 13,0 }, + {2543,2543, 0, 0, 40000, 1306,0 }, + {2544,2544, 0, 0, 40000, 0,0 }, + {2545,2545, 0, 0, 4613, 13,0 }, + {2546,2547, 0, 4, 6933, 133,0 }, + {2548,2549, 0, 4, 40000, 86,0 }, + {2550,2550, 0, 0, 9233, 100,0 }, + {2551,2552, 0, 4, 4640, 73,0 }, + {2553,2553, 0, 0, 40000, 73,0 }, + {2554,2554, 0, 0, 40000, 0,0 }, + {2555,2556, 0, 4, 40000, 73,0 }, + {2557,2557, 0, 0, 40000, 60,0 }, + {2558,1467, 0, 4, 40000, 66,0 }, + {2559,2560, 0, 4, 40000, 40,0 }, + {2561,2561, 0, 0, 40000, 186,0 }, + {2562,2562, 0, 0, 4026, 66,0 }, + {2563,2564, 0, 4, 14586, 80,0 }, + {2565,2565, 0, 0, 40000, 0,0 }, + {2566,2567, 0, 4, 40000, 40,0 }, + {2568,2568, 0, 0, 4020, 73,0 }, + {2569,2569, 0, 0, 40000, 0,0 }, + {2570,2570, 0, 0, 40000, 0,0 }, + {2571,2572, 0, 4, 40000, 126,0 }, + {2573,2574, 0, 4, 40000, 100,0 }, + {2575,2575, 0, 0, 40000, 213,0 }, + { 229,2576, 0, 4, 40000, 166,0 }, + {2577,2577, 0, 0, 7366, 53,0 }, + { 239,2578, 0, 4, 40000, 133,0 }, + {2579,2579, 0, 0, 40000, 80,0 }, + {2580,2580, 0, 0, 40000, 140,0 }, + {2581,2582, 0, 4, 16980, 1173,0 }, + {2583,2584, 0, 4, 726, 100,0 }, + {2585,2586, 0, 4, 40000, 73,0 }, + {2587,2588, 0, 4, 40000, 73,0 }, + {2589,2589, 0, 0, 40000, 60,0 }, + {2590,2590, 0, 0, 40000, 80,0 }, + {2591,2592, 0, 4, 40000, 73,0 }, + {2593,2594, 0, 4, 40000, 60,0 }, + {2595,2595, 0, 0, 40000, 66,0 }, + {2596,2597, 0, 4, 40000, 66,0 }, + {2598,2599, 0, 4, 40000, 60,0 }, + {2600,2601, 0, 4, 40000, 173,0 }, + {2602,2602, 0, 0, 40000, 60,0 }, + {2603,2603, 0, 0, 40000, 73,0 }, + {2604,2604, 0, 0, 40000, 93,0 }, + {2605,2606, 0, 4, 40000, 73,0 }, + {2607,2607, 0, 0, 40000, 66,0 }, + {2608,2609, 0, 4, 40000, 66,0 }, + {2610,2610, 0, 0, 40000, 86,0 }, + {2611,2611, 0, 0, 40000, 60,0 }, + {2612,2612, 0, 0, 14286, 73,0 }, + {2613,2613, 0, 0, 40000, 0,0 }, + {2614,2615, 0, 4, 40000, 73,0 }, + {2616,2617, 0, 4, 40000, 66,0 }, + {2618,2619, 0, 4, 133, 0,0 }, + {2620,2621, 0, 4, 40000, 1280,0 }, + {2622,2623, 0, 4, 40000, 160,0 }, + {2624,2625, 0, 4, 40000, 0,0 }, + {2626,2627, 0, 4, 40000, 73,0 }, + {2628,2629, 0, 4, 40000, 0,0 }, + {1516,2630, 0, 4, 1193, 406,0 }, + {2631,2632, 0, 4, 40000, 553,0 }, + {2633,2633, 0, 0, 40000, 40,0 }, + {2634,2635, 0, 4, 40000, 773,0 }, + {2636,2636, 0, 0, 40000, 320,0 }, + {2637,2637, 0, 0, 1880, 73,0 }, + {2638,2639, 0, 4, 486, 0,0 }, + {2640,2641, 0, 4, 17020, 1193,0 }, + {2642,2642, 0, 0, 40000, 720,0 }, + {2643,2644, 0, 4, 1880, 40,0 }, + {2645,2645, 0, 0, 40000, 73,0 }, + {2646,2647, 0, 4, 40000, 46,0 }, + {2648,2648, 0, 0, 2466, 80,0 }, + {2649,2649, 0, 0, 40000, 193,0 }, + {2650,2651, 0, 4, 993, 73,0 }, + {2652,2652, 0, 0, 40000, 220,0 }, + {2653,2654, 0, 4, 40000, 46,0 }, + {2655,2656, 0, 4, 40000, 46,0 }, + {2657,2657, 0, 0, 40000, 66,0 }, + {2658,2658, 35, 0, 626, 20,0 }, + {2659,2659, 35, 0, 306, 26,0 }, + {2660,2660, 52, 0, 126, 26,0 }, + {2661,2661, 60, 0, 286, 20,0 }, + {2662,2662, 58, 0, 113, 0,0 }, + {2663,2663, 60, 0, 380, 20,0 }, + {2664,2664, 50, 0, 1640, 66,0 }, + {2665,2665, 43, 0, 153, 20,0 }, + {2664,2664, 55, 0, 1640, 20,0 }, + {1553,1553, 43, 0, 160, 80,0 }, + {2666,2666, 50, 0, 980, 20,0 }, + {2667,2667, 43, 0, 446, 73,0 }, + {2666,2666, 53, 0, 1000, 80,0 }, + {2666,2666, 57, 0, 700, 73,0 }, + {2668,2668, 72, 0, 773, 13,0 }, + {2666,2666, 60, 0, 686, 20,0 }, + { 373, 373, 76, 0, 826, 20,0 }, + {2669,2669, 84, 0, 713, 20,0 }, + {2670,2670, 42, 0, 1186, 20,0 }, + {2671,2671, 65, 0, 293, 33,0 }, + {2672,2672, 84, 0, 386, 33,0 }, + {2673,2673, 84, 0, 1366, 20,0 }, + {2674,2674, 24, 0, 960, 73,0 }, + { 383, 383, 77, 0, 800, 20,0 }, + {2675,2675, 58, 0, 426, 26,0 }, + {2676,2676, 53, 0, 426, 20,0 }, + {2677,2677, 64, 0, 200, 66,0 }, + {2678,2678, 71, 0, 113, 13,0 }, + {2679,2679, 44, 0, 766, 66,0 }, + {2680,2680, 40, 0, 460, 60,0 }, + {2681,2681, 69, 0, 126, 26,0 }, + {2682,2682, 60, 0, 573, 66,0 }, + {2683,2683, 80, 0, 226, 20,0 }, + {2684,2684, 64, 0, 2693, 20,0 }, + {2685,2685, 72, 0, 120, 66,0 }, + {2686,2686, 70, 0, 820, 20,0 }, + {2687,2687, 48, 0, 173, 20,0 }, + {2688,2688, 53, 0, 980, 33,0 }, + {2689,2690, 0, 4, 40000, 286,0 }, + {2691,2692, 0, 4, 2340, 100,0 }, + {2693,2694, 0, 4, 380, 80,0 }, + {2695,2696, 0, 4, 14793, 73,0 }, + {2697,2698, 0, 4, 40000, 40,0 }, + { 192,2699, 0, 4, 40000, 73,0 }, + {2700,2701, 0, 4, 973, 126,0 }, + {2702,2703, 0, 4, 4666, 106,0 }, + {2704,2705, 0, 4, 40000, 73,0 }, + {2706,2707, 0, 4, 40000, 73,0 }, + {2708,2709, 0, 4, 40000, 73,0 }, + {2710,2711, 0, 4, 2053, 0,0 }, + {2712,1473, 0, 4, 320, 26,0 }, + {2713,2714, 0, 4, 573, 93,0 }, + {2715,2716, 0, 4, 6513, 0,0 }, + {1478,2717, 0, 4, 40000, 146,0 }, + {2718,2719, 0, 4, 40000, 66,0 }, + { 286,2720, 0, 4, 40000, 73,0 }, + {2721,2722, 0, 4, 40000, 86,0 }, + {2723,2724, 0, 4, 40000, 60,0 }, + {2725,2726, 0, 4, 393, 73,0 }, + {2727,2724, 0, 4, 40000, 60,0 }, + {1514,2728, 0, 4, 40000, 180,0 }, + {2729,2730, 0, 4, 40000, 0,0 }, + {2731,2732, 0, 4, 486, 0,0 }, + {2733,2734, 0, 4, 733, 0,0 }, + {2735,2736, 0, 4, 286, 40,0 }, + {2737,2738, 0, 4, 40000, 73,0 }, + {2739,2740, 0, 4, 1326, 746,0 }, + {2741,2742, 0, 4, 1340, 700,0 }, + {2743,2744, 0, 4, 40000, 0,0 }, + {2745,2746, 0, 4, 2046, 0,0 }, + {2747,2747, 35, 0, 386, 166,0 }, + {2748,2748, 60, 0, 493, 193,0 }, + {2749,2749, 43, 0, 126, 66,0 }, + {2750,2750, 0, 0, 3740, 1260,0 }, + {2751,2752, 0, 4, 14846, 353,0 }, + {2753,2754, 0, 4, 10266, 0,0 }, + {2755,2756, 0, 4, 18286, 146,0 }, + {2757,2758, 0, 4, 14520, 333,0 }, + {2759,2760, 0, 4, 14686, 633,0 }, + {2761,2762, 0, 4, 14826, 300,0 }, + {2763,2764, 0, 4, 10493, 0,0 }, + {2765,2766, 0, 4, 40000, 60,0 }, + {2767,2768, 0, 4, 40000, 80,0 }, + {2769,2770, 0, 4, 40000, 80,0 }, + {2771,2772, 0, 4, 40000, 73,0 }, + {2773,2774, 0, 4, 40000, 73,0 }, + {2775,2776, 0, 4, 40000, 80,0 }, + {2777,2778, 0, 4, 40000, 73,0 }, + {2779,2780, 0, 4, 40000, 73,0 }, + {2781,2782, 0, 4, 40000, 66,0 }, + {2783,2784, 0, 4, 7260, 186,0 }, + {2785,2786, 0, 4, 10386, 0,0 }, + {2787,2788, 0, 4, 40000, 246,0 }, + {2789,2790, 0, 4, 9173, 746,0 }, + {2791,2792, 0, 4, 7440, 666,0 }, + {2793,2794, 0, 4, 40000, 0,0 }, + {2795,2796, 0, 4, 40000, 413,0 }, + {2795,2797, 0, 4, 40000, 1506,0 }, + {2798,2799, 0, 4, 40000, 60,0 }, + {2800,2801, 0, 4, 40000, 233,0 }, + {2802,2803, 0, 4, 40000, 80,0 }, + {2804,2805, 0, 4, 40000, 80,0 }, + {2806,2807, 0, 4, 4520, 80,0 }, + {2808,2809, 0, 4, 40000, 73,0 }, + {2810,2811, 0, 4, 1186, 100,0 }, + {2812,2813, 0, 4, 953, 153,0 }, + {2814,2815, 0, 4, 14786, 126,0 }, + {2816,2817, 0, 4, 14800, 193,0 }, + {2818,2819, 0, 4, 14573, 626,0 }, + {2820,2821, 0, 4, 2200, 73,0 }, + {2822,2823, 0, 4, 373, 86,0 }, + {2824,2825, 0, 4, 12780, 200,0 }, + {2826,2827, 0, 4, 40000, 73,0 }, + {2828,2829, 0, 4, 9193, 146,0 }, + {2830,2831, 0, 4, 2540, 326,0 }, + {2832,2833, 0, 4, 6933, 200,0 }, + {2834,2835, 0, 4, 40000, 413,0 }, + {2836,2837, 0, 4, 4826, 1313,0 }, + {2838,2839, 0, 4, 14740, 340,0 }, + {2840,2841, 0, 4, 1886, 653,0 }, + {2842,2843, 0, 4, 5280, 260,0 }, + {2844,2845, 0, 4, 40000, 240,0 }, + {2846,2847, 0, 4, 40000, 240,0 }, + {2848,2849, 0, 4, 40000, 240,0 }, + {2850,2851, 0, 4, 40000, 406,0 }, + {2852,2853, 0, 4, 40000, 406,0 }, + {2854,2855, 0, 4, 40000, 146,0 }, + {2856,2856, 0, 0, 2400, 1126,0 }, + {2857,2857, 0, 0, 2400, 1126,0 }, + {2858,2859, 0, 4, 4613, 73,0 }, + {2860,2861, 0, 4, 40000, 426,0 }, + {2862,2863, 0, 4, 4580, 100,0 }, + {2864,2865, 0, 4, 40000, 80,0 }, + {2866,2867, 0, 4, 5300, 53,0 }, + {2868,2869, 0, 4, 5313, 113,0 }, + {2870,2871, 0, 4, 7080, 186,0 }, + {2872,2873, 0, 4, 4720, 106,0 }, + {2874,2875, 0, 4, 40000, 73,0 }, + {2876,2877, 0, 4, 1640, 0,0 }, + {2878,2879, 0, 4, 7306, 186,0 }, + {2880,2881, 0, 4, 7373, 1246,0 }, + {2882,2883, 0, 4, 4620, 93,0 }, + {2884,2885, 0, 4, 3460, 926,0 }, + {2886,2887, 0, 4, 40000, 73,0 }, + {2888,2888, 0, 0, 18926, 426,0 }, + {2889,2889, 0, 0, 18520, 73,0 }, + {2890,2890, 0, 0, 18473, 73,0 }, + {2891,2892, 0, 4, 40000, 93,0 }, + {2893,2893, 0, 0, 8006, 133,0 }, + {2894,2894, 0, 0, 18533, 66,0 }, + {2895,2895, 0, 0, 14786, 4966,0 }, + {2896,2897, 0, 4, 40000, 80,0 }, + {2898,2899, 0, 4, 40000, 73,0 }, + {2353,2900, 0, 4, 18520, 86,0 }, + {2901,2901, 0, 0, 40000, 0,0 }, + {2902,2903, 0, 4, 40000, 100,0 }, + {2904,2905, 0, 4, 40000, 93,0 }, + {2906,2907, 0, 4, 40000, 73,0 }, + {2908,2909, 0, 4, 10720, 153,0 }, + {2910,2911, 0, 4, 40000, 73,0 }, + {2912,2912, 0, 0, 40000, 40,0 }, + {2913,2914, 0, 4, 8720, 446,0 }, + {2915,2916, 0, 4, 14706, 653,0 }, + {2917,2918, 0, 4, 9213, 426,0 }, + {2919,2920, 0, 4, 9286, 240,0 }, + {2921,2922, 0, 4, 8706, 413,0 }, + {2923,2924, 0, 4, 2233, 346,0 }, + {2925,2926, 0, 4, 2373, 426,0 }, + {2927,2928, 0, 4, 2353, 233,0 }, + {2929,2929, 0, 0, 40000, 140,0 }, + {2930,2931, 0, 4, 40000, 100,0 }, + {2932,2933, 0, 4, 40000, 73,0 }, + {2934,2935, 0, 4, 40000, 80,0 }, + {2936,2937, 0, 4, 40000, 80,0 }, + {2938,2939, 0, 4, 40000, 246,0 }, + {2940,2940, 0, 0, 553, 446,0 }, + {2941,2941, 0, 0, 40000, 193,0 }, + {2942,2943, 0, 4, 1206, 406,0 }, + {2944,2944, 0, 0, 7026, 1553,0 }, + {2945,2945, 0, 0, 3426, 360,0 }, + {2946,2947, 0, 4, 7313, 646,0 }, + {2948,2948, 0, 0, 40000, 386,0 }, + {2949,2949, 0, 0, 1953, 726,0 }, + {2950,2951, 0, 4, 14606, 106,0 }, + {2952,2953, 0, 4, 40000, 1566,0 }, + {2954,2954, 60, 2, 6, 0,0 }, + {2955,2956, 0, 4, 40000, 240,0 }, + {2957,2958, 0, 4, 40000, 80,0 }, + {2959,2960, 0, 4, 40000, 113,0 }, + {2961,2962, 0, 4, 40000, 240,0 }, + {2963,2963, 0, 0, 8506, 680,0 }, + {2964,2964, 0, 0, 40000, 1593,0 }, + {2436,2436, 49, 0, 1873, 633,0 }, + {2357,2357, 61, 0, 113, 20,0 }, + {2357,2357, 56, 0, 113, 26,0 }, + {2357,2357, 58, 0, 113, 26,0 }, + {2357,2357, 49, 0, 126, 26,0 }, + {2357,2357, 44, 0, 126, 26,0 }, + {2965,2965, 0, 0, 40000, 380,0 }, + {2966,2966, 0, 0, 4440, 66,0 }, + {2967,2967, 0, 0, 8133, 1433,0 }, + {2968,2968, 0, 0, 40000, 126,0 }, + {2969,2969, 0, 0, 40000, 0,0 }, + {2970,2970, 84, 0, 160, 26,0 }, + {2971,2971, 72, 0, 440, 180,0 }, + {2972,2972, 0, 0, 8313, 580,0 }, + {2973,2973, 0, 0, 40000, 160,0 }, + {2974,2974, 0, 0, 40000, 3000,0 }, + {2975,2975, 0, 0, 8300, 493,0 }, + {2976,2976, 0, 0, 973, 673,0 }, + {2977,2977, 0, 0, 40000, 73,0 }, + {2978,2978, 0, 0, 40000, 133,0 }, + {2979,2979, 0, 0, 40000, 140,0 }, + {2980,2980, 0, 0, 40000, 346,0 }, + {2981,2981, 0, 0, 40000, 1006,0 }, + {2982,2982, 0, 0, 40000, 966,0 }, + {2983,2983, 0, 0, 40000, 0,0 }, + {2984,2984, 0, 0, 40000, 0,0 }, + {2985,2985, 0, 0, 40000, 66,0 }, + {2986,2986, 0, 0, 40000, 66,0 }, + {2987,2987, 0, 0, 40000, 46,0 }, + {2988,2988, 0, 0, 40000, 533,0 }, + {2989,2989, 0, 0, 2400, 780,0 }, + {2990,2990, 0, 0, 820, 66,0 }, + {2991,2991, 0, 0, 40000, 240,0 }, + {2992,2992, 0, 0, 40000, 220,0 }, + {2993,2993, 0, 0, 40000, 0,0 }, + {2994,2994, 0, 0, 15100, 73,0 }, + {2995,2995, 0, 0, 40000, 200,0 }, + {2996,2996, 0, 0, 2426, 93,0 }, + {2997,2997, 0, 0, 4640, 1553,0 }, + {2998,2998, 0, 0, 40000, 73,0 }, + {2999,2999, 0, 0, 40000, 73,0 }, + {3000,3000, 0, 0, 1133, 633,0 }, + {3001,3001, 0, 0, 40000, 0,0 }, + {3002,3002, 0, 0, 40000, 1006,0 }, + {3003,3003, 0, 0, 4653, 653,0 }, + {3004,3004, 0, 0, 40000, 1000,0 }, + {3005,3005, 0, 0, 40000, 53,0 }, + {3006,3006, 0, 0, 40000, 60,0 }, + {3007,3007, 0, 0, 40000, 0,0 }, + { 350, 350, 0, 0, 513, 200,0 }, + {3008,3008, 0, 0, 213, 106,0 }, + {3009,3009, 0, 0, 280, 126,0 }, + {3010,3010, 0, 0, 1193, 426,0 }, + {3011,3011, 0, 0, 14653, 4906,0 }, + {3012,3012, 0, 0, 1040, 326,0 }, + {3013,3013, 0, 0, 5740, 2326,0 }, + {3014,3014, 0, 0, 40000, 73,0 }, + {3015,3015, 0, 0, 40000, 240,0 }, + { 350, 350, 36, 0, 380, 153,0 }, + { 369, 369, 37, 0, 213, 66,0 }, + {3008,3008, 38, 0, 213, 106,0 }, + { 369, 369, 24, 0, 193, 13,0 }, + {3008,3008, 32, 0, 206, 106,0 }, + { 369, 369, 48, 0, 186, 20,0 }, + {3009,3009, 42, 0, 220, 106,0 }, + { 369, 369, 50, 0, 186, 73,0 }, + { 369, 369, 52, 0, 186, 73,0 }, + { 369, 369, 54, 0, 186, 33,0 }, + { 369, 369, 55, 0, 186, 33,0 }, + { 369, 369, 57, 0, 180, 33,0 }, + {3010,3010, 51, 0, 966, 353,0 }, + { 144, 144, 61, 0, 213, 126,0 }, + {3016,3016, 0, 0, 8340, 520,0 }, + {3016,3016, 63, 0, 6106, 373,0 }, + {3016,3016, 64, 0, 6073, 380,0 }, + {3017,3017, 40, 0, 206, 100,0 }, + {3017,3017, 70, 0, 160, 93,0 }, + {3018,3018, 0, 0, 40000, 73,0 }, + {3019,3019, 0, 0, 40000, 73,0 }, + {3020,3020, 0, 0, 40000, 73,0 }, + {3021,3021, 0, 0, 40000, 73,0 }, + {3022,3022, 38, 0, 246, 33,0 }, + {2441,2441, 57, 0, 286, 126,0 }, + {3023,3023, 63, 0, 146, 126,0 }, + {3024,3024, 74, 0, 280, 73,0 }, + {3025,3025, 74, 0, 453, 100,0 }, + {3026,3026, 60, 0, 666, 33,0 }, + {1439,1440, 0, 0, 13566, 273,0 }, + {1593,1594, 35, 0, 2200, 673,0 }, + {1564,1565, 35, 0, 740, 280,0 }, + {1443,1444, 0, 0, 11886, 333,0 }, + {1481,1482, 0, 0, 40000, 133,0 }, + { 185, 186, 0, 0, 5980, 1540,0 }, + { 235, 237, 0, 0, 3366, 1093,0 }, + { 239, 240, 0, 0, 40000, 133,0 }, + {1477,1476, 0, 0, 40000, 160,0 }, + { 268, 269, 0, 0, 40000, 80,0 }, + { 176, 177, 0, 0, 40000, 0,0 }, + {1490,1491, 0, 0, 40000, 60,0 }, + { 231, 232, 0, 0, 40000, 146,0 }, + { 233, 234, 0, 0, 40000, 433,0 }, + { 254, 255, 0, 0, 40000, 93,0 }, + { 192, 193, 0, 0, 40000, 66,0 }, + { 252, 253, 0, 0, 40000, 73,0 }, + { 248,3027, 0, 0, 40000, 80,0 }, + { 39,1476, 0, 0, 40000, 160,0 }, + { 241, 242, 0, 0, 40000, 146,0 }, + {1508,1509, 0, 0, 40000, 0,0 }, + { 246, 247, 0, 0, 3966, 800,0 }, + { 181,1451, 0, 0, 2153, 640,0 }, + { 209, 210, 0, 0, 4453, 100,0 }, + { 270, 271, 0, 0, 40000, 80,0 }, + { 115,1533, 0, 0, 4260, 1720,0 }, + {1454,1455, 0, 0, 40000, 0,0 }, + { 107, 319, 0, 0, 1266, 413,0 }, + { 46, 238, 0, 0, 6873, 1246,0 }, + { 216, 217, 0, 0, 4046, 100,0 }, + { 272, 273, 0, 0, 40000, 126,0 }, + {1445,3028, 0, 0, 9966, 386,0 }, + { 172, 173, 0, 0, 7340, 100,0 }, + { 174, 175, 0, 0, 6913, 100,0 }, + {1447,3029, 0, 0, 10306, 80,0 }, + {1452,3030, 0, 0, 9240, 240,0 }, + { 183, 184, 0, 0, 586, 253,0 }, + {1456,1457, 0, 0, 1386, 180,0 }, + {1458,1459, 0, 0, 40000, 60,0 }, + { 190,1460, 0, 0, 40000, 46,0 }, + {1462,1463, 0, 0, 40000, 340,0 }, + {1464,1465, 0, 0, 40000, 360,0 }, + { 195, 196, 0, 0, 40000, 66,0 }, + { 197, 198, 0, 0, 40000, 86,0 }, + { 199, 200, 0, 0, 40000, 60,0 }, + { 201, 202, 0, 0, 3713, 100,0 }, + { 203, 204, 0, 0, 14633, 126,0 }, + { 205, 206, 0, 0, 9440, 153,0 }, + { 214, 215, 0, 0, 17020, 100,0 }, + { 218, 219, 0, 0, 14000, 180,0 }, + { 220, 221, 0, 0, 2846, 100,0 }, + {1472,1473, 0, 0, 8066, 66,0 }, + {1474,1475, 0, 0, 8040, 93,0 }, + { 225, 226, 0, 0, 8066, 106,0 }, + { 229, 230, 0, 0, 40000, 160,0 }, + {1478,1479, 0, 0, 40000, 413,0 }, + { 50,1480, 0, 0, 40000, 393,0 }, + {1485,1486, 0, 0, 40000, 226,0 }, + { 258, 259, 0, 0, 40000, 73,0 }, + { 262, 263, 0, 0, 40000, 160,0 }, + { 264, 265, 0, 0, 40000, 160,0 }, + {1494,1495, 0, 0, 40000, 80,0 }, + {1496,1497, 0, 0, 40000, 73,0 }, + { 274, 275, 0, 0, 40000, 100,0 }, + {1499,1500, 0, 0, 40000, 73,0 }, + { 277, 278, 0, 0, 40000, 173,0 }, + { 279, 280, 0, 0, 40000, 160,0 }, + { 281, 282, 0, 0, 40000, 173,0 }, + {1501,1502, 0, 0, 40000, 146,0 }, + { 284, 285, 0, 0, 40000, 66,0 }, + { 286, 287, 0, 0, 40000, 86,0 }, + {1503,1504, 0, 0, 40000, 86,0 }, + {1505,1506, 0, 0, 40000, 73,0 }, + { 289, 290, 0, 0, 40000, 66,0 }, + { 291, 292, 0, 0, 40000, 160,0 }, + { 293, 294, 0, 0, 40000, 200,0 }, + { 86,1507, 0, 0, 40000, 80,0 }, + { 88, 297, 0, 0, 40000, 1346,0 }, + { 298, 299, 0, 0, 40000, 320,0 }, + { 300, 301, 0, 0, 40000, 1273,0 }, + {1514,1515, 0, 0, 40000, 100,0 }, + {1518,1519, 0, 0, 4733, 0,0 }, + {1520,1521, 0, 0, 40000, 440,0 }, + {1524,1525, 0, 0, 40000, 1180,0 }, + {1526,1527, 0, 0, 40000, 746,0 }, + {1528,1529, 0, 0, 40000, 920,0 }, + { 311, 312, 0, 0, 15306, 213,0 }, + { 313, 314, 0, 0, 7280, 340,0 }, + { 315, 316, 0, 0, 3693, 346,0 }, + { 317, 318, 0, 0, 13720, 4033,0 }, + { 108, 320, 0, 0, 40000, 66,0 }, + { 109, 321, 0, 0, 40000, 180,0 }, + { 322, 323, 0, 0, 40000, 73,0 }, + { 111,1530, 0, 0, 4053, 426,0 }, + { 324, 325, 0, 0, 626, 260,0 }, + { 326, 327, 0, 0, 1166, 400,0 }, + {1531,1532, 0, 0, 186, 340,0 }, + {1534,1535, 0, 0, 3240, 440,0 }, + { 330, 331, 0, 0, 1920, 360,0 }, + {1536,1537, 0, 0, 3020, 0,0 }, + {1538,1539, 0, 0, 1660, 846,0 }, + {1541, 339, 0, 0, 9213, 813,0 }, + {1542,1543, 0, 0, 993, 100,0 }, + {1544,3031, 0, 0, 860, 180,0 }, + {1546,3032, 0, 0, 40000, 80,0 }, + { 338, 339, 0, 0, 40000, 200,0 }, + { 340, 341, 0, 0, 40000, 0,0 }, + {1441,1442, 0, 0, 7393, 186,0 }, + { 207, 208, 0, 0, 14373, 126,0 }, + {1466,1467, 0, 0, 40000, 66,0 }, + {1468,1469, 0, 0, 40000, 46,0 }, + { 179, 180, 0, 0, 4080, 346,0 }, + {1449,1450, 0, 0, 8313, 3373,0 }, + { 35,1470, 0, 0, 40000, 0,0 }, + { 36,1471, 0, 0, 8093, 40,0 }, + { 235, 236, 0, 0, 2393, 333,0 }, + {1483,1484, 0, 0, 40000, 0,0 }, + { 55,1487, 0, 0, 40000, 80,0 }, + {1488,1489, 0, 0, 40000, 80,0 }, + {1492,1493, 0, 0, 40000, 66,0 }, + { 256, 257, 0, 0, 40000, 53,0 }, + { 260, 261, 0, 0, 40000, 86,0 }, + {1512,1513, 0, 0, 40000, 40,0 }, + {1510,1511, 0, 0, 40000, 80,0 }, + {1496,1498, 0, 0, 40000, 73,0 }, + { 295, 296, 0, 0, 40000, 340,0 }, + {1540, 339, 0, 0, 2466, 633,0 }, + { 398, 399, 35, 0, 1860, 226,0 }, + {1516,1517, 0, 0, 40000, 660,0 }, + {1550,3033, 35, 0, 213, 26,0 }, + {1556,1557, 35, 0, 1200, 426,0 }, + {1558,1559, 35, 0, 1173, 406,0 }, + {1570,1571, 35, 0, 1160, 140,0 }, + {1608,1609, 35, 0, 2100, 320,0 }, + {1595,1596, 35, 0, 220, 273,0 }, + { 159,1597, 35, 0, 220, 266,0 }, + {1610,1611, 35, 0, 220, 273,0 }, + { 397,1588, 35, 0, 253, 86,0 }, + {1606,1607, 35, 0, 133, 66,0 }, + { 145,1576, 35, 0, 180, 0,0 }, + {1612,1613, 35, 0, 526, 400,0 }, + {1577,1578, 35, 0, 506, 453,0 }, + {1614,1615, 35, 0, 773, 933,0 }, + { 305, 306, 0, 0, 40000, 1160,0 }, + {1522,1523, 0, 0, 40000, 0,0 }, + {1550,1551, 35, 0, 526, 146,0 }, + { 364, 365, 35, 0, 186, 20,0 }, + { 129,1549, 35, 0, 326, 133,0 }, + { 132,1552, 35, 0, 226, 113,0 }, + {1553,1554, 35, 0, 206, 73,0 }, + { 129,1548, 35, 0, 326, 133,0 }, + { 134,1555, 35, 0, 2580, 893,0 }, + {1560,1561, 35, 0, 600, 366,0 }, + {1562,1563, 35, 0, 40000, 0,0 }, + {1572,1573, 35, 0, 340, 133,0 }, + {1574,1575, 35, 0, 406, 226,0 }, + {1581,1582, 35, 0, 640, 213,0 }, + { 149,1583, 35, 0, 326, 20,0 }, + {1584,1585, 35, 0, 633, 240,0 }, + {1591,1592, 35, 0, 1226, 413,0 }, + {1568,1569, 35, 0, 1313, 460,0 }, + {1579,1580, 35, 0, 326, 0,0 }, + {1586,1587, 35, 0, 606, 220,0 }, + {1589,1590, 35, 0, 120, 86,0 }, + {1600,1601, 35, 0, 1326, 420,0 }, + {1602,1603, 35, 0, 706, 266,0 }, + {1604,1605, 35, 0, 4540, 1326,0 }, + { 391, 392, 35, 0, 360, 153,0 }, + { 391, 393, 35, 0, 453, 153,0 }, + {1598,1599, 35, 0, 1246, 346,0 }, + { 367, 368, 35, 0, 600, 153,0 }, + { 380, 381, 35, 0, 40000, 0,0 }, + { 374, 375, 35, 0, 40000, 0,0 }, + {1566,1567, 35, 0, 40000, 0,0 }, + {2306,2307, 35, 0, 7660, 1560,0 }, + {3034, 339, 35, 0, 5860, 426,0 }, + {2301,2302, 35, 0, 2146, 753,0 }, + {2305,1548, 35, 0, 326, 133,0 }, + {1595,1595, 35, 0, 220, 273,0 }, + {2303,2304, 35, 0, 186, 20,0 }, + {1560,2308, 35, 0, 600, 373,0 }, + {2309,2310, 35, 0, 40000, 0,0 }, + {1568,1568, 35, 0, 1280, 453,0 }, + {2311,2312, 35, 0, 360, 106,0 }, + {2313,2314, 35, 0, 620, 0,0 }, + {2315,2315, 35, 0, 106, 0,0 }, + {2316,2316, 35, 0, 506, 453,0 }, + {1612,1612, 35, 0, 526, 400,0 }, + {2317,2317, 35, 0, 640, 253,0 }, + {2318,2318, 35, 0, 326, 20,0 }, + {2319,2319, 35, 0, 453, 446,0 }, + {2320,2320, 35, 0, 466, 453,0 }, + {2321,2321, 35, 0, 120, 26,0 }, + {2322,2322, 35, 0, 1220, 406,0 }, + {2323,2323, 35, 0, 2360, 786,0 }, + {2324, 392, 35, 0, 353, 146,0 }, + {2324, 393, 35, 0, 453, 146,0 }, + {2325,2326, 35, 0, 533, 140,0 }, + {2327,2328, 35, 0, 1273, 393,0 }, + {2329,2330, 35, 0, 40000, 0,0 }, + {2331,2332, 35, 0, 40000, 0,0 }, + {3035,3036, 35, 0, 4133, 433,0 }, + {3037,3038, 35, 0, 1740, 286,0 }, + {1564,1564, 35, 0, 713, 273,0 }, + {3039,3039, 0, 0, 40000, 0,0 }, + {3040,3040, 0, 0, 6100, 146,0 }, + {3041,3041, 0, 0, 2386, 26,0 }, + {3042,3042, 0, 0, 4320, 80,0 }, + {3043,3043, 0, 0, 3433, 313,0 }, + {3044,3044, 0, 0, 6620, 2446,0 }, + {3045,3045, 0, 0, 3726, 1253,0 }, + {3046,3046, 0, 0, 40000, 133,0 }, + {3047,3047, 0, 0, 4566, 1253,0 }, + {3048,3048, 0, 0, 40000, 813,0 }, + {3049,3049, 0, 0, 18513, 1560,0 }, + {3050,3050, 0, 0, 2186, 426,0 }, + {3051,3051, 0, 0, 1186, 420,0 }, + {3052,3052, 0, 0, 766, 420,0 }, + {3053,3053, 0, 0, 14513, 4713,0 }, + {3054,3054, 0, 0, 15493, 1580,0 }, + {3055,3055, 0, 0, 40000, 66,0 }, + {3056,3056, 0, 0, 40000, 60,0 }, + {3057,3057, 0, 0, 4740, 100,0 }, + {3058,3058, 0, 0, 40000, 66,0 }, + {3059,3059, 0, 0, 40000, 73,0 }, + {3060,3060, 0, 0, 40000, 73,0 }, + {3061,3061, 0, 0, 40000, 0,0 }, + {3062,3062, 0, 0, 8373, 633,0 }, + {3063,3063, 0, 0, 7560, 133,0 }, + {3064,3064, 0, 0, 40000, 0,0 }, + {3065,3065, 0, 0, 40000, 86,0 }, + {3066,3066, 0, 0, 340, 140,0 }, + {3067,3067, 0, 0, 40000, 0,0 }, + {3068,3068, 0, 0, 40000, 166,0 }, + {3069,3069, 0, 0, 4280, 1466,0 }, + {3070,3070, 0, 0, 2193, 73,0 }, + {3071,3071, 0, 0, 4846, 100,0 }, + {3072,3072, 0, 0, 12740, 93,0 }, + {3073,3073, 0, 0, 6953, 200,0 }, + {3074,3074, 0, 0, 13780, 73,0 }, + {3075,3075, 0, 0, 40000, 73,0 }, + {3076,3076, 0, 0, 5860, 600,0 }, + {3077,3077, 0, 0, 2206, 73,0 }, + {3078,3078, 0, 0, 40000, 140,0 }, + {3079,3079, 0, 0, 40000, 53,0 }, + {3080,3080, 0, 0, 40000, 120,0 }, + {3081,3081, 0, 0, 40000, 140,0 }, + {3082,3082, 0, 0, 40000, 126,0 }, + {3083,3083, 0, 0, 360, 140,0 }, + {3084,3084, 0, 0, 8880, 1373,0 }, + {3085,3085, 0, 0, 593, 73,0 }, + {3086,3086, 0, 0, 40000, 193,0 }, + {3087,3087, 0, 0, 40000, 200,0 }, + {3088,3088, 0, 0, 40000, 160,0 }, + {3089,3089, 0, 0, 40000, 200,0 }, + {3090,3090, 0, 0, 40000, 53,0 }, + {3091,3091, 0, 0, 40000, 73,0 }, + {3092,3092, 0, 0, 40000, 73,0 }, + {3093,3093, 0, 0, 760, 213,0 }, + {3094,3094, 0, 0, 40000, 133,0 }, + {3095,3095, 0, 0, 40000, 220,0 }, + {3096,3096, 0, 0, 40000, 100,0 }, + {3097,3097, 0, 0, 40000, 73,0 }, + {3098,3098, 0, 0, 40000, 140,0 }, + {3099,3099, 0, 0, 40000, 140,0 }, + {3100,3100, 0, 0, 40000, 140,0 }, + {3101,3101, 0, 0, 40000, 73,0 }, + {3102,3102, 0, 0, 40000, 73,0 }, + {3103,3103, 0, 0, 40000, 73,0 }, + {3104,3104, 0, 0, 40000, 73,0 }, + {3105,3105, 0, 0, 40000, 66,0 }, + {3106,3106, 0, 0, 40000, 66,0 }, + {3107,3107, 0, 0, 40000, 73,0 }, + {3108,3108, 0, 0, 40000, 73,0 }, + {3109,3109, 0, 0, 40000, 73,0 }, + {3110,3110, 0, 0, 40000, 73,0 }, + {3111,3111, 0, 0, 40000, 86,0 }, + {3112,3112, 0, 0, 5393, 100,0 }, + {3113,3113, 0, 0, 40000, 60,0 }, + {3114,3114, 0, 0, 18500, 73,0 }, + {3115,3115, 0, 0, 40000, 93,0 }, + {3116,3116, 0, 0, 40000, 86,0 }, + {3117,3117, 0, 0, 40000, 173,0 }, + {3118,3118, 0, 0, 40000, 1353,0 }, + {3119,3119, 0, 0, 17506, 73,0 }, + {3120,3120, 0, 0, 40000, 100,0 }, + {3121,3121, 0, 0, 40000, 73,0 }, + {3122,3122, 0, 0, 5620, 193,0 }, + {3123,3123, 0, 0, 3700, 80,0 }, + {3124,3124, 0, 0, 40000, 66,0 }, + {3125,3125, 0, 0, 2740, 80,0 }, + {3126,3126, 0, 0, 8333, 173,0 }, + {3127,3127, 0, 0, 2226, 466,0 }, + {3128,3128, 0, 0, 340, 146,0 }, + {3129,3129, 0, 0, 19980, 6280,0 }, + {3130,3130, 0, 0, 353, 73,0 }, + {3131,3131, 35, 0, 566, 233,0 }, + {3132,3132, 35, 0, 226, 46,0 }, + {3133,3133, 35, 0, 40000, 100,0 }, + {3134,3134, 35, 0, 40000, 100,0 }, + {3135,3135, 35, 0, 360, 146,0 }, + {3061,3061, 35, 0, 40000, 0,0 }, + {3136,3136, 35, 0, 366, 20,0 }, + { 739, 739, 35, 0, 246, 20,0 }, + {3137,3137, 35, 0, 333, 33,0 }, + {3138,3138, 35, 0, 420, 166,0 }, + {3139,3139, 35, 0, 626, 240,0 }, + {3140,3140, 35, 0, 233, 100,0 }, + {3141,3141, 35, 0, 1166, 440,0 }, + {3142,3142, 35, 0, 166, 66,0 }, + {3143,3143, 35, 0, 1166, 440,0 }, + {3144,3144, 35, 0, 813, 100,0 }, + {3145,3145, 35, 0, 1040, 440,0 }, + {3146,3146, 35, 0, 40000, 0,0 }, + {3147,3147, 35, 0, 40000, 0,0 }, + {3148,3148, 35, 0, 180, 40,0 }, + {3149,3149, 35, 0, 40000, 0,0 }, + {3150,3150, 0, 0, 40000, 0,0 }, + {3151,3151, 0, 0, 4900, 240,0 }, + {3152,3152, 0, 0, 3480, 80,0 }, + {3153,3153, 0, 0, 3586, 86,0 }, + {3154,3154, 0, 0, 4626, 633,0 }, + {3155,3155, 0, 0, 4293, 2286,0 }, + {3156,3156, 0, 0, 13653, 0,0 }, + {3157,3157, 0, 0, 1206, 426,0 }, + {3158,3158, 0, 0, 653, 426,0 }, + {3159,3159, 0, 0, 40000, 0,0 }, + {3160,3160, 0, 0, 4633, 633,0 }, + {3161,3161, 0, 0, 40000, 73,0 }, + {3162,3162, 0, 0, 40000, 60,0 }, + {3163,3163, 0, 0, 40000, 146,0 }, + {3164,3164, 0, 0, 40000, 73,0 }, + {3165,3165, 0, 0, 40000, 73,0 }, + {3166,3166, 0, 0, 40000, 0,0 }, + {3167,3167, 0, 0, 40000, 66,0 }, + {3168,3168, 0, 0, 3680, 1180,0 }, + {3169,3169, 0, 0, 2406, 846,0 }, + {3170,3170, 0, 0, 1560, 73,0 }, + {3171,3171, 0, 0, 1946, 226,0 }, + {3172,3172, 0, 0, 4333, 13,0 }, + {3173,3173, 0, 0, 40000, 0,0 }, + {3174,3174, 0, 0, 40000, 0,0 }, + {3175,3175, 0, 0, 40000, 66,0 }, + {3176,3176, 0, 0, 40000, 180,0 }, + {3177,3177, 0, 0, 15380, 80,0 }, + {3178,3178, 0, 0, 18213, 73,0 }, + {3179,3179, 0, 0, 1706, 0,0 }, + {3180,3180, 0, 0, 5733, 1266,0 }, + {3181,3181, 0, 0, 40000, 0,0 }, + {3182,3182, 0, 0, 40000, 366,0 }, + {3183,3183, 0, 0, 40000, 66,0 }, + {3184,3184, 0, 0, 4786, 73,0 }, + {3185,3185, 0, 0, 5660, 720,0 }, + {3186,3186, 0, 0, 1293, 406,0 }, + {3187,3187, 0, 0, 40000, 0,0 }, + {3188,3188, 0, 0, 2686, 233,0 }, + {3189,3189, 0, 0, 40000, 0,0 }, + {3190,3190, 0, 0, 40000, 73,0 }, + {3191,3191, 0, 0, 40000, 0,0 }, + {3192,3192, 0, 0, 40000, 73,0 }, + {3193,3193, 0, 0, 40000, 0,0 }, + {3194,3194, 0, 0, 3920, 73,0 }, + {3195,3195, 0, 0, 40000, 73,0 }, + {3196,3196, 0, 0, 40000, 66,0 }, + {3197,3197, 0, 0, 40000, 80,0 }, + {3198,3198, 0, 0, 40000, 86,0 }, + {3199,3199, 0, 0, 40000, 60,0 }, + {3200,3200, 0, 0, 40000, 0,0 }, + {3201,3201, 0, 0, 40000, 353,0 }, + {3202,3202, 0, 0, 3920, 73,0 }, + {3203,3203, 0, 0, 5833, 813,0 }, + {3204,3204, 0, 0, 40000, 60,0 }, + {3205,3205, 0, 0, 40000, 73,0 }, + {3206,3206, 0, 0, 1400, 406,0 }, + {3207,3207, 0, 0, 40000, 66,0 }, + {3208,3208, 0, 0, 9066, 2220,0 }, + {3209,3209, 0, 0, 1473, 773,0 }, + {3210,3210, 0, 0, 40000, 120,0 }, + {3211,3211, 0, 0, 40000, 306,0 }, + {3212,3212, 0, 0, 9306, 3013,0 }, + {3213,3213, 0, 0, 40000, 60,0 }, + {3214,3214, 0, 0, 40000, 73,0 }, + {3215,3215, 0, 0, 40000, 73,0 }, + {3216,3216, 0, 0, 40000, 453,0 }, + {3217,3217, 0, 0, 40000, 3460,0 }, + {3218,3218, 0, 0, 40000, 453,0 }, + {3219,3219, 0, 0, 40000, 40,0 }, + {3220,3220, 0, 0, 40000, 3926,0 }, + {3221,3221, 0, 0, 40000, 4506,0 }, + {3222,3222, 0, 0, 4646, 646,0 }, + {3223,3223, 0, 0, 773, 100,0 }, + {3224,3224, 0, 0, 40000, 73,0 }, + {3225,3225, 0, 0, 40000, 173,0 }, + {3226,3226, 0, 0, 1606, 653,0 }, + {3227,3227, 0, 0, 2353, 806,0 }, + {3228,3228, 0, 0, 980, 360,0 }, + {3229,3229, 0, 0, 1193, 413,0 }, + { 499, 499, 0, 0, 266, 0,0 }, + {3230,3230, 0, 0, 973, 360,0 }, + {3231,3231, 0, 0, 273, 53,0 }, + {3232,3232, 0, 0, 726, 220,0 }, + {3233,3233, 0, 0, 19933, 6093,0 }, + {3234,3234, 0, 0, 40000, 0,0 }, + { 403, 403, 0, 0, 40000, 73,0 }, + {3235,3235, 0, 0, 4966, 233,0 }, + {3236,3236, 0, 0, 4946, 240,0 }, + {3237,3237, 0, 0, 4946, 233,0 }, + {3238,3238, 0, 0, 4640, 1613,0 }, + {3239,3239, 0, 0, 2360, 806,0 }, + {3240,3240, 0, 0, 4466, 200,0 }, + {3241,3241, 0, 0, 40000, 73,0 }, + {3242,3242, 0, 0, 40000, 73,0 }, + {3243,3243, 0, 0, 40000, 73,0 }, + {3244,3244, 0, 0, 40000, 73,0 }, + {3245,3245, 0, 0, 40000, 240,0 }, + {3246,3246, 0, 0, 40000, 226,0 }, + {3247,3247, 0, 0, 40000, 233,0 }, + {3248,3248, 0, 0, 40000, 240,0 }, + {3249,3249, 0, 0, 4306, 1253,0 }, + {3250,3250, 0, 0, 3873, 1206,0 }, + {3251,3251, 0, 0, 4640, 633,0 }, + {3252,3252, 0, 0, 1233, 80,0 }, + {3253,3253, 0, 0, 1233, 26,0 }, + {3254,3254, 0, 0, 1233, 26,0 }, + {3255,3255, 0, 0, 4573, 1253,0 }, + {3256,3256, 0, 0, 3793, 1240,0 }, + {3257,3257, 0, 0, 40000, 73,0 }, + {3258,3258, 0, 0, 40000, 73,0 }, + {3259,3259, 0, 0, 40000, 140,0 }, + {3260,3260, 0, 0, 40000, 146,0 }, + {3261,3261, 0, 0, 40000, 80,0 }, + {3262,3262, 0, 0, 5953, 200,0 }, + {3263,3263, 0, 0, 5926, 200,0 }, + {3264,3264, 0, 0, 5866, 26,0 }, + {3265,3265, 0, 0, 18573, 6153,0 }, + {3266,3266, 0, 0, 40000, 2093,0 }, + {3267,3267, 0, 0, 40000, 73,0 }, + {3268,3268, 0, 0, 18626, 1553,0 }, + {3269,3269, 0, 0, 40000, 1820,0 }, + {3270,3270, 0, 0, 40000, 500,0 }, + {3271,3271, 0, 0, 18206, 5900,0 }, + {3272,3272, 0, 0, 14200, 93,0 }, + {3273,3273, 0, 0, 40000, 2873,0 }, + {3274,3274, 0, 0, 14960, 4913,0 }, + {3275,3275, 0, 0, 40000, 86,0 }, + {3276,3276, 0, 0, 40000, 826,0 }, + {3277,3277, 0, 0, 40000, 200,0 }, + {3278,3278, 0, 0, 40000, 340,0 }, + {3279,3279, 0, 0, 13220, 2500,0 }, + {3280,3280, 0, 0, 40000, 100,0 }, + {3281,3281, 0, 0, 40000, 1026,0 }, + {3282,3282, 0, 0, 40000, 366,0 }, + {3283,3283, 0, 0, 40000, 386,0 }, + {3284,3284, 0, 0, 40000, 0,0 }, + {3285,3285, 0, 0, 40000, 0,0 }, + {3286,3286, 0, 0, 40000, 140,0 }, + {3287,3287, 0, 0, 40000, 53,0 }, + {3288,3288, 0, 0, 40000, 120,0 }, + {3289,3289, 0, 0, 8866, 1366,0 }, + {3290,3290, 0, 0, 4193, 1400,0 }, + {3291,3291, 0, 0, 8353, 673,0 }, + {3292,3292, 0, 0, 8353, 673,0 }, + {3293,3293, 0, 0, 8400, 593,0 }, + {3294,3294, 0, 0, 8440, 666,0 }, + {3295,3295, 0, 0, 9600, 1580,0 }, + {3296,3296, 0, 0, 40000, 46,0 }, + {3297,3297, 0, 0, 40000, 0,0 }, + {3298,3298, 0, 0, 1653, 93,0 }, + {3299,3299, 0, 0, 2706, 73,0 }, + {3300,3300, 0, 0, 11680, 26,0 }, + {3301,3301, 0, 0, 6500, 340,0 }, + {3302,3302, 0, 0, 40000, 0,0 }, + {3303,3303, 0, 0, 40000, 0,0 }, + {3304,3304, 0, 0, 40000, 73,0 }, + {3305,3305, 0, 0, 40000, 73,0 }, + {3306,3306, 0, 0, 40000, 73,0 }, + {3307,3307, 0, 0, 40000, 73,0 }, + {3308,3308, 0, 0, 40000, 73,0 }, + {3309,3309, 0, 0, 40000, 73,0 }, + {3310,3310, 0, 0, 40000, 73,0 }, + {3311,3311, 0, 0, 40000, 73,0 }, + {3312,3312, 0, 0, 40000, 73,0 }, + {3313,3313, 0, 0, 40000, 133,0 }, + {3314,3314, 0, 0, 40000, 126,0 }, + {3315,3315, 0, 0, 40000, 73,0 }, + {3316,3316, 0, 0, 40000, 73,0 }, + {3317,3317, 0, 0, 40000, 73,0 }, + {3318,3318, 0, 0, 40000, 200,0 }, + {3319,3319, 0, 0, 40000, 133,0 }, + {3320,3320, 0, 0, 40000, 0,0 }, + {3321,3321, 0, 0, 40000, 240,0 }, + {3322,3322, 0, 0, 40000, 220,0 }, + {3323,3323, 0, 0, 40000, 226,0 }, + {3324,3324, 0, 0, 40000, 100,0 }, + {3325,3325, 0, 0, 40000, 140,0 }, + {3326,3326, 0, 0, 40000, 0,0 }, + {3327,3327, 0, 0, 40000, 426,0 }, + {3328,3328, 0, 0, 40000, 426,0 }, + {3329,3329, 0, 0, 3680, 1220,0 }, + {3330,3330, 0, 0, 40000, 533,0 }, + {3331,3331, 0, 0, 40000, 813,0 }, + {3332,3332, 0, 0, 14506, 4706,0 }, + {3333,3333, 0, 0, 766, 420,0 }, + {3334,3334, 0, 0, 40000, 1566,0 }, + {3335,3335, 0, 0, 40000, 120,0 }, + {3336,3336, 0, 0, 40000, 2380,0 }, + {3337,3337, 0, 0, 5666, 300,0 }, + {3338,3338, 0, 0, 40000, 73,0 }, + {3339,3339, 0, 0, 40000, 2513,0 }, + {3340,3340, 0, 0, 1260, 826,0 }, + {3341,3341, 0, 0, 2420, 413,0 }, + {3342,3342, 0, 0, 626, 240,0 }, + {3343,3343, 0, 0, 273, 60,0 }, + {3344,3344, 0, 0, 540, 20,0 }, + {3345,3345, 0, 0, 540, 20,0 }, + {3346,3346, 0, 0, 540, 20,0 }, + {3347,3347, 0, 0, 1153, 760,0 }, + {3348,3348, 0, 0, 40000, 100,0 }, + {3349,3349, 0, 0, 7326, 2380,0 }, + {3350,3350, 0, 0, 40000, 4426,0 }, + {3351,3351, 0, 0, 7413, 2493,0 }, + {3352,3352, 0, 0, 253, 20,0 }, + {3353,3353, 0, 0, 246, 33,0 }, + {3354,3354, 0, 0, 286, 13,0 }, + {3355,3355, 0, 0, 953, 13,0 }, + {3356,3356, 0, 0, 293, 20,0 }, + { 142, 142, 20, 0, 1893, 620,0 }, + {3357,1451, 0, 4, 2373, 780,0 }, + {3358,3359, 0, 4, 9260, 246,0 }, + {3360,1455, 0, 4, 40000, 0,0 }, + {3361,1463, 0, 4, 40000, 266,0 }, + { 225,3362, 0, 4, 7993, 100,0 }, + {3363,1545, 0, 4, 293, 86,0 }, + {3364,1547, 0, 4, 40000, 180,0 }, + {3365,3366, 39, 4, 66, 0,0 }, + {3367, 368, 58, 4, 173, 0,0 }, + {3368,1551, 48, 4, 520, 200,0 }, + {3368,3033, 49, 4, 53, 0,0 }, + {3368,3033, 51, 4, 53, 0,0 }, + {3368,3033, 54, 4, 60, 0,0 }, + {3368,3033, 57, 4, 60, 0,0 }, + {3368,3033, 60, 4, 60, 0,0 }, + {3369,3370, 70, 4, 840, 0,0 }, + {1564,1565, 80, 4, 220, 0,0 }, + {3371,1571, 44, 4, 420, 0,0 }, + {3372,3372, 0, 0, 8366, 666,0 }, + {3373,3373, 0, 0, 8366, 666,0 }, + {3374,3374, 0, 0, 3773, 73,0 }, + {3375,3375, 0, 0, 8366, 666,0 }, + {3376,3376, 0, 0, 4693, 26,0 }, + {3377,3377, 0, 0, 7400, 80,0 }, + {3378,3378, 0, 0, 3586, 80,0 }, + {3379,3379, 0, 0, 8366, 666,0 }, + {3380,3380, 0, 0, 3786, 1240,0 }, + {3381,3381, 0, 0, 9013, 1466,0 }, + {3382,3382, 0, 0, 1200, 73,0 }, + {3383,3383, 0, 0, 8146, 1446,0 }, + {3384,3384, 0, 0, 3660, 1206,0 }, + {3385,3385, 0, 0, 200, 100,0 }, + {3386,3386, 0, 0, 40000, 0,0 }, + {3387,3387, 0, 0, 1213, 426,0 }, + {3388,3388, 0, 0, 40000, 2573,0 }, + {3389,3389, 0, 0, 40000, 3446,0 }, + {3390,3390, 0, 0, 40000, 333,0 }, + {3391,3391, 0, 0, 40000, 73,0 }, + {3392,3392, 0, 0, 40000, 93,0 }, + {3393,3393, 0, 0, 40000, 73,0 }, + {3394,3394, 0, 0, 40000, 73,0 }, + {3395,3395, 0, 0, 40000, 73,0 }, + {3396,3396, 0, 0, 2193, 413,0 }, + {3397,3397, 0, 0, 14606, 2886,0 }, + {3398,3398, 0, 0, 10626, 4520,0 }, + {3399,3399, 0, 0, 2413, 100,0 }, + {3400,3400, 0, 0, 3593, 1140,0 }, + {3401,3401, 0, 0, 40000, 146,0 }, + {3402,3402, 0, 0, 40000, 86,0 }, + {3403,3403, 0, 0, 40000, 86,0 }, + {3404,3404, 0, 0, 9366, 106,0 }, + {3405,3405, 0, 0, 40000, 73,0 }, + {3406,3406, 0, 0, 40000, 0,0 }, + {3407,3407, 0, 0, 40000, 0,0 }, + {3408,3408, 0, 0, 1626, 400,0 }, + {3409,3409, 0, 0, 4473, 2933,0 }, + {3410,3410, 0, 0, 40000, 66,0 }, + {3411,3411, 0, 0, 40000, 0,0 }, + {3412,3412, 0, 0, 40000, 253,0 }, + {3413,3413, 0, 0, 40000, 233,0 }, + {3414,3414, 0, 0, 40000, 346,0 }, + {3415,3415, 0, 0, 1966, 26,0 }, + {3416,3416, 0, 0, 40000, 366,0 }, + {3417,3417, 0, 0, 2266, 386,0 }, + {3418,3418, 0, 0, 40000, 0,0 }, + {3419,3419, 0, 0, 2313, 766,0 }, + {3420,3420, 0, 0, 40000, 340,0 }, + {3421,3421, 0, 0, 40000, 346,0 }, + {3422,3422, 0, 0, 40000, 340,0 }, + {3423,3423, 0, 0, 40000, 353,0 }, + {3424,3424, 0, 0, 40000, 353,0 }, + {3425,3425, 0, 0, 40000, 226,0 }, + {3426,3426, 0, 0, 40000, 73,0 }, + {3427,3427, 0, 0, 940, 253,0 }, + {3428,3428, 0, 0, 40000, 73,0 }, + {3429,3429, 0, 0, 40000, 80,0 }, + {3430,3430, 0, 0, 40000, 240,0 }, + {3431,3431, 0, 0, 40000, 80,0 }, + {3432,3432, 0, 0, 40000, 73,0 }, + {3433,3433, 0, 0, 40000, 73,0 }, + {3434,3434, 0, 0, 40000, 73,0 }, + {3435,3435, 0, 0, 40000, 73,0 }, + {3436,3436, 0, 0, 40000, 73,0 }, + {3437,3437, 0, 0, 40000, 73,0 }, + {3438,3438, 0, 0, 40000, 73,0 }, + {3439,3439, 0, 0, 40000, 73,0 }, + {3440,3440, 0, 0, 40000, 73,0 }, + {3441,3441, 0, 0, 40000, 73,0 }, + {3442,3442, 0, 0, 40000, 66,0 }, + {3443,3443, 0, 0, 40000, 73,0 }, + {3444,3444, 0, 0, 40000, 80,0 }, + {3445,3445, 0, 0, 40000, 66,0 }, + {3446,3446, 0, 0, 40000, 66,0 }, + {3447,3447, 0, 0, 40000, 66,0 }, + {3448,3448, 0, 0, 40000, 66,0 }, + {3449,3449, 0, 0, 40000, 80,0 }, + {3450,3450, 0, 0, 40000, 353,0 }, + {3451,3451, 0, 0, 40000, 0,0 }, + {3452,3452, 0, 0, 18440, 100,0 }, + {3453,3453, 0, 0, 18086, 100,0 }, + {3454,3454, 0, 0, 266, 66,0 }, + {3455,3455, 0, 0, 40000, 80,0 }, + {3456,3456, 0, 0, 40000, 100,0 }, + {3457,3457, 0, 0, 40000, 80,0 }, + {3458,3458, 0, 0, 40000, 120,0 }, + {3459,3459, 0, 0, 40000, 93,0 }, + {3460,3460, 0, 0, 40000, 233,0 }, + {3461,3461, 0, 0, 40000, 0,0 }, + {3462,3462, 0, 0, 40000, 86,0 }, + {3463,3463, 0, 0, 40000, 820,0 }, + {3464,3464, 0, 0, 40000, 4986,0 }, + {3465,3465, 0, 0, 40000, 146,0 }, + {3466,3466, 0, 0, 40000, 100,0 }, + {3467,3467, 0, 0, 40000, 3346,0 }, + {3468,3468, 0, 0, 40000, 660,0 }, + {3469,3469, 0, 0, 40000, 366,0 }, + {3470,3470, 0, 0, 40000, 1480,0 }, + {3471,3471, 0, 0, 40000, 646,0 }, + {3472,3472, 0, 0, 40000, 2673,0 }, + {3473,3473, 0, 0, 40000, 2500,0 }, + {3474,3474, 0, 0, 40000, 2513,0 }, + {3475,3475, 0, 0, 40000, 66,0 }, + {3476,3476, 0, 0, 9600, 1580,0 }, + {3477,3477, 0, 0, 40000, 46,0 }, + {3478,3478, 0, 0, 10673, 100,0 }, + {3479,3479, 0, 0, 2333, 800,0 }, + {3480,3480, 0, 0, 3673, 1200,0 }, + {3481,3481, 0, 0, 40000, 73,0 }, + {3482,3482, 0, 0, 40000, 146,0 }, + {3483,3483, 0, 0, 40000, 73,0 }, + {3484,3484, 0, 0, 2266, 726,0 }, + {3485,3485, 0, 0, 333, 140,0 }, + {3486,3486, 0, 0, 2286, 746,0 }, + {3487,3487, 0, 0, 293, 126,0 }, + {3488,3488, 0, 0, 3700, 1213,0 }, + {3489,3489, 0, 0, 3773, 1186,0 }, + {3490,3490, 0, 0, 3646, 1200,0 }, + {3491,3491, 0, 0, 3020, 73,0 }, + {3492,3492, 0, 0, 786, 273,0 }, + {3493,3493, 0, 0, 40000, 146,0 }, + {3494,3494, 0, 0, 40000, 3093,0 }, + {3495,3495, 0, 0, 273, 60,0 }, + {3496,3496, 0, 0, 40000, 73,0 }, + {3497,3497, 0, 0, 40000, 73,0 }, + {3498,3498, 0, 0, 40000, 3093,0 }, + {3499,3499, 0, 0, 40000, 240,0 }, + {3500,3500, 0, 2, 6, 0,0 }, + { 739, 739, 46, 0, 220, 33,0 }, + {3501,3501, 47, 0, 973, 93,0 }, + {3502,3502, 64, 0, 126, 66,0 }, + {3503,3503, 40, 0, 340, 146,0 }, + {3504,3504, 48, 0, 100, 0,0 }, + {3505,3505, 48, 0, 286, 133,0 }, + {3506,3506, 46, 0, 466, 166,0 }, + {3507,3507,111, 0, 226, 113,0 }, + {3508,3508, 49, 0, 473, 166,0 }, + {3509,3509, 56, 0, 126, 40,0 }, + {3510,3510, 52, 0, 520, 206,0 }, + {3511,3511, 96, 0, 1346, 473,0 }, + {3510,3510, 54, 0, 513, 206,0 }, + {3512,3512, 57, 0, 973, 266,0 }, + {3513,3513, 82, 0, 1580, 553,0 }, + {3510,3510, 60, 0, 506, 200,0 }, + {3514,3514, 60, 0, 1886, 646,0 }, + {3515,3515, 92, 0, 1026, 520,0 }, + {3516,3516, 60, 0, 180, 93,0 }, + {3517,3517, 58, 0, 213, 213,0 }, + {3518,3518, 22, 0, 2300, 766,0 }, + {3519,3519, 60, 0, 1873, 653,0 }, + {3520,3520, 72, 0, 260, 93,0 }, + {3521,3521, 77, 0, 253, 93,0 }, + {3522,3522, 70, 0, 206, 93,0 }, + {3523,3523, 75, 0, 173, 93,0 }, + {3524,3524, 69, 0, 406, 113,0 }, + {3525,3525, 59, 0, 380, 160,0 }, + {3526,3526, 48, 0, 373, 40,0 }, + {3527,3527, 89, 0, 433, 180,0 }, + {3528,3528, 84, 0, 813, 180,0 }, + {3529,3529, 33, 0, 240, 53,0 }, + {3530,3530, 55, 0, 220, 86,0 }, + {3531,3531, 58, 0, 526, 200,0 }, + {3532,3532, 52, 0, 526, 193,0 }, + {3533,3533, 57, 0, 166, 80,0 }, + {3534,3534, 57, 0, 240, 100,0 }, + {3535,3535, 85, 0, 220, 113,0 }, + {3536,3536, 68, 0, 173, 93,0 }, + {3536,3536, 61, 0, 220, 113,0 }, + {3537,3537, 64, 0, 346, 53,0 }, + {3538,3538, 44, 0, 1080, 346,0 }, + {3539,3539,100, 0, 193, 20,0 }, + {3540,3540,100, 0, 793, 26,0 }, + {3541,3541, 0, 0, 14166, 320,0 }, + {3542,3542, 0, 0, 3873, 1613,0 }, + {3543,3543, 0, 0, 3586, 86,0 }, + {3544,3544, 0, 0, 7406, 2486,0 }, + {3545,3545, 0, 0, 4640, 1560,0 }, + {3546,3546, 0, 0, 446, 440,0 }, + {3547,3547, 0, 0, 9253, 3100,0 }, + {3548,3548, 0, 0, 4646, 646,0 }, + {3549,3549, 0, 0, 40000, 66,0 }, + {3550,3550, 0, 0, 40000, 73,0 }, + {3551,3551, 0, 0, 40000, 113,0 }, + {3552,3552, 0, 0, 40000, 73,0 }, + {3553,3553, 0, 0, 40000, 73,0 }, + {3554,3554, 0, 0, 40000, 0,0 }, + {3555,3555, 0, 0, 40000, 60,0 }, + {3556,3556, 0, 0, 3673, 1206,0 }, + {3557,3557, 0, 0, 3706, 1293,0 }, + {3558,3558, 0, 0, 5693, 1126,0 }, + {3559,3559, 0, 0, 2406, 846,0 }, + {3560,3560, 0, 0, 40000, 66,0 }, + {3561,3561, 0, 0, 40000, 73,0 }, + {3562,3562, 0, 0, 4333, 13,0 }, + {3563,3563, 0, 0, 3700, 66,0 }, + {3564,3564, 0, 0, 40000, 0,0 }, + {3565,3565, 0, 0, 3713, 1260,0 }, + {3566,3566, 0, 0, 1140, 126,0 }, + {3567,3567, 0, 0, 40000, 186,0 }, + {3568,3568, 0, 0, 40000, 0,0 }, + {3569,3569, 0, 0, 14400, 6,0 }, + {3570,3570, 0, 0, 14580, 66,0 }, + {3571,3571, 0, 0, 40000, 73,0 }, + {3572,3572, 0, 0, 40000, 353,0 }, + {3573,3573, 0, 0, 40000, 0,0 }, + {3574,3574, 0, 0, 40000, 173,0 }, + {3575,3575, 0, 0, 1833, 600,0 }, + {3576,3576, 0, 0, 40000, 0,0 }, + {3577,3577, 0, 0, 40000, 206,0 }, + {3578,3578, 0, 0, 40000, 46,0 }, + {3579,3579, 0, 0, 40000, 73,0 }, + {3580,3580, 0, 0, 9166, 2900,0 }, + {3581,3581, 0, 0, 5640, 680,0 }, + {3582,3582, 0, 0, 640, 220,0 }, + {3583,3583, 0, 0, 40000, 53,0 }, + {3584,3584, 0, 0, 40000, 26,0 }, + {3585,3585, 0, 0, 40000, 0,0 }, + {3586,3586, 0, 0, 40000, 66,0 }, + {3587,3587, 0, 0, 40000, 60,0 }, + {3588,3588, 0, 0, 40000, 0,0 }, + {3589,3589, 0, 0, 40000, 73,0 }, + {3590,3590, 0, 0, 40000, 0,0 }, + {3591,3591, 0, 0, 40000, 0,0 }, + {3592,3592, 0, 0, 3780, 73,0 }, + {3593,3593, 0, 0, 40000, 0,0 }, + {3594,3594, 0, 0, 3786, 73,0 }, + {3595,3595, 0, 0, 40000, 73,0 }, + {3596,3596, 0, 0, 40000, 66,0 }, + {3597,3597, 0, 0, 40000, 73,0 }, + {3598,3598, 0, 0, 40000, 53,0 }, + {3599,3599, 0, 0, 40000, 426,0 }, + {3600,3600, 0, 0, 40000, 133,0 }, + {3601,3601, 0, 0, 40000, 66,0 }, + {3602,3602, 0, 0, 40000, 433,0 }, + {3603,3603, 0, 0, 393, 126,0 }, + {3604,3604, 0, 0, 40000, 66,0 }, + {3605,3605, 0, 0, 40000, 353,0 }, + {3606,3606, 0, 0, 3813, 73,0 }, + {3607,3607, 0, 0, 5793, 780,0 }, + {3608,3608, 0, 0, 40000, 73,0 }, + {3609,3609, 0, 0, 40000, 86,0 }, + {3610,3610, 0, 0, 820, 206,0 }, + {3611,3611, 0, 0, 40000, 66,0 }, + {3612,3612, 0, 0, 40000, 200,0 }, + {3613,3613, 0, 0, 18186, 720,0 }, + {3614,3614, 0, 0, 40000, 0,0 }, + {3615,3615, 0, 0, 40000, 493,0 }, + {3616,3616, 0, 0, 40000, 306,0 }, + {3617,3617, 0, 0, 2166, 600,0 }, + {3618,3618, 0, 0, 40000, 73,0 }, + {3619,3619, 0, 0, 40000, 3073,0 }, + {3620,3620, 0, 0, 2333, 413,0 }, + {3621,3621, 0, 0, 14880, 73,0 }, + {3622,3622, 0, 0, 40000, 66,0 }, + {3623,3623, 0, 0, 40000, 73,0 }, + {3624,3624, 0, 0, 40000, 1873,0 }, + {3625,3625, 0, 0, 40000, 446,0 }, + {3626,3626, 0, 0, 40000, 3126,0 }, + {3627,3627, 0, 0, 18446, 6140,0 }, + {3628,3628, 0, 0, 1113, 240,0 }, + {3629,3629, 0, 0, 40000, 3600,0 }, + {3630,3630, 0, 0, 40000, 4726,0 }, + {3631,3631, 0, 0, 40000, 0,0 }, + {3632,3632, 0, 0, 2893, 606,0 }, + {3633,3633, 0, 0, 40000, 0,0 }, + {3634,3634, 0, 0, 40000, 0,0 }, + {3635,3635, 0, 0, 40000, 173,0 }, + {3636,3636, 0, 0, 40000, 60,0 }, + {3637,3637, 0, 0, 40000, 0,0 }, + {3638,3638, 0, 0, 986, 326,0 }, + {3639,3639, 0, 0, 1873, 646,0 }, + {3640,3640, 0, 0, 200, 260,0 }, + {3641,3641, 0, 0, 1180, 393,0 }, + {3642,3642, 0, 0, 266, 0,0 }, + {3643,3643, 0, 0, 313, 126,0 }, + {3644,3644, 0, 0, 406, 253,0 }, + {3645,3645, 0, 0, 1013, 813,0 }, + {3646,3646, 0, 0, 273, 53,0 }, + {3647,3647, 0, 0, 720, 213,0 }, + {3648,3648, 0, 0, 386, 120,0 }, + {3649,3649, 0, 0, 40000, 766,0 }, + {3650,3650, 0, 0, 40000, 66,0 }, + {3651,3651, 0, 0, 40000, 73,0 }, + {3652,3652, 0, 0, 1186, 426,0 }, + {3653,3653, 0, 0, 16720, 240,0 }, + {3654,3654, 0, 0, 8026, 246,0 }, + {3655,3655, 0, 0, 18186, 140,0 }, + {3656,3656, 0, 0, 14566, 200,0 }, + {3657,3657, 0, 0, 7973, 20,0 }, + {3658,3658, 0, 0, 4446, 86,0 }, + {3659,3659, 0, 0, 4473, 100,0 }, + {3660,3660, 0, 0, 8646, 153,0 }, + {3661,3661, 0, 0, 3726, 660,0 }, + {3662,3662, 0, 0, 1893, 653,0 }, + {3663,3663, 0, 0, 1933, 760,0 }, + {3664,3664, 0, 0, 9160, 240,0 }, + {3665,3665, 0, 0, 1133, 100,0 }, + {3666,3666, 0, 0, 633, 233,0 }, + {3667,3667, 0, 0, 9153, 3060,0 }, + {3668,3668, 0, 0, 2166, 406,0 }, + {3669,3669, 0, 0, 40000, 66,0 }, + {3670,3670, 0, 0, 40000, 73,0 }, + {3671,3671, 0, 0, 40000, 73,0 }, + {3672,3672, 0, 0, 40000, 73,0 }, + {3673,3673, 0, 0, 40000, 346,0 }, + {3674,3674, 0, 0, 40000, 353,0 }, + {3675,3675, 0, 0, 40000, 200,0 }, + {3676,3676, 0, 0, 40000, 320,0 }, + {3677,3677, 0, 0, 4646, 100,0 }, + {3678,3678, 0, 0, 4426, 133,0 }, + {3679,3679, 0, 0, 4633, 100,0 }, + {3680,3680, 0, 0, 2266, 133,0 }, + {3681,3681, 0, 0, 2346, 53,0 }, + {3682,3682, 0, 0, 40000, 66,0 }, + {3683,3683, 0, 0, 9686, 173,0 }, + {3684,3684, 0, 0, 14300, 66,0 }, + {3685,3685, 0, 0, 40000, 0,0 }, + {3686,3686, 0, 0, 40000, 0,0 }, + {3687,3687, 0, 0, 40000, 0,0 }, + {3688,3688, 0, 0, 8613, 73,0 }, + {3689,3689, 0, 0, 40000, 0,0 }, + {3690,3690, 0, 0, 40000, 0,0 }, + {3691,3691, 0, 0, 40000, 0,0 }, + {3692,3692, 0, 0, 40000, 0,0 }, + {3693,3693, 0, 0, 40000, 393,0 }, + {3694,3694, 0, 0, 40000, 126,0 }, + {3695,3695, 0, 0, 40000, 120,0 }, + {3696,3696, 0, 0, 40000, 0,0 }, + {3697,3697, 0, 0, 40000, 226,0 }, + {3698,3698, 0, 0, 7420, 1186,0 }, + {3699,3699, 0, 0, 3280, 1726,0 }, + {3700,3700, 0, 0, 3680, 1220,0 }, + {3701,3701, 0, 0, 40000, 480,0 }, + {3702,3702, 0, 0, 40000, 306,0 }, + {3703,3703, 0, 0, 40000, 433,0 }, + {3704,3704, 0, 0, 40000, 133,0 }, + {3705,3705, 0, 0, 40000, 0,0 }, + {3706,3706, 0, 0, 40000, 0,0 }, + {3707,3707, 0, 0, 1166, 380,0 }, + {3708,3708, 0, 0, 40000, 140,0 }, + {3709,3709, 0, 0, 40000, 126,0 }, + {3710,3710, 0, 0, 40000, 100,0 }, + {3711,3711, 0, 0, 40000, 66,0 }, + {3712,3712, 0, 0, 40000, 226,0 }, + {3713,3713, 0, 0, 40000, 133,0 }, + {3714,3714, 0, 0, 40000, 73,0 }, + {3715,3715, 0, 0, 40000, 226,0 }, + {3716,3716, 0, 0, 40000, 100,0 }, + {3717,3717, 0, 0, 40000, 80,0 }, + {3718,3718, 0, 0, 40000, 100,0 }, + {3719,3719, 0, 0, 40000, 73,0 }, + {3720,3720, 0, 0, 40000, 73,0 }, + {3721,3721, 0, 0, 40000, 73,0 }, + {3722,3722, 0, 0, 40000, 126,0 }, + {3723,3723, 0, 0, 40000, 80,0 }, + {3724,3724, 0, 0, 40000, 73,0 }, + {3725,3725, 0, 0, 40000, 86,0 }, + {3726,3726, 0, 0, 40000, 100,0 }, + {3727,3727, 0, 0, 40000, 0,0 }, + {3728,3728, 0, 0, 40000, 126,0 }, + {3729,3729, 0, 0, 40000, 133,0 }, + {3730,3730, 0, 0, 40000, 140,0 }, + {3731,3731, 0, 0, 40000, 73,0 }, + {3732,3732, 0, 0, 40000, 60,0 }, + {3733,3733, 0, 0, 40000, 93,0 }, + {3734,3734, 0, 0, 40000, 80,0 }, + {3735,3735, 0, 0, 40000, 66,0 }, + {3736,3736, 0, 0, 40000, 0,0 }, + {3737,3737, 0, 0, 40000, 220,0 }, + {3738,3738, 0, 0, 40000, 80,0 }, + {3739,3739, 0, 0, 40000, 400,0 }, + {3740,3740, 0, 0, 40000, 1373,0 }, + {3741,3741, 0, 0, 40000, 86,0 }, + {3742,3742, 0, 0, 40000, 1313,0 }, + {3743,3743, 0, 0, 40000, 0,0 }, + {3744,3744, 0, 0, 11486, 593,0 }, + {3745,3745, 0, 0, 40000, 1246,0 }, + {3746,3746, 0, 0, 40000, 140,0 }, + {3747,3747, 0, 0, 14386, 2680,0 }, + {3748,3748, 0, 0, 40000, 653,0 }, + {3749,3749, 0, 0, 2286, 713,0 }, + {3750,3750, 0, 0, 40000, 253,0 }, + {3751,3751, 0, 0, 6933, 406,0 }, + {3752,3752, 0, 0, 40000, 1313,0 }, + {3753,3753, 0, 0, 40000, 1440,0 }, + {3754,3754, 0, 0, 40000, 73,0 }, + {3755,3755, 0, 0, 11100, 420,0 }, + {3756,3756, 0, 0, 6493, 320,0 }, + {3757,3757, 0, 0, 3486, 126,0 }, + {3758,3758, 0, 0, 6620, 2133,0 }, + {3759,3759, 0, 0, 1180, 413,0 }, + {3760,3760, 0, 0, 40000, 73,0 }, + {3761,3761, 0, 0, 40000, 73,0 }, + {3762,3762, 0, 0, 40000, 66,0 }, + {3763,3763, 0, 0, 4580, 413,0 }, + {3764,3764, 0, 0, 340, 146,0 }, + {3765,3765, 0, 0, 1166, 400,0 }, + {3766,3766, 0, 0, 1346, 660,0 }, + {3767,3767, 0, 0, 1260, 393,0 }, + {3768,3768, 0, 0, 3646, 1186,0 }, + {3769,3769, 0, 0, 2713, 400,0 }, + {3770,3770, 0, 0, 1780, 73,0 }, + {3771,3771, 0, 0, 800, 213,0 }, + {3772,3772, 0, 0, 660, 173,0 }, + {3773,3773, 0, 0, 12146, 73,0 }, + {3774,3774, 0, 0, 273, 60,0 }, + {3775,3775, 0, 0, 40000, 73,0 }, + {3776,3776, 0, 0, 380, 53,0 }, + {3777,3777, 0, 0, 40000, 200,0 }, + {3778,3778, 0, 0, 586, 20,0 }, + {3779,3779, 0, 2, 6, 0,0 }, + { 738, 738, 44, 0, 840, 340,0 }, + {3780,3780, 36, 0, 7366, 140,0 }, + {3781,3781, 32, 0, 100, 0,0 }, + {2030,2030, 60, 0, 293, 126,0 }, + {3782,3782, 24, 0, 100, 0,0 }, + {3783,3783, 60, 0, 126, 73,0 }, + {3784,3784, 44, 0, 393, 93,0 }, + { 132, 132, 44, 0, 173, 100,0 }, + {3785,3785, 47, 0, 393, 93,0 }, + { 152, 152, 44, 0, 213, 86,0 }, + {3784,3784, 50, 0, 393, 93,0 }, + { 139, 139, 44, 0, 293, 100,0 }, + {3784,3784, 54, 0, 393, 93,0 }, + {3784,3784, 57, 0, 393, 93,0 }, + {3786,3786, 60, 0, 1900, 666,0 }, + {3784,3784, 60, 0, 393, 93,0 }, + {3787,3787, 60, 0, 1900, 666,0 }, + {3788,3788, 60, 0, 1886, 666,0 }, + {3789,3789, 60, 0, 1866, 653,0 }, + {3790,3790, 60, 0, 1873, 633,0 }, + {3791,3791, 44, 0, 946, 333,0 }, + {2037,2037, 44, 0, 213, 126,0 }, + { 144, 144, 44, 0, 213, 126,0 }, + {2038,2038, 44, 0, 106, 0,0 }, + {3792,3792, 44, 0, 380, 360,0 }, + {3793,3793, 44, 0, 520, 206,0 }, + {3794,3794, 45, 0, 273, 100,0 }, + {3795,3795, 33, 0, 326, 106,0 }, + {3796,3796, 56, 0, 506, 200,0 }, + {3796,3796, 51, 0, 506, 200,0 }, + {3797,3797, 44, 0, 126, 66,0 }, + {3798,3798, 44, 0, 553, 186,0 }, + {3534,3534, 56, 0, 240, 100,0 }, + { 158, 158, 68, 0, 126, 140,0 }, + {3799,3799, 51, 0, 513, 206,0 }, + {3800,3800, 46, 0, 506, 200,0 }, + {3801,3801, 44, 0, 513, 206,0 }, + {3802,3802, 44, 0, 3720, 1260,0 }, + { 152, 152, 45, 0, 220, 80,0 }, + {3803,3803, 0, 0, 40000, 86,0 }, + {3804,3804, 0, 0, 40000, 226,0 }, + {3805,3805, 0, 0, 40000, 73,0 }, + {3806,3806, 0, 0, 40000, 73,0 }, + {3807,3807, 0, 0, 40000, 93,0 }, + {3808,3808, 0, 0, 4653, 660,0 }, + {3809,3809, 0, 0, 6686, 2246,0 }, + {3810,3810, 0, 0, 1180, 413,0 }, + {3811,3811, 0, 0, 966, 293,0 }, + {3812,3812, 0, 0, 1780, 66,0 }, + {3780,3780, 45, 0, 5900, 113,0 }, + {3061,3061, 45, 0, 40000, 0,0 }, + {3813,3813, 60, 0, 126, 226,0 }, + {3781,3781, 60, 0, 93, 0,0 }, + {3814,3814, 44, 0, 393, 86,0 }, + {3815,3815, 57, 0, 166, 80,0 }, + {3816,3816, 56, 0, 240, 100,0 }, + {3817,3817, 60, 0, 113, 0,0 }, + {3818,3818, 60, 0, 113, 0,0 }, + {3517,3517, 45, 0, 213, 213,0 }, + {3819,3819, 0, 0, 4033, 100,0 }, + {3820,3820, 0, 0, 5200, 873,0 }, + {3821,3821, 0, 0, 40000, 0,0 }, + {3822,3822, 0, 0, 10493, 160,0 }, + {3823,3823, 0, 0, 40000, 740,0 }, + {3824,3825, 0, 1, 40000, 366,0.078125 }, + {3826,3826, 0, 0, 40000, 5100,0 }, + {3827,3827, 0, 0, 40000, 766,0 }, + {3828,1172, 0, 1, 40000, 780,0.15625 }, + {3829,3829, 0, 0, 40000, 60,0 }, + {3830,3830, 0, 0, 566, 133,0 }, + {3831,3831, 32, 0, 146, 0,0 }, + {3832,3832, 36, 0, 273, 0,0 }, + {3833,3833, 88, 0, 340, 120,0 }, + {3834,3834, 0, 0, 9006, 240,0 }, + {3835,3835, 0, 0, 9206, 246,0 }, + {3836,3836, 0, 0, 9246, 386,0 }, + {3837,3837, 0, 0, 9440, 220,0 }, + {3838,3838, 0, 0, 8900, 133,0 }, + {3839,3839, 0, 0, 9400, 253,0 }, + {3840,3840, 0, 0, 4613, 420,0 }, + {3841,3841, 0, 0, 9233, 426,0 }, + {3842,3842, 0, 0, 40000, 526,0 }, + {3843,3843, 0, 0, 40000, 640,0 }, + {3844,3844, 0, 0, 40000, 666,0 }, + {3845,3845, 0, 0, 40000, 1053,0 }, + {3846,3846, 0, 0, 40000, 173,0 }, + {3847,3847, 0, 0, 40000, 246,0 }, + {3848,3848, 0, 0, 40000, 226,0 }, + {3849,3849, 0, 0, 4073, 233,0 }, + {3850,3850, 0, 0, 14286, 326,0 }, + {3851,3851, 0, 0, 9233, 146,0 }, + {3852,3852, 0, 0, 4480, 133,0 }, + {3853,3853, 0, 0, 40000, 53,0 }, + {3854,3854, 0, 0, 40000, 126,0 }, + {3855,3855, 0, 0, 40000, 126,0 }, + {3856,3856, 0, 0, 18226, 146,0 }, + {3857,3857, 0, 0, 40000, 326,0 }, + {3858,3858, 0, 0, 40000, 0,0 }, + {3859,3859, 0, 0, 40000, 300,0 }, + {3860,3860, 0, 0, 40000, 0,0 }, + {3861,3861, 0, 0, 40000, 0,0 }, + {3862,3862, 0, 0, 40000, 0,0 }, + {3863,3863, 0, 0, 40000, 140,0 }, + {3864,3864, 0, 0, 40000, 153,0 }, + {3865,3865, 0, 0, 40000, 233,0 }, + {3866,3866, 0, 0, 40000, 186,0 }, + {3867,3867, 0, 0, 40000, 413,0 }, + {3868,3868, 0, 0, 40000, 373,0 }, + {3869,3869, 0, 0, 1246, 440,0 }, + {3870,3870, 0, 0, 4620, 1513,0 }, + {3871,3871, 0, 0, 40000, 433,0 }, + {3872,3872, 0, 0, 40000, 453,0 }, + {3873,3873, 0, 0, 40000, 1440,0 }, + {3874,3874, 0, 0, 40000, 480,0 }, + {3875,3875, 0, 0, 40000, 1360,0 }, + {3876,3876, 0, 0, 40000, 0,0 }, + {3877,3877, 0, 0, 40000, 353,0 }, + {3878,3878, 0, 0, 40000, 86,0 }, + {3879,3879, 0, 0, 40000, 126,0 }, + {3880,3880, 0, 0, 40000, 73,0 }, + {3881,3881, 0, 0, 40000, 80,0 }, + {3882,3882, 0, 0, 40000, 246,0 }, + {3883,3883, 0, 0, 40000, 93,0 }, + {3884,3884, 0, 0, 40000, 120,0 }, + {3885,3885, 0, 0, 40000, 180,0 }, + {3886,3886, 0, 0, 40000, 133,0 }, + {3887,3887, 0, 0, 40000, 133,0 }, + {3888,3888, 0, 0, 40000, 153,0 }, + {3889,3889, 0, 0, 40000, 93,0 }, + {3890,3890, 0, 0, 40000, 140,0 }, + {3891,3891, 0, 0, 40000, 100,0 }, + {3892,3892, 0, 0, 40000, 146,0 }, + {3893,3893, 0, 0, 40000, 126,0 }, + {3894,3894, 0, 0, 40000, 160,0 }, + {3895,3895, 0, 0, 40000, 226,0 }, + {3896,3896, 0, 0, 40000, 140,0 }, + {3897,3897, 0, 0, 40000, 200,0 }, + {3898,3898, 0, 0, 40000, 66,0 }, + {3899,3899, 0, 0, 40000, 446,0 }, + {3900,3900, 0, 0, 40000, 140,0 }, + {3901,3901, 0, 0, 40000, 400,0 }, + {3902,3902, 0, 0, 40000, 373,0 }, + {3903,3903, 0, 0, 40000, 1306,0 }, + {3904,3904, 0, 0, 40000, 186,0 }, + {3905,3905, 0, 0, 40000, 640,0 }, + {3906,3906, 0, 0, 40000, 346,0 }, + {3907,3907, 0, 0, 40000, 140,0 }, + {3908,3908, 0, 0, 40000, 253,0 }, + {3909,3909, 0, 0, 8980, 746,0 }, + {3910,3910, 0, 0, 40000, 1266,0 }, + {3911,3911, 0, 0, 40000, 1306,0 }, + {3912,3912, 0, 0, 7226, 593,0 }, + {3913,3913, 0, 0, 40000, 140,0 }, + {3914,3914, 0, 0, 40000, 220,0 }, + {3915,3915, 0, 0, 40000, 146,0 }, + {3916,3916, 0, 0, 4606, 1506,0 }, + {3917,3917, 0, 0, 40000, 80,0 }, + {3918,3918, 0, 0, 40000, 0,0 }, + {3919,3919, 0, 0, 40000, 0,0 }, + {3920,3920, 0, 0, 613, 226,0 }, + {3921,3921, 0, 0, 9073, 2946,0 }, + {3922,3922, 0, 0, 40000, 73,0 }, + {3923,3923, 0, 0, 3726, 200,0 }, + {3924,3924, 0, 0, 3680, 373,0 }, + {3925,3925, 0, 0, 7113, 186,0 }, + {3926,3926, 0, 0, 2406, 106,0 }, + {3927,3927, 0, 0, 40000, 0,0 }, + {3928,3928, 0, 0, 40000, 253,0 }, + {3929,3929, 0, 0, 40000, 0,0 }, + {3930,3930, 0, 0, 40000, 80,0 }, + {3931,3931, 0, 0, 40000, 86,0 }, + {3932,3932, 0, 0, 18186, 740,0 }, + {3933,3933, 0, 0, 18426, 813,0 }, + { 523, 523, 0, 0, 200, 260,0 }, + {3934,3934, 0, 0, 340, 146,0 }, + {3935,3935, 0, 0, 366, 260,0 }, + {3936,3936, 48, 0, 126, 0,0 }, + {3937,3937, 27, 0, 200, 106,0 }, + {3938,3938, 40, 0, 1073, 800,0 }, + {3939,3939, 48, 0, 100, 0,0 }, + {3938,3938, 45, 0, 933, 666,0 }, + {3940,3940, 48, 0, 140, 333,0 }, + {3938,3938, 47, 0, 933, 666,0 }, + {3941,3941, 48, 0, 1840, 0,0 }, + {3938,3938, 49, 0, 953, 686,0 }, + {3938,3938, 53, 0, 906, 686,0 }, + {3938,3938, 56, 0, 913, 693,0 }, + { 129, 129, 52, 0, 293, 126,0 }, + { 130, 130, 48, 0, 173, 93,0 }, + { 129, 129, 58, 0, 286, 126,0 }, + { 132, 132, 47, 0, 173, 100,0 }, + { 492, 492, 43, 0, 820, 306,0 }, + { 132, 132, 49, 0, 173, 93,0 }, + { 132, 132, 51, 0, 173, 93,0 }, + { 132, 132, 54, 0, 173, 93,0 }, + { 132, 132, 57, 0, 146, 86,0 }, + { 492, 492, 72, 0, 706, 300,0 }, + { 137, 137, 76, 0, 1900, 666,0 }, + { 138, 138, 84, 0, 740, 300,0 }, + { 139, 139, 36, 0, 353, 146,0 }, + { 140, 140, 76, 0, 1886, 680,0 }, + { 141, 141, 84, 0, 220, 113,0 }, + { 135, 135, 83, 0, 1353, 480,0 }, + { 142, 142, 84, 0, 386, 160,0 }, + {3942,3942, 24, 0, 2313, 780,0 }, + { 137, 137, 77, 0, 1893, 660,0 }, + { 144, 144, 60, 0, 213, 0,0 }, + { 145, 145, 65, 0, 180, 146,0 }, + { 146, 146, 59, 0, 173, 93,0 }, + { 147, 147, 51, 0, 266, 213,0 }, + { 148, 148, 45, 0, 513, 200,0 }, + { 149, 149, 71, 0, 246, 26,0 }, + { 150, 150, 60, 0, 500, 193,0 }, + { 151, 151, 58, 0, 513, 200,0 }, + { 152, 152, 53, 0, 220, 86,0 }, + { 153, 153, 64, 0, 113, 40,0 }, + { 154, 154, 71, 0, 840, 300,0 }, + { 156, 156, 61, 0, 166, 80,0 }, + { 158, 158, 48, 0, 173, 213,0 }, + { 159, 159, 69, 0, 126, 140,0 }, + { 160, 160, 68, 0, 126, 140,0 }, + { 161, 161, 63, 0, 326, 113,0 }, + { 162, 162, 74, 0, 860, 286,0 }, + { 163, 163, 60, 0, 386, 160,0 }, + { 164, 164, 80, 0, 1106, 273,0 }, + { 165, 165, 64, 0, 126, 66,0 }, + { 166, 166, 69, 0, 386, 80,0 }, + { 167, 167, 73, 0, 546, 306,0 }, + { 168, 168, 75, 0, 126, 140,0 }, + { 169, 169, 68, 0, 340, 320,0 }, + { 131, 131, 48, 0, 520, 200,0 }, + {3061,3061, 53, 0, 40000, 0,0 }, + {3943,3944, 0, 4, 2153, 0,0 }, + {3945,3946, 0, 4, 9060, 393,0 }, + { 174,3947, 0, 4, 6953, 0,0 }, + {3948,3949, 0, 4, 9386, 140,0 }, + { 9,3950, 0, 4, 1626, 426,0 }, + {3951,3952, 0, 4, 18466, 240,0 }, + {3953,3954, 0, 4, 4666, 1486,0 }, + { 15,3955, 0, 4, 5700, 1986,0 }, + {3956,3957, 0, 4, 40000, 100,0 }, + {3958,3959, 0, 4, 40000, 73,0 }, + {3960,3961, 0, 4, 40000, 73,0 }, + {3962,3963, 0, 4, 40000, 73,0 }, + {3964,3965, 0, 4, 18280, 153,0 }, + {3966,3965, 0, 4, 18686, 160,0 }, + { 31,3967, 0, 4, 40000, 0,0 }, + {3968,3969, 0, 4, 17966, 100,0 }, + {3970,3971, 0, 4, 40000, 66,0 }, + {3972,3971, 0, 4, 40000, 66,0 }, + {3973,3974, 0, 4, 40000, 46,0 }, + {3975,3976, 0, 4, 18693, 106,0 }, + {3977,3976, 0, 4, 18593, 106,0 }, + {3978,3979, 0, 4, 9366, 106,0 }, + {3980,3981, 0, 4, 9120, 226,0 }, + {3982,3983, 0, 4, 40000, 140,0 }, + {3984,3985, 0, 4, 40000, 800,0 }, + { 54,3986, 0, 4, 2520, 706,0 }, + {3987,3988, 0, 4, 40000, 86,0 }, + {3989,3990, 0, 4, 40000, 233,0 }, + {3991, 253, 0, 4, 40000, 66,0 }, + {3992,3992, 0, 0, 40000, 0,0 }, + {3993,3993, 0, 0, 40000, 120,0 }, + {3994,3995, 0, 4, 40000, 80,0 }, + {3996,3997, 0, 4, 40000, 73,0 }, + {3998,3999, 0, 4, 40000, 86,0 }, + {1503,4000, 0, 4, 40000, 93,0 }, + { 88,4001, 0, 4, 40000, 1220,0 }, + {3743,4002, 0, 4, 7706, 1260,0 }, + { 92,4003, 0, 4, 40000, 186,0 }, + { 93,4004, 0, 4, 40000, 813,0 }, + { 94,4005, 0, 4, 7720, 1260,0 }, + { 96,4006, 0, 4, 40000, 2460,0 }, + { 103,4007, 0, 4, 3720, 1240,0 }, + { 104,4008, 0, 4, 3020, 1226,0 }, + { 105,4009, 0, 4, 6086, 2453,0 }, + { 107,4010, 0, 4, 2100, 760,0 }, + { 108,4011, 0, 4, 40000, 73,0 }, + { 110,4012, 0, 4, 40000, 100,0 }, + { 111,4013, 0, 4, 2366, 820,0 }, + {4014,4015, 0, 4, 1026, 326,0 }, + { 115,4016, 0, 4, 1853, 0,0 }, + { 118,4017, 0, 4, 1553, 53,0 }, + { 119,4018, 0, 4, 593, 0,0 }, + { 120,4019, 0, 4, 2293, 1173,0 }, + { 121,4020, 0, 4, 10673, 3000,0 }, + { 123,4021, 0, 4, 7413, 2486,0 }, + { 124,4022, 0, 4, 40000, 1126,0 }, + { 125,4023, 0, 4, 40000, 1546,0 }, + {4024,4024, 35, 0, 706, 266,0 }, + {4025,4026, 38, 1, 273, 106,0 }, + {4027,4028, 38, 1, 366, 133,0 }, + {4029,4030, 48, 1, 280, 133,-1.90625 }, + {4031,4031, 51, 0, 113, 80,0 }, + {4032,4033, 48, 1, 953, 346,-1.90625 }, + {4034,4034, 61, 1, 3200, 540,0.09375 }, + {3369,1557, 70, 4, 833, 0,0 }, + {4035,4036, 79, 1, 1306, 513,0.078125 }, + {4037,4037, 62, 0, 5200, 466,0 }, + {4038,4039, 67, 1, 2153, 1080,0.078125 }, + {4040,4040, 62, 1, 3226, 573,0.09375 }, + {4041,4042, 54, 1, 286, 133,0 }, + {4041,4043, 48, 1, 286, 126,0 }, + { 389, 389, 42, 0, 266, 73,0 }, + {4044,4045, 48, 1, 280, 126,0 }, + {4046,4047, 48, 1, 380, 60,0 }, + {4048,4048, 16, 0, 180, 20,0 }, + {4049,4049, 16, 0, 740, 20,0 }, + {4050,4051, 64, 4, 1366, 0,0 }, + { 844, 844,244, 2, 6, 0,0 }, + { 855, 855,244, 2, 6, 0,0 }, + { 880, 880,232, 0, 253, 80,0 }, + { 882, 882,220, 0, 40000, 266,0 }, + { 887, 887, 35, 0, 133, 46,0 }, + { 884, 884, 35, 0, 233, 80,0 }, + { 885, 885, 35, 0, 226, 86,0 }, + { 886, 886, 35, 0, 113, 0,0 }, + { 361, 361, 35, 0, 286, 73,0 }, + { 767, 767, 35, 0, 3020, 786,0 }, + { 888, 888, 35, 0, 246, 53,0 }, + {2141,2141, 35, 0, 186, 73,0 }, + { 891, 891, 35, 0, 713, 266,0 }, + {2142,2142, 35, 0, 200, 100,0 }, + {2143,2143, 35, 0, 220, 80,0 }, + {2144,2144, 35, 0, 2393, 100,0 }, + {2145,2145, 35, 0, 1980, 813,0 }, + { 376, 376, 35, 0, 1880, 840,0 }, + { 895, 895, 35, 0, 366, 140,0 }, + {2146,2146, 35, 0, 346, 106,0 }, + { 382, 382, 35, 0, 1073, 113,0 }, + {2147,2147, 35, 0, 106, 80,0 }, + { 898, 898, 35, 0, 206, 153,0 }, + { 899, 899, 35, 0, 633, 240,0 }, + { 900, 900, 35, 0, 620, 240,0 }, + { 871, 871, 35, 0, 380, 73,0 }, + { 388, 388, 35, 0, 286, 80,0 }, + { 901, 901, 35, 0, 260, 26,0 }, + { 902, 902, 35, 0, 1093, 73,0 }, + { 903, 903, 35, 0, 126, 73,0 }, + {3500,3500, 35, 2, 6, 0,0 }, + {4052,4052, 0, 0, 14166, 320,0 }, + {4053,4053, 0, 0, 7413, 653,0 }, + {4054,4054, 0, 0, 40000, 146,0 }, + {4055,4055, 0, 0, 40000, 113,0 }, + {4056,4056, 0, 0, 16773, 193,0 }, + {4057,4057, 0, 0, 40000, 73,0 }, + {4058,4058, 0, 0, 40000, 0,0 }, + {4059,4059, 0, 0, 966, 373,0 }, + {4060,4060, 0, 0, 40000, 80,0 }, + {4061,4061, 0, 0, 40000, 80,0 }, + {4062,4062, 0, 0, 18473, 93,0 }, + {4063,4063, 0, 0, 40000, 60,0 }, + {4064,4064, 0, 0, 40000, 73,0 }, + {4065,4065, 0, 0, 40000, 0,0 }, + {4066,4066, 0, 0, 40000, 213,0 }, + {4067,4067, 0, 0, 40000, 66,0 }, + {4068,4068, 0, 0, 1413, 1026,0 }, + {4069,4069, 0, 0, 506, 200,0 }, + {4070,4070, 0, 0, 3793, 1106,0 }, + {4071,4071, 0, 0, 40000, 220,0 }, + {4072,4072, 0, 0, 40000, 46,0 }, + {4073,4073, 0, 0, 40000, 0,0 }, + {4074,4074, 0, 0, 40000, 60,0 }, + {4075,4075, 0, 0, 40000, 0,0 }, + {4076,4076, 0, 0, 40000, 33,0 }, + {4077,4077, 0, 0, 40000, 0,0 }, + {4078,4078, 0, 0, 40000, 146,0 }, + {4079,4079, 0, 0, 40000, 66,0 }, + {4080,4080, 0, 0, 40000, 353,0 }, + {4081,4081, 0, 0, 40000, 66,0 }, + {4082,4082, 0, 0, 40000, 53,0 }, + {4083,4083, 0, 0, 40000, 73,0 }, + {4084,4084, 0, 0, 40000, 66,0 }, + {4085,4085, 0, 0, 40000, 926,0 }, + {4086,4086, 0, 0, 2833, 200,0 }, + { 127, 127, 36, 0, 386, 166,0 }, + {4087,4087, 36, 0, 100, 0,0 }, + {2030,2030, 36, 0, 346, 140,0 }, + {3782,3782, 48, 0, 93, 0,0 }, + {3783,3783, 36, 0, 146, 0,0 }, + {4088,4088, 48, 0, 1886, 653,0 }, + { 132, 132, 69, 0, 126, 66,0 }, + {4088,4088, 52, 0, 1853, 626,0 }, + { 152, 152, 48, 0, 220, 86,0 }, + {4088,4088, 55, 0, 1886, 640,0 }, + { 139, 139, 57, 0, 293, 133,0 }, + {4088,4088, 58, 0, 1860, 633,0 }, + {4088,4088, 60, 0, 1886, 633,0 }, + {4089,4089, 62, 0, 2660, 900,0 }, + {4088,4088, 63, 0, 1880, 646,0 }, + { 134, 134, 70, 0, 966, 360,0 }, + {4090,4090, 70, 0, 973, 346,0 }, + {4091,4091, 53, 0, 1866, 640,0 }, + {3516,3516, 48, 0, 180, 93,0 }, + {4092,4092, 84, 0, 1360, 473,0 }, + {4093,4093, 43, 0, 513, 206,0 }, + {4094,4094, 56, 0, 1353, 480,0 }, + {3791,3791, 24, 0, 1866, 613,0 }, + { 134, 134, 65, 0, 1346, 486,0 }, + { 146, 146, 48, 0, 173, 93,0 }, + { 146, 146, 54, 0, 173, 93,0 }, + {4095,4095, 42, 0, 246, 140,0 }, + {4095,4095, 39, 0, 240, 133,0 }, + {3816,3816, 52, 0, 306, 113,0 }, + {4096,4096, 52, 0, 413, 86,0 }, + { 158, 158, 60, 0, 146, 166,0 }, + { 158, 158, 66, 0, 146, 166,0 }, + { 158, 158, 59, 0, 146, 166,0 }, + {3538,3538, 91, 0, 773, 233,0 }, + {3547,3547,109, 0, 5300, 1786,0 }, + {4097,4097, 79, 0, 560, 313,0 }, + {4098,4098, 0, 0, 10646, 73,0 }, + {4099,4100, 0, 1, 14166, 586,0.03125 }, + {4101,4102, 0, 1, 15553, 546,0.03125 }, + {4103,4104, 0, 1, 11746, 320,0.046875 }, + {4105,4106, 0, 1, 14706, 646,0.15625 }, + {4107,4108, 0, 1, 7320, 100,0.046875 }, + {4109,4110, 0, 1, 40000, 0,0.0625 }, + {4111,4112, 0, 1, 13660, 260,0 }, + {4113,4114, 0, 1, 15026, 133,0 }, + {4115,4116, 0, 1, 40000, 0,2.5e-05 }, + {4117,4118, 0, 1, 4980, 3400,0 }, + {4119,4120, 0, 1, 7840, 2660,0.046875 }, + {4121,4122, 0, 1, 8326, 180,0 }, + {4123,4124, 0, 1, 1093, 140,0 }, + {4125,4126, 0, 1, 2280, 400,0 }, + {4127,4128, 0, 1, 4553, 1486,0.03125 }, + {4129,4129, 0, 1, 40000, 0,0.03125 }, + {4130,4131, 0, 1, 40000, 60,0.15625 }, + {4132,4133, 0, 1, 40000, 93,0.078125 }, + {4134,4135, 0, 1, 40000, 86,0.15625 }, + {4136,4137, 0, 1, 40000, 520,0.03125 }, + {4138,4139, 0, 1, 40000, 140,0.0625 }, + {4140,4141, 0, 1, 40000, 133,0.140625 }, + {4142,4143, 0, 1, 40000, 73,0 }, + {4144,4145, 0, 1, 40000, 346,0.109375 }, + {4146,4147, 0, 1, 3693, 86,0 }, + {4148,4149, 0, 1, 6586, 460,2.5e-05 }, + {4150,4151, 0, 1, 4320, 93,0 }, + {4152,4153, 0, 1, 7346, 126,0.046875 }, + {4154,4155, 0, 1, 3633, 260,0 }, + {4156,4157, 0, 1, 40000, 126,-1.95312 }, + {4158,4159, 0, 1, 40000, 126,-1.9375 }, + {4160,4161, 0, 1, 40000, 46,0.234375 }, + {4162,4163, 0, 1, 40000, 0,0.03125 }, + {4164,4165, 0, 1, 10320, 86,0 }, + {4166,4167, 0, 1, 12933, 133,0 }, + {4168,4169, 0, 1, 11820, 240,0.046875 }, + {4170,4171, 0, 1, 3966, 166,0 }, + {4172,4173, 0, 1, 40000, 0,0 }, + {4174,4174, 0, 0, 2666, 160,0 }, + {4175,4176, 0, 1, 15046, 93,0.078125 }, + {4177,4178, 0, 1, 40000, 100,0 }, + {4179,4179, 0, 0, 40000, 260,0 }, + {4180,4181, 0, 1, 40000, 126,2.5e-05 }, + {4182,4182, 0, 0, 40000, 233,0 }, + {4183,4184, 0, 1, 40000, 440,0.078125 }, + {4185,4186, 0, 1, 2160, 606,0.109375 }, + {4187,4188, 0, 1, 14753, 2400,0.03125 }, + {4189,4190, 0, 1, 7680, 646,0.03125 }, + {4191,4192, 0, 1, 40000, 446,0.0625 }, + {4193,4194, 0, 1, 40000, 866,-0.0625 }, + {4195,4195, 0, 1, 40000, 1220,0.078125 }, + {4196,4196, 0, 1, 40000, 1960,0.0625 }, + {4197,4198, 0, 1, 40000, 433,0.125 }, + {4199,4200, 0, 1, 40000, 140,0.140625 }, + {4201,4202, 0, 1, 40000, 806,0.109375 }, + {4203,4204, 0, 1, 2040, 486,0.125 }, + {4205,4206, 0, 1, 40000, 86,0 }, + {4207,4208, 0, 1, 40000, 80,0.03125 }, + {4209,4209, 0, 0, 40000, 73,0 }, + {4210,4210, 0, 0, 40000, 126,0 }, + {4211,4212, 0, 1, 40000, 400,0.0625 }, + {4213,4214, 0, 1, 40000, 120,0.0625 }, + {4215,4216, 0, 1, 40000, 0,0.09375 }, + {4217,4217, 0, 1, 40000, 0,0.125 }, + {4218,4219, 0, 1, 40000, 186,0 }, + {4220,4220, 0, 0, 40000, 166,0 }, + {4221,4221, 0, 0, 40000, 73,0 }, + {4222,4222, 0, 0, 40000, 60,0 }, + {4223,4224, 0, 1, 40000, 140,0 }, + {4225,4225, 0, 0, 40000, 140,0 }, + {4226,4226, 0, 0, 40000, 66,0 }, + {4227,4228, 0, 1, 40000, 133,0 }, + {4229,4229, 0, 0, 40000, 86,0 }, + {4230,4230, 0, 0, 40000, 73,0 }, + {4231,4231, 0, 0, 40000, 106,0 }, + {4232,4233, 0, 1, 40000, 186,0.03125 }, + {4234,4235, 0, 1, 40000, 86,0.046875 }, + {4236,4237, 0, 1, 40000, 0,0.03125 }, + {4238,4238, 0, 0, 40000, 300,0 }, + {4239,4239, 0, 0, 40000, 66,0 }, + {4240,4241, 0, 1, 40000, 73,0.125 }, + {4242,4243, 0, 1, 40000, 86,0.109375 }, + {4244,4245, 0, 1, 40000, 146,0.109375 }, + {4246,4247, 0, 1, 40000, 66,-0.03125 }, + {4248,4248, 0, 0, 40000, 60,0 }, + {4249,4250, 0, 1, 40000, 213,0.15625 }, + {4251,4252, 0, 1, 40000, 66,0.125 }, + {4253,4254, 0, 1, 40000, 100,0.03125 }, + {4255,4256, 0, 1, 40000, 1513,0.078125 }, + {4257,4258, 0, 1, 40000, 353,0.109375 }, + {4259,4260, 0, 1, 40000, 133,0.078125 }, + {4261,4262, 0, 1, 40000, 746,0.140625 }, + {4263,4264, 0, 1, 40000, 0,0.109375 }, + {4265,4266, 0, 1, 5033, 1606,0.0625 }, + {4267,4268, 0, 1, 40000, 1146,0.09375 }, + {4269,4270, 0, 1, 40000, 1586,0.109375 }, + {4271,4272, 0, 1, 40000, 0,0.09375 }, + {4273,4274, 0, 1, 40000, 1006,0.125 }, + {4275,4275, 0, 1, 2680, 793,0.109375 }, + {4276,4277, 0, 1, 40000, 0,-0.046875 }, + {4278,4279, 0, 1, 9000, 3186,0.125 }, + {4280,4281, 0, 1, 40000, 1073,-0.078125 }, + {4282,4283, 0, 1, 40000, 2093,0.140625 }, + {4284,4285, 0, 1, 40000, 0,0.078125 }, + {4286,4287, 0, 1, 9580, 713,0.03125 }, + {4288,4289, 0, 1, 6286, 380,0 }, + {4290,4291, 0, 1, 2220, 426,0.03125 }, + {4292,4292, 0, 0, 1166, 760,0 }, + {4293,4294, 0, 1, 1186, 240,0 }, + {4295,4296, 0, 1, 40000, 100,0.0625 }, + {4297,4297, 0, 0, 40000, 160,0 }, + {4298,4298, 0, 0, 40000, 120,0 }, + {4299,4299, 0, 0, 8673, 2413,0 }, + {4300,4300, 0, 0, 393, 126,0 }, + {4301,4302, 0, 1, 1220, 393,0.03125 }, + {4303,4303, 0, 0, 246, 93,0 }, + {4304,4305, 0, 1, 1953, 393,0 }, + {4306,4307, 0, 1, 566, 146,0 }, + {4308,4309, 0, 1, 4220, 133,0 }, + {4310,4311, 0, 1, 2873, 73,0.109375 }, + {4312,4312, 0, 0, 613, 60,0 }, + {4313,4314, 0, 1, 40000, 186,0 }, + {4315,4316, 0, 1, 11880, 2993,0 }, + {4317,4317, 0, 0, 1573, 86,0 }, + {4318,4319, 0, 1, 40000, 793,0 }, + {4320,4321, 0, 1, 40000, 173,0 }, + {4322,4323, 0, 1, 40000, 793,0 }, + {4324,4324, 0, 0, 606, 133,0 }, + {4325,4325, 34, 0, 133, 40,0 }, + {4326,4326, 28, 0, 193, 46,0 }, + {4327,4328, 39, 1, 553, 126,0 }, + {4327,4328, 33, 1, 553, 126,0 }, + {4329,4330, 63, 1, 160, 80,0 }, + {4331,4331, 15, 0, 113, 0,0 }, + {4332,4332, 36, 0, 106, 0,0 }, + {4332,4333, 36, 1, 480, 173,0.40625 }, + {4334,4335, 25, 1, 313, 153,0 }, + {4336,4335, 25, 1, 206, 100,0 }, + {4337,4338, 61, 1, 153, 93,0 }, + {4339,4340, 38, 1, 340, 133,0 }, + {4341,4342, 37, 1, 206, 93,0 }, + {4343,4344, 15, 1, 346, 153,0 }, + {4345,4346,100, 1, 146, 0,0.140625 }, + {4347,4348, 19, 1, 553, 200,0 }, + {4349,4349, 48, 0, 180, 86,0 }, + {4350,4351, 15, 1, 333, 153,0 }, + {4352,4353, 12, 1, 340, 146,0 }, + {4354,4355, 11, 1, 346, 146,0 }, + {4356,4357, 61, 1, 2706, 1033,0.09375 }, + {4358,4355, 8, 1, 340, 146,0 }, + {4359,4360, 91, 1, 1166, 366,-0.046875 }, + {4361,4361, 70, 0, 966, 346,0 }, + {4362,4363, 80, 1, 300, 93,0.125 }, + {4364,4364, 58, 0, 206, 53,0 }, + {4365,4357, 62, 1, 2333, 820,0.09375 }, + {4366,4367, 31, 1, 773, 200,0 }, + {4368,4360, 91, 1, 1160, 360,-0.03125 }, + {4369,4370, 41, 1, 373, 113,0 }, + {4371,4372, 35, 1, 406, 126,0 }, + {4373,4374, 29, 1, 146, 106,0 }, + {4375,4376, 41, 1, 400, 126,0 }, + {4375,4376, 37, 1, 400, 126,0 }, + {4377,4378, 54, 1, 286, 133,0 }, + {4377,4379, 48, 1, 286, 126,0 }, + {4380,4381, 77, 1, 193, 93,0 }, + {4382,4383, 72, 1, 200, 93,0 }, + {4384,4384, 40, 0, 513, 0,0 }, + {4385,4385, 38, 0, 200, 20,0 }, + {4386,4386, 36, 0, 620, 20,0 }, + {4387,4388, 60, 1, 120, 80,0 }, + {4388,4389, 60, 1, 380, 80,0 }, + {4390,4390, 73, 0, 166, 33,0 }, + {4391,4392, 68, 1, 153, 40,0 }, + {4393,4394, 18, 1, 200, 80,0 }, + {4395,4396, 18, 1, 253, 73,0 }, + {4397,4397, 90, 0, 193, 20,0 }, + {4398,4398, 90, 0, 793, 40,0 }, + {4399,4400, 64, 1, 373, 73,0.03125 }, + {4401,4402, 80, 1, 406, 153,0.03125 }, + {4403,4404, 64, 1, 1866, 606,0 }, + {4405,4405, 67, 0, 106, 26,0 }, + {4406,4407, 50, 1, 173, 0,0 }, + {4408,4408, 36, 0, 4646, 0,0 }, + {4409,4409, 0, 0, 40000, 86,0 }, + {4410,4410, 0, 0, 40000, 73,0 }, + {4411,4411, 0, 0, 2433, 700,0 }, + {4412,4412, 0, 0, 1233, 26,0 }, + {4413,4413, 0, 0, 40000, 66,0 }, + {4414,4414, 0, 0, 40000, 60,0 }, + {4415,4415, 0, 0, 40000, 60,0 }, + {4416,4416, 0, 0, 40000, 66,0 }, + {4417,4417, 0, 0, 40000, 66,0 }, + {4418,4418, 0, 0, 40000, 0,0 }, + {4418,4418, 73, 0, 40000, 0,0 }, + {4419,4419, 0, 0, 40000, 60,0 }, + {4420,4420, 0, 0, 40000, 60,0 }, + {4421,4421, 0, 0, 7326, 2486,0 }, + {4422,4422, 0, 0, 4886, 1586,0 }, + {4423,4423, 0, 0, 646, 20,0 }, + {4424,4424, 0, 0, 253, 20,0 }, + {4424,4424, 12, 0, 253, 20,0 }, + {4425,4425, 0, 0, 640, 100,0 }, + {4425,4425, 1, 0, 640, 106,0 }, + {4426,4426, 0, 0, 133, 106,0 }, + {4426,4426, 23, 0, 133, 106,0 }, + {4427,4427, 0, 0, 653, 100,0 }, + {4428,4428, 0, 0, 4166, 1546,0 }, + {4429,4429, 0, 0, 40000, 73,0 }, + {4430,4430, 0, 0, 40000, 60,0 }, + {4431,4431, 0, 0, 40000, 53,0 }, + {4432,4432, 0, 0, 40000, 0,0 }, + {4433,4433, 0, 0, 246, 20,0 }, + {4434,4434, 0, 2, 6, 0,0 }, + {4435,4435, 0, 0, 4946, 233,0 }, + {4436,4436, 0, 0, 4946, 233,0 }, + {4437,4437, 0, 0, 4953, 240,0 }, + {4438,4438, 0, 0, 4946, 233,0 }, + {4439,4439, 0, 0, 18233, 46,0 }, + {4440,4440, 0, 0, 2386, 26,0 }, + {4441,4441, 0, 0, 4640, 633,0 }, + {4442,4442, 0, 0, 18466, 100,0 }, + {4443,4443, 0, 0, 18440, 66,-2 }, + {4444,4444, 0, 0, 18440, 6140,-2 }, + {4445,4445, 0, 0, 1206, 433,-2 }, + {4446,4446, 0, 0, 4626, 240,0 }, + {4447,4447, 0, 0, 726, 400,0 }, + {4448,4448, 0, 0, 5866, 73,0 }, + {4449,4449, 0, 0, 40000, 73,0 }, + {4450,4450, 0, 0, 40000, 73,0 }, + {4451,4451, 0, 0, 40000, 73,0 }, + {4452,4452, 0, 0, 40000, 73,0 }, + {4453,4453, 0, 0, 6500, 346,0 }, + {4454,4454, 0, 0, 6506, 346,0 }, + {4455,4455, 0, 0, 40000, 66,-2 }, + {4456,4456, 0, 0, 40000, 66,-2 }, + {4457,4457, 0, 0, 40000, 0,0 }, + {4458,4458, 0, 0, 40000, 46,0 }, + {4459,4459, 0, 0, 40000, 0,0 }, + {4459,4459, 0, 0, 40000, 0,-2 }, + {4460,4460, 0, 0, 2386, 26,0 }, + {4461,4461, 0, 0, 40000, 73,-2 }, + {4462,4462, 0, 0, 5866, 26,-2 }, + {4463,4463, 0, 0, 40000, 133,0 }, + {4464,4464, 0, 0, 40000, 133,0 }, + {4465,4465, 0, 0, 40000, 126,0 }, + {4466,4466, 0, 0, 253, 20,0 }, + {4467,4467, 0, 0, 8866, 1366,0 }, + {4468,4468, 0, 0, 1040, 766,0 }, + {4469,4469, 0, 0, 40000, 146,-2 }, + {4470,4470, 0, 0, 40000, 153,-2 }, + {4471,4471, 0, 0, 40000, 466,-2 }, + {4472,4472, 0, 0, 40000, 66,0 }, + {4473,4473, 0, 0, 2333, 566,0 }, + {4474,4474, 0, 0, 40000, 140,-2 }, + {4475,4475, 0, 0, 40000, 100,-2 }, + {4476,4476, 0, 0, 40000, 226,-2 }, + {4477,4477, 0, 0, 40000, 0,0 }, + {3712,3712, 0, 0, 40000, 226,-2 }, + {4478,4478, 0, 0, 40000, 140,-2 }, + {4479,4479, 0, 0, 40000, 66,0 }, + {4480,4480, 0, 0, 40000, 73,0 }, + {4481,4481, 0, 0, 40000, 73,0 }, + {4482,4482, 0, 0, 40000, 86,-2 }, + {4483,4483, 0, 0, 40000, 80,0 }, + {4484,4484, 0, 0, 40000, 73,-2 }, + {4485,4485, 0, 0, 40000, 80,-2 }, + {4486,4486, 0, 0, 40000, 73,-2 }, + {4487,4487, 0, 0, 40000, 73,0 }, + {4488,4488, 0, 0, 40000, 73,0 }, + {4489,4489, 0, 0, 40000, 93,0 }, + {4490,4490, 0, 0, 40000, 73,0 }, + {4491,4491, 0, 0, 11946, 13,0 }, + {4492,4492, 0, 0, 40000, 73,0 }, + {4462,4462, 0, 0, 5866, 26,0 }, + {4493,4493, 0, 0, 40000, 820,0 }, + {4494,4494, 0, 0, 2153, 873,0 }, + {1221,1221, 0, 0, 40000, 293,0.171875 }, + {4495,4495, 0, 0, 1620, 120,0 }, + {4496,4496, 0, 0, 15120, 93,0 }, + {4497,4497, 0, 0, 14613, 93,0 }, + {4498,4498, 0, 0, 2346, 793,0 }, + {4499,4499, 0, 0, 40000, 2380,0 }, + {4500,4500, 0, 0, 40000, 1280,0 }, + {4501,4501, 0, 0, 40000, 1460,0 }, + {4502,4502, 0, 0, 40000, 2513,0 }, + {4503,4503, 0, 0, 14840, 1266,0 }, + {4504,4504, 0, 0, 4513, 640,0 }, + {4505,4505, 0, 0, 4680, 806,0 }, + {4506,4506, 0, 0, 40000, 100,0 }, + {4507,4507, 0, 0, 40000, 66,0 }, + {4508,4508, 0, 0, 2420, 413,0 }, + {4509,4509, 0, 0, 406, 73,-2 }, + {4510,4510, 0, 0, 1166, 400,0 }, + {4511,4511, 0, 0, 1213, 106,0 }, + {4512,4512, 0, 0, 273, 60,-2 }, + {4513,4513, 0, 0, 40000, 2380,0 }, + {4514,4514, 0, 0, 40000, 440,0 }, + {1261,1261, 0, 0, 40000, 2960,0 }, + {4515,4515, 37, 0, 973, 73,-2 }, + {4516,4516, 48, 0, 106, 0,-2 }, + {4517,4517, 48, 0, 286, 133,-2 }, + {4518,4518, 62, 0, 166, 60,0 }, + {4519,4519, 44, 0, 980, 360,0 }, + {4520,4520, 80, 0, 100, 0,0 }, + {4519,4519, 50, 0, 980, 346,0 }, + {4521,4521, 48, 0, 106, 0,-2 }, + {4519,4519, 55, 0, 973, 360,0 }, + {4522,4522, 61, 0, 513, 20,0 }, + {4519,4519, 58, 0, 966, 353,0 }, + {4519,4519, 63, 0, 973, 353,0 }, + {4523,4523, 71, 0, 1366, 580,0 }, + {4519,4519, 72, 0, 820, 306,0 }, + {4524,4524, 70, 0, 1886, 666,0 }, + {4523,4523, 88, 0, 1353, 560,0 }, + {4525,4525, 76, 0, 1873, 653,0 }, + {4526,4526, 84, 0, 260, 113,0 }, + {4523,4523, 68, 0, 1366, 553,0 }, + {4527,4527, 72, 0, 153, 53,0 }, + {4528,4528, 28, 0, 1193, 413,0 }, + {4524,4524, 81, 0, 1353, 480,0 }, + {4529,4529, 58, 0, 246, 120,-2 }, + {4529,4529, 55, 0, 246, 120,-2 }, + {4529,4529, 44, 0, 246, 120,-2 }, + {4529,4529, 49, 0, 246, 120,-2 }, + {4529,4529, 40, 0, 286, 133,-2 }, + {4530,4530, 55, 0, 740, 560,-2 }, + {4530,4530, 48, 0, 893, 693,-2 }, + {4531,4531, 52, 0, 513, 206,0 }, + {4531,4531, 45, 0, 513, 206,0 }, + {4532,4532, 48, 0, 173, 100,-2 }, + {4533,4533, 48, 0, 120, 266,-2 }, + {4534,4534, 48, 0, 253, 60,-2 }, + {4509,4509, 73, 0, 160, 20,-2 }, + {4509,4509, 68, 0, 160, 20,-2 }, + {4509,4509, 63, 0, 193, 20,-2 }, + {4535,4535,108, 0, 406, 26,0 }, + {4536,4536,108, 0, 740, 26,0 }, }; //Returns total number of generated banks int maxAdlBanks() -{ return 74; +{ return 75; } -const char* const banknames[74] = +const char* const banknames[75] = { "AIL (Star Control 3, Albion, Empire 2, etc.)", "Bisqwit (selection of 4op and 2op)", @@ -9069,15 +9438,16 @@ const char* const banknames[74] = "DMX (Raptor)", "OP3 (Modded GMOPL by Wohlstand)", "SB (Jammey O'Connel's bank)", - "TMB (Default bank of Build Engine)", + "TMB (Default bank of Apgee Sound System)", "WOPL (4op bank by James Alan Nguyen and Wohlstand)", "TMB (Blood)", "TMB (Lee)", "TMB (Nam)", "WOPL (DMXOPL3 bank by Sneakernets)", "EA (Cartooners)", + "WOPL (Apogee IMF 90-ish)", }; -const unsigned short banks[74][256] = +const unsigned short banks[75][256] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, @@ -9105,7 +9475,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235, 236, 237, 238, 239, 71, 72, 240, 241, 242, 243, 244, 245, 246, 247, 248, 82, 249, 250, 251, 86, 252, 253, 254, 255, 91, 92, 256, 257, 258, 259, 260, 98, 99, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, - 111, 272, 273, 274, 115, 275, 276, 277, 278, 120, 279, 280, 281, 282, 283, 284, + 111, 272, 273, 274, 115, 275, 276, 277, 278, 120, 279, 280, 281, 282, 295, 284, 127, 132, 285, 286, 127, 287, 288, 289, 290, 291, 292, 127, 127, 293, 294, 295, 289, 296, 297, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 318, 320, 318, 321, 318, @@ -9124,86 +9494,86 @@ const unsigned short banks[74][256] = 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 386, 387, 388, 389, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 385, 386, 387, 388, 389, 390, 391, 392, 369, 393, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 419, 430, 351, 431, 308, 432, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { - 433, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, - 468, 469, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 304, 470, 471, 472, 473, + 468, 469, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 304, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, - 521, 522, 523, 524, 525, 526, 527, 528, 355, 356, 357, 358, 359, 529, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 521, 522, 523, 524, 525, 526, 527, 528, 355, 356, 357, 358, 359, 529, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { - 433, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, - 468, 433, 112, 99, 530, 531, 93, 532, 248, 107, 116, 533, 28, 78, 534, 535, - 536, 79, 94, 38, 33, 115, 537, 538, 539, 540, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 541, 542, 543, 544, 545, 546, 433, 295, + 468, 295, 112, 99, 530, 531, 93, 532, 248, 107, 116, 533, 28, 78, 534, 535, + 536, 79, 94, 38, 33, 115, 537, 538, 539, 540, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 541, 542, 543, 544, 545, 546, 295, 295, 547, 132, 134, 136, 138, 139, 141, 173, 156, 291, 292, 127, 548, 549, 550, 551, - 552, 362, 553, 143, 433, 433, 433, 433, 433, 433, 433, 304, 470, 471, 472, 473, + 552, 362, 553, 143, 295, 295, 295, 295, 295, 295, 295, 304, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 554, 555, 556, 557, 487, 488, 489, 490, 491, 492, 493, 558, 495, 496, 497, 498, 499, 500, 501, 559, 503, 504, 505, 506, 507, 508, 560, 561, 511, 512, 513, 514, 562, 563, 345, 517, 518, 519, 520, - 521, 522, 523, 524, 525, 526, 527, 528, 564, 356, 565, 358, 359, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 295, + 521, 522, 523, 524, 525, 526, 527, 528, 564, 356, 565, 358, 359, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { - 433, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, - 468, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 566, 567, 568, 569, + 468, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 566, 567, 568, 569, 570, 34, 571, 572, 437, 51, 52, 84, 573, 574, 575, 576, 577, 85, 530, 90, - 93, 94, 101, 578, 114, 119, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 293, 127, 548, + 93, 94, 101, 578, 114, 119, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 293, 127, 548, 550, 296, 297, 296, 297, 298, 299, 300, 301, 302, 303, 304, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 355, 356, 357, 358, 359, 360, 361, 362, - 164, 363, 156, 364, 292, 365, 366, 367, 368, 178, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 164, 363, 156, 364, 292, 365, 366, 367, 368, 178, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { - 433, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, - 468, 433, 579, 580, 581, 433, 433, 582, 433, 433, 433, 433, 583, 584, 585, 586, - 587, 444, 588, 589, 590, 433, 591, 592, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 577, 566, + 468, 295, 579, 580, 581, 295, 295, 582, 295, 295, 295, 295, 583, 584, 585, 586, + 587, 444, 588, 589, 590, 295, 591, 592, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 577, 566, 437, 90, 460, 28, 593, 594, 595, 596, 31, 597, 598, 28, 441, 442, 567, 530, - 93, 572, 599, 279, 30, 435, 100, 94, 575, 38, 281, 437, 447, 51, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 356, 357, 358, 359, 304, 470, 471, 472, 473, + 93, 572, 599, 279, 30, 435, 100, 94, 575, 38, 281, 437, 447, 51, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 356, 357, 358, 359, 304, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 355, 600, 601, 602, 127, 548, 296, 603, 604, 550, 605, 606, 362, 607, 135, 608, 361, 609, 610, 611, 612, 507, 146, 613, - 547, 500, 614, 364, 173, 615, 366, 600, 616, 617, 618, 619, 620, 621, 433, 433, + 547, 500, 614, 364, 173, 615, 366, 600, 616, 617, 618, 619, 620, 621, 295, 295, }, { 622, 179, 371, 180, 372, 373, 374, 375, 376, 377, 186, 378, 187, 379, 380, 190, @@ -9231,15 +9601,15 @@ const unsigned short banks[74][256] = 659, 599, 660, 661, 662, 68, 663, 664, 71, 665, 666, 74, 75, 667, 668, 669, 577, 85, 441, 442, 38, 84, 46, 86, 530, 88, 670, 90, 91, 92, 93, 94, 258, 671, 97, 532, 99, 100, 101, 672, 536, 673, 674, 675, 676, 677, 464, 110, - 678, 578, 113, 114, 679, 116, 680, 599, 31, 462, 279, 125, 465, 281, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, - 433, 433, 433, 681, 682, 683, 500, 503, 614, 684, 685, 296, 552, 550, 605, 600, + 678, 578, 113, 114, 679, 116, 680, 599, 31, 462, 279, 125, 465, 281, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 681, 682, 683, 500, 503, 614, 684, 685, 296, 552, 550, 605, 600, 548, 602, 127, 601, 293, 549, 604, 551, 603, 298, 362, 299, 135, 300, 361, 301, 302, 547, 303, 142, 143, 144, 619, 146, 291, 686, 149, 687, 367, 368, 292, 365, 366, 364, 688, 158, 689, 690, 615, 162, 163, 164, 363, 691, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 692, 693, 694, 695, 696, 697, 698, 699, - 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 712, 713, 714, 715, 716, 713, 717, 718, 719, 720, 721, 722, 723, 724, 715, 725, @@ -9250,14 +9620,14 @@ const unsigned short banks[74][256] = 769, 770, 771, 664, 772, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 788, 789, 786, 753, 790, 780, 780, 780, 791, 791, 792, 793, 794, 795, 796, 680, 680, 797, 798, 799, 800, 801, 802, 803, 742, 804, - 805, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, + 805, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 821, 824, 825, 821, 826, 821, 827, 828, 829, 830, 831, 832, - 833, 834, 834, 835, 835, 825, 825, 836, 837, 838, 806, 839, 840, 841, 806, 806, - 842, 843, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, + 833, 834, 834, 835, 835, 825, 825, 836, 837, 838, 295, 839, 840, 841, 295, 295, + 842, 843, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, @@ -9268,14 +9638,14 @@ const unsigned short banks[74][256] = 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 295, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 971, 972, 973, 974, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 971, 972, 973, 974, 975, 310, 976, 977, 978, 974, 979, 980, 981, 982, 983, 984, 983, 985, 986, 987, 988, 989, 990, 325, 989, 325, 991, 989, 992, 989, 330, 993, 994, 995, 996, 997, 998, 999,1000,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, -1012, 429,1003, 430,1013,1014, 974,1015, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 295, +1012, 429,1003, 430,1013,1014, 974,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031, @@ -9286,14 +9656,14 @@ const unsigned short banks[74][256] = 1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110, 1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126, 1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 305, 306, 307, 308, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 305, 306, 307, 308, 1143, 425, 311,1144, 393, 308, 394, 395, 396, 397,1145, 399,1145, 400,1146, 402, 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 419, 430, 351, 431, 308, 432, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 1147,1017,1018,1148,1149,1150,1022, 184,1024,1025,1026,1027,1028,1151,1152,1153, @@ -9304,14 +9674,14 @@ const unsigned short banks[74][256] = 1188,1189,1097,1098,1190,1100,1191,1192,1103,1193,1194,1195,1196,1197,1198,1199, 1111,1200,1201,1202,1203,1116,1204,1205,1119,1206,1207,1122,1123,1124,1125,1208, 1127,1209,1210,1130,1131,1211,1212,1213,1135,1214,1215,1138,1139,1216,1217,1218, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 386, 387, 388, 389, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 385, 386, 387, 388, 389, 390, 391, 392,1219,1219, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, 414, 415, 416,1220,1221, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 419, 430, 351, 431, 308, 432, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, @@ -9344,7 +9714,7 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 312,1349,1350,1351,1352,1353,1354, 319,1355, 320,1356, 321,1357, 1358,1359,1360,1361,1362,1363,1364,1365,1366,1359,1367,1361, 332, 333, 334, 335, - 336,1368,1369, 338, 339, 320,1370,1371,1371,1371,1371,1372,1373,1374,1375,1371, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -9362,7 +9732,7 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 312,1349,1350,1351,1352,1382,1354, 319,1355, 320,1356, 321,1357, 1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1384,1361, 332, 333, 334, 335, - 336,1368,1369, 338, 339, 320,1370,1371,1371,1371,1371,1372,1373,1374,1375,1371, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -9380,7 +9750,7 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 312,1349,1350,1351,1352,1353,1354, 319,1355, 320,1356, 321,1357, 1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1387,1361, 332, 333, 334, 335, - 336,1368,1369, 338, 339, 320,1370,1371,1371,1371,1371,1372,1373,1374,1375,1371, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -9447,7 +9817,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,1611,1612, 283, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 127, 127,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623, @@ -9556,14 +9926,14 @@ const unsigned short banks[74][256] = 2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2071,2072,2073, 2074,2075,2076,2077,2078,2079,2080, 275,2081,2082,1527,2083,2084,2085,2086,2087, 2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099, 122,2100,2101,2102, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, -2115, 547,2115,2116,2103,2103,2117,2103,2118,2103,2103,2103,2119,2120,2121,2122, -2123,2124,2125,2126,2127, 160, 161, 162, 163,2128,2103,2129,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103, 295,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, +2115, 547,2115,2116, 295, 295,2117, 295,2118, 295, 295, 295,2119,2120,2121,2122, +2123,2124,2125,2126,2127, 160, 161, 162, 163,2128, 295,2129, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 2130,2130,2130,2130,2130,2130, 727,2131,2132,2133,2134,2135,2136,2137,2138,2139, @@ -9645,7 +10015,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235,2281, 237, 238, 239,1584,1585,2282,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590,2283, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,1611,1612, 283, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2284, 295, 295, 295,2284,2285,2286,1614,1615,2287,2288,1618,1619,1620,1621,1622,1623, @@ -9735,7 +10105,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,1611,1612, 283, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2430,2430,2431,2432,2433,2434,2435,2436,2437,2436,2438,2436,2439, @@ -9771,7 +10141,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,1611,1612, 283, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 127, 127,2286,1614,1615,1616,2288,1618,1619,1620,1621,1622,1623, @@ -9811,7 +10181,7 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, 295,2555, 295,2556,2495,2557,2558,2559,2496,2497,2560,2498,2561,2499,2562,2500, -2563,2564,2565,2527, 295, 295,2566, 295,2567, 295, 295, 295,2570,2571, 295,2572, +2563,2564,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,2570,2571, 295,2572, 2573, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -9897,7 +10267,7 @@ const unsigned short banks[74][256] = 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,2873, 242, 243, 244,2874, 246, 247,1588,1589, 249,2875,2876,2877,2878, 253, 254, 255,1591,1592,1593,2879, 1595, 259,1596,2880,1598,1599,1600,1601, 264, 265, 266,2881, 268, 108, 270,2859, -2882,2883, 273,1603,1604,1605, 276,2884,2885,2886,1609,1610,2887,1612,2888, 284, +2882,2883, 273,1603,1604,1605, 276,2884,2885,2886, 295,1610,2887,1612,2888, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2889,2889, 128, 129, 130,2890, 132,2891, 134, 135, 136, 137, 138, @@ -9934,14 +10304,14 @@ const unsigned short banks[74][256] = 2968,2969,2554,2970,2489,2971,2972,2973, 657, 575,2974,2975,2976, 779,2977,2978, 2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2553,2989,2990,2991,2992,2993, 2994,2995, 680, 796,2996, 797,2662,2997,2998,2999,2514,3000,3001,1913,3002,3003, - 295, 295, 295, 295, 295, 295, 295, 295, 295, 384, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,3004, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,1219,1219, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, 414, 415, 416,1220,1221, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 419, 430, 351, 431, 308, 432, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 2501,2502,2503,2504,2505,2506,2507, 730,2508,2509,2510,2511,2512,2513,2514,2515, @@ -9955,7 +10325,7 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, 295,2555, 295,2556,2495,2557,2558,2559,2496,2497,2560,2498,2561,2499,2562,2500, -2563,3011,2565,2527, 295, 295,2566, 295,2567, 295, 295, 295,3012,3013,3014,3015, +2563,3011,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,3012,3013,3014,3015, 3016,3015,3016, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10006,14 +10376,14 @@ const unsigned short banks[74][256] = 2059,2060,2061,2062,2063,2064,2065,2066,3089,2068,2069,2070,3090,3091,2072,2073, 2074,2075,2076,2077,2078,2079,2080, 275,2081,2082,1527,2083,2084,2085,2086,2087, 2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099, 122,2100,2101,2102, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, -2115, 547,2115,2116,2103,2103,2117,2103,2118,2103,2103,2103,2119,2120,2121,2122, -2123,2124,2125,2126,2127, 160, 161, 162, 163,2128,2103,2129,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103, 295,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, -2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103,2103, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, +2115, 547,2115,2116, 295, 295,2117, 295,2118, 295, 295, 295,2119,2120,2121,2122, +2123,2124,2125,2126,2127, 160, 161, 162, 163,2128, 295,2129, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 2501,2502,2503,2504,2505,2506,2507, 730,2508,2509,2510,2511,2512,2513,2514,2515, @@ -10027,75 +10397,75 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, 295,2555, 295,3092,2495,2557,2558,2559,2496,2497,2562,2498,2561,2499,2562,2500, -2563,3011,2565,2527, 295, 295,2566, 295,2567, 295, 295, 295,2570,2571, 295,2572, +2563,3011,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,2570,2571, 295,2572, 3093, 295, 295, 295, 295, 295,3094, 295, 295,3095,3096, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,3097, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -1551,1552,1553,3101, 181, 182, 183,3102, 185,1556,1557,3103,1559, 188, 189,1560, -1561,1562, 193,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, - 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, - 220, 221,1574,1575,1576,1577,1578, 225,1579,1580,3100,1581,1582, 229, 230, 231, - 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, - 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, -1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,3104,3105, 283, 284, +3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, +3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, +3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, +3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, +3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, +3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, +3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, +3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,3107,3122,3126, 316,3123,3121,3124,3107,3125,3107,3127,3107, -3107, 329,3107,3108,3143,3109,3128,3129,3099,3144,1634,3110,3130,3131,3117,3119, -3136,3132,3133,3134,3137,3115,3138,3135,3098, 343, 344,3112,3113,3113,3142,3139, -3140,3141,3116,3106,3111,3114,3118,3120, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127,3227,3244,3248,3268,3245,3243,3246,3227,3247,3227,3249,3227, +3227,3269,3227,3228,3270,3229,3250,3251,3100,3271,3258,3230,3252,3253,3237,3239, +3259,3254,3255,3256,3260,3235,3261,3257,3099,3265,3266,3232,3233,3233,3267,3262, +3263,3264,3236,3225,3231,3234,3238,3240, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -1551,1552,1553,3101, 181, 182, 183,3102, 185,1556,1557,3103,1559, 188, 189,1560, -1561,1562, 193,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, - 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, - 220, 221,1574,1575,1576,1577,1578, 225,1579,1580,3100,1581,1582, 229, 230, 231, - 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, - 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, -1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,3104,3105, 283, 284, +3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, +3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, +3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, +3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, +3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, +3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, +3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, +3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,2430,2430,3149,3147,3148,3147,3145,3145,3145,3145,3145,3145,3145, -3145,3145,3145,3145,3145,3145,3150,3151,3173, 329,3152, 329,3153,3154,3155,3156, -3157,3158,3159,3160,3161, 349,3162,3163,3164,3165,3166,3148,3148,3148,3167,3168, -3169,3170, 349,3171,3172,3148,3155,3156,3146, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3274,3274,3277,3275,3276,3275,3272,3272,3272,3272,3272,3272,3272, +3272,3272,3272,3272,3272,3272,3278,3279,3301,3269,3280,3269,3281,3282,3283,3284, +3285,3286,3287,3288,3289, 349,3290,3291,3292,3293,3294,3276,3276,3276,3295,3296, +3297,3298, 349,3299,3300,3276,3283,3284,3273, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187,3188,3189, -3190,3191,3192,3193,3194,3195,3196,3196,3197,3198,3199,3200,3201,3202,3203,3204, -3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218,3219,3220, -3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3196,3232,3233,3234,3235, -3196,3236,3237,3238,3196,3239,3196,3240,3196,3241,3196,3242,3196,3243,3244,3245, -3246,3247,3248,3196,3249,3250,3196,3251,3252,3253,3254,3255,3196,3196,3256,3196, -3257,3196,3258,3196,3259,3260,3196,3261,3196,3196,3262,3196,3196,3196,3196,3196, -3196,3196,3196,3263,3196,3196,3196,3196,3196,3196,3264,3265,3196,3196,3196,3196, -3266,3267, 285,3268,3269, 287, 288,3270,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3272,3273,3271,3274,3271,3275,3276,3277,3278,3279,3280,3281,3282, -3282,3283,3280,3284,3271,3271,3285,3286,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, -3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, +3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317, +3318,3319,3320,3321,3322,3323,3324,3324,3325,3326,3327,3328,3329,3330,3331,3332, +3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348, +3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3324,3360,3361,3362,3363, +3324,3364,3365,3366,3324,3367,3324,3368,3324,3369,3324,3370,3324,3371,3372,3373, +3374,3375,3376,3324,3377,3378,3324,3379,3380,3381,3382,3383,3324,3324,3384,3324, +3385,3324,3386,3324,3387,3388,3324,3389,3324,3324,3390,3324,3324,3324,3324,3324, +3324,3324,3324,3391,3324,3324,3324,3324,3324,3324,3392,3393,3324,3324,3324,3324, +3394,3395, 285,3396,3397, 287, 288,3398,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3400,3401,3399,3402,3399,3403,3404,3405,3406,3407,3408,3409,3410, +3410,3411,3408,3412,3399,3399,3413,3414,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, }, { -3287,3288, 466,3289, 713,3196,3290, 467,3291, 632,3292,3293,3294,3295,3296,3297, -3298, 598,3299,3300,3301,3302,3303,3304,1512,3305,3294,3306, 759, 437, 437,3307, -3308,3309, 439,3310, 440,3311, 442, 441,3312,3313,3314,3315,2306,3316,3317, 582, -3318,3319, 452,3320,3321,3290,3322,3323,3324, 778,3325,2312, 779,3326,3327,3328, -3329, 769,3330,3331,3332,3333, 663, 664,3334,2316,3335,3336,3337,1299,3338,3339, -3340,3341,3342,3343,3344,3345,3346, 442,3347,3348,3349,3304,3350,1669,3351,3352, -3353,3354,3355,3356,3353, 261,3357,3358,1919,2420,3359,3360, 676,3361,3362,3363, -3364,3365,3366,3367, 677,3368, 677,3369,3370, 677,3371, 455,3372,2421,3373, 465, +3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,3424,3425, +3426, 598,3427,3428,3429,3430,3431,3432,1512,3433,3422,3434, 759, 437, 437,3435, +3436,3437, 439,3438, 440,3439, 442, 441,3440,3441,3442,3443,2306,3444,3445, 582, +3446,3447, 452,3448,3449,3418,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, +3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, +3468,3469,3470,3471,3472,3473,3474, 442,3475,3476,3477,3432,3478,1669,3479,3480, +3481,3482,3483,3484,3481, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, +3492,3493,3494,3495, 677,3496, 677,3497,3498, 677,3499, 455,3500,2421,3501, 465, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10106,14 +10476,14 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3374,3374,3375,3376,3377,3378,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387, -3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403, -3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419, -3420,3421,3422,3423,3424,3425,3426,3427,3425,3428,3429,3430,3431,3432,3433,3434, -3435,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,3446,3335,3447,3448,3449, -3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462,3462,3463,3464, -3465,3466,3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478,3479,3480, -3481,3482,3481,3483,3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495, +3502,3502,3503,3504,3505,3506,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515, +3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531, +3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547, +3548,3549,3550,3551,3552,3553,3554,3555,3553,3556,3557,3558,3559,3560,3561,3562, +3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3463,3575,3576,3577, +3578,3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3590,3591,3592, +3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608, +3609,3610,3609,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10135,155 +10505,155 @@ const unsigned short banks[74][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,3496, 150, 151, 152, 153, 154, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,3624, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,3497,3498,3499, 188, 189,1560, -1561,1562,1563,3500,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, - 206, 207, 208,1568,1569,1570,1571,3501,1572,1573, 214, 215, 216, 217, 218, 219, +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,3625,3626,3627, 188, 189,1560, +1561,1562,1563,3628,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571,3629,1572,1573, 214, 215, 216, 217, 218, 219, 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607,1608,1609,1610,3502,3503, 283, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3630,3631, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,3504,1613,1614,3505,1616,3506,1618,3507,1620,3508,1622,3509, -3510,1625,3511,3512,1628,1629,1630,1631,3513,1633,1634,3514,1636,1637,1638,1639, + 295, 295, 295, 127,3632,1613,1614,3633,1616,3634,1618,3635,1620,3636,1622,3637, +3638,1625,3639,3640,1628,1629,1630,1631,3641,1633,1634,3642,1636,1637,1638,1639, 1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655, 1656,1657,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530, -3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546, -3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557,3558,3559,3560,3561,3562, -3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576,3577,3578, -3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594, -3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610, -3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626, -3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642, -3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643, -3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643, -3643,3643,3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, -3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, -3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, -3686,3687, 806, 806, 806, 806, 806,3643,3643,3643,3643,3643,3643,3643,3643,3643, -3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643, -3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643,3643, - }, - { -3287,1492,3688,3289, 713,3689,3690, 730,1914,3691,1533,3293,3692,3693,3694,3695, -3298,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706, 759, 437,3707,3708, -3308,3709,3710,3711,3712,3713, 442,3714,3715,3716,3717,3718,3719,3720,3721,3722, -3723,2306,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737, -3738,3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753, -3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769, -3770,3771,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785, -3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,3371,3796,3797,2421,3798,3799, +3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658, +3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674, +3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690, +3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706, +3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721,3722, +3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, +3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754, +3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, -3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, -3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, -3686,3687, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, -3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831, -3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847, -3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862, -3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, -3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, -3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909, -3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3924,3925, -3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926, -3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926, -3926,3926,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939, -3940,3941,3942,3660,3943,3944,3662,3945,3663,3946,3947,3665,3948,3949,3950,3951, -3952,3953,3954,3955,3956,3936,3676,3957,3958,3679,3959,3960,3961,3962,3684,3685, -3963,3964,3965,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926, -3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926, -3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926,3926, +3415,1492,3816,3417, 713,3817,3818, 730,1914,3819,1533,3421,3820,3821,3822,3823, +3426,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833,3834, 759, 437,3835,3836, +3436,3837,3838,3839,3840,3841, 442,3842,3843,3844,3845,3846,3847,3848,3849,3850, +3851,2306,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,3864,3865, +3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878,3879,3880,3881, +3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897, +3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909,3910,3911,3912,3913, +3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3499,3924,3925,2421,3926,3927, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, -3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3830,3966,3831, -3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3967,3845,3846,3847, -3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3968,3862, -3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, -3969,3879,3970,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, -3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3971,3972,3973,3907,3908,3909, -3910,3911,3912,3913,3974,3915,3916,3975,3918,3919,3920,3921,3922,3923,3924,3925, -3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976, -3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3977,3977,3977,3977,3978, -3977,3977,3977,3927,3928,3979,3930,3931,3932,3980,3934,3935,3936,3937,3938,3939, -3940,3941,3942,3660,3943,3944,3662,3945,3663,3946,3947,3665,3948,3949,3950,3951, -3952,3953,3954,3955,3956,3936,3676,3957,3958,3981,3982,3960,3983,3984,3684,3685, -3963,3964,3965,3977,3977,3977,3977,3977,3977,3976,3985,3985,3985,3985,3985,3985, -3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985, -3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985, +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, +4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, +4091,4092,4093, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3958,4094,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,4095,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,4096,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4097,4007,4098,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4099,4100,4101,4035,4036,4037, +4038,4039,4040,4041,4102,4043,4044,4103,4046,4047,4048,4049,4050,4051,4052,4053, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4106, +4105,4105,4105,4055,4056,4107,4058,4059,4060,4108,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,4109,4110,4088,4111,4112,3812,3813, +4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, }, { 1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, -1238,1239,1240,1241,1242,1243,1244,1245,3986,3987,3988,1249,1250,1376,1377,1253, -1378,1255,1256,3989,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,3990, -1270,3991,3992,3993,3994,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, +1238,1239,1240,1241,1242,1243,1244,1245,4114,4115,4116,1249,1250,1376,1377,1253, +1378,1255,1256,4117,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,4118, +1270,4119,4120,4121,4122,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, 1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, -1302,1303,1304,1305,3995,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1302,1303,1304,1305,4123,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, 1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, -1333,1334,1335,1336,1337,3996,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, +1333,1334,1335,1336,1337,4124,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 312,1349,1350,3997,1352,3998,1354,3999,1355, 320,1356, 321,1357, + 295, 295, 295, 312,1349,1350,4125,1352,4126,1354,4127,1355, 320,1356, 321,1357, 1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1384,1361, 332, 333, 334, 335, - 336,1368,1369, 338, 339, 320,1370,1371,1371,1371,1371,1372,1373,1374,1375,1371, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -4000,4001,4002,4003,4004,4005, 6, 7,4006, 9, 10,4007, 12, 13, 14, 15, -4008,4009,4010,4011, 20,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022, - 32,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036, 46, -4037,4038,4039,4040,4041,4042,4043, 54,4044,4045,4046,4047,4048,4049,4050,4051, -4052,4053,4054, 66,4055,4056, 69,4057,4058,4059,4060, 74, 75, 76, 77, 78, -4061,4062,4063, 82,4064,4065, 85,4066,4067, 88,4068,4069,4070,4071,4072,4073, - 95, 96, 97,4074,4075,4076,4077, 102, 103,4078, 105, 106, 107,4079,4080,4081, -4082, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,4083,4084,4085, 125,4086, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, -3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, -3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, -3686,3687, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, +4128,4129,4130,4131,4132,4133, 6, 7,4134, 9, 10,4135, 12, 13, 14, 15, +4136,4137,4138,4139, 20,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150, + 32,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163,4164, 46, +4165,4166,4167,4168,4169,4170,4171, 54,4172,4173,4174,4175,4176,4177,4178,4179, +4180,4181,4182, 66,4183,4184, 69,4185,4186,4187,4188, 74, 75, 76, 77, 78, +4189,4190,4191, 82,4192,4193, 85,4194,4195, 88,4196,4197,4198,4199,4200,4201, + 95, 96, 97,4202,4203,4204,4205, 102, 103,4206, 105, 106, 107,4207,4208,4209, +4210, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,4211,4212,4213, 125,4214, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3287,3288, 466,3289, 713,3196,3290, 467,3291, 632,3292,3293,3294,3295,4087,3297, -3298, 598,4088,3300,3301,3302,3303,3304,4089,4090,4091,4092, 759, 437, 438,3307, -3308,3309, 439,3310, 440,4093, 442,4094,3312,3313,3314,3315,2306,4095,3317, 582, -3318,3319, 452,3320,3321,4096,3322,3323,3324, 778,3325,2312, 779,3326,3327,3328, -3329, 769,3330,3331,3332,3333, 663, 664,3334,2316,3335,3336,3337,1299,3338,3339, -3340,3341,4097,3343,3344,3345,4098, 442,3347,3348,3349,3304,3350,1669,3351,3352, -3353,3354,3355,3356,4099, 261,3357,3358,1919,2420,3359,3360, 676,3361,3362,3363, -3364,4100,3366,3367,4101,4102, 680,3369,3370, 462,3371, 455,3372,2421,3373, 465, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806,3644,3645,4103,3649,3648,4104,4105,4106,4107,4108,4109,4110,4111, -4112,3658,4113,3660,3658,3661,3662,3658,3663,3658,3664,2617,3666,3667,3668,3669, -3670,3671,3672,3673,3674, 160,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, -3686,3687, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, +3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,4215,3425, +3426, 598,4216,3428,3429,3430,3431,3432,4217,4218,4219,4220, 759, 437, 438,3435, +3436,3437, 439,3438, 440,4221, 442,4222,3440,3441,3442,3443,2306,4223,3445, 582, +3446,3447, 452,3448,3449,4224,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, +3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, +3468,3469,4225,3471,3472,3473,4226, 442,3475,3476,3477,3432,3478,1669,3479,3480, +3481,3482,3483,3484,4227, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, +3492,4228,3494,3495,4229,4230, 680,3497,3498, 462,3499, 455,3500,2421,3501, 465, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,4231,3777,3776,4232,4233,4234,4235,4236,4237,4238,4239, +4240,3786,4241,3788,3786,3789,3790,3786,3791,3786,3792,2617,3794,3795,3796,3797, +3798,3799,3800,3801,3802, 160,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { 622, 179, 371, 180, 372, 373, 374, 375, 376, 377, 186, 378, 187, 379, 380, 190, @@ -10296,106 +10666,106 @@ const unsigned short banks[74][256] = 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127,4114,4115,4116, 141,4117,4118,4119,4118,4120,4118,4121,4122, -4123,1954,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133,4134,4135,4136,4137, -4138,4139,4140,4141,4142,4143, 163,4144, 625,4145,4146,4147,4148,4149,4150,4151, -4152,4153,4154,4155,4156,4157,4158, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127,4242,4243,4244, 141,4245,4246,4247,4246,4248,4246,4249,4250, +4251,1954,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, +4266,4267,4268,4269,4270,4271, 163,4272, 625,4273,4274,4275,4276,4277,4278,4279, +4280,4281,4282,4283,4284,4285,4286, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, }, { -4159,4160,1553,1554, 181,4161,4162,1555, 185,4163,3497,4164,1559, 188,4165,4166, -4167,1562,4168,4169,1565, 195,4170, 197,4171,4172, 200, 201, 28,1566,1567,4173, -4174,4175,4176,4177,4178,4179,4180,4181,1572,1573, 214, 215, 216, 217, 218, 219, - 220,4182,4183,1575,1576,1577,1578,4184,1579,1580,4185,1581,4186,4187,4188,4189, - 232, 233, 234, 235,4190, 237, 238,4191,1584,1585, 240,1586, 242, 243, 244,1587, - 246,4192,4193,1589, 249, 250, 251,1590, 252,4194, 254, 255,4195,4196,4197,4198, -1595,4199,1596,1597,1598,1599,1600,1601,4200,4201,4202, 267,4203,4204, 270,4205, -4206,4207, 273,1603,4208,1605, 276,4209,4210,4211,4212,1610,4213,4214,4215, 126, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806,4216,1470,1613,4217,1615,4218,1475,4219,1477,4220,1479,4221,1481, -1482,4222,1484,4223,4224,4225,1630,4226,2291,4227,1634,3514,1636,1637,1638,1639, -1640,4228,4229,1643,1644,1645,1646,4230, 342,4231,4232,1651,1652,1653,4233,4234, -1656,4235,1658,1659,1660,1661,1662,1663, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, +4287,4288,1553,1554, 181,4289,4290,1555, 185,4291,3625,4292,1559, 188,4293,4294, +4295,1562,4296,4297,1565, 195,4298, 197,4299,4300, 200, 201, 28,1566,1567,4301, +4302,4303,4304,4305,4306,4307,4308,4309,1572,1573, 214, 215, 216, 217, 218, 219, + 220,4310,4311,1575,1576,1577,1578,4312,1579,1580,4313,1581,4314,4315,4316,4317, + 232, 233, 234, 235,4318, 237, 238,4319,1584,1585, 240,1586, 242, 243, 244,1587, + 246,4320,4321,1589, 249, 250, 251,1590, 252,4322, 254, 255,4323,4324,4325,4326, +1595,4327,1596,1597,1598,1599,1600,1601,4328,4329,4330, 267,4331,4332, 270,4333, +4334,4335, 273,1603,4336,1605, 276,4337,4338,4339,4340,1610,4341,4342,4343, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4344,1470,1613,4345,1615,4346,1475,4347,1477,4348,1479,4349,1481, +1482,4350,1484,4351,4352,4353,1630,4354,2291,4355,1634,3642,1636,1637,1638,1639, +1640,4356,4357,1643,1644,1645,1646,4358, 342,4359,4360,1651,1652,1653,4361,4362, +1656,4363,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3643, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, + 295, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871,2216, 873, 874, 875, 876, 877, 878,2217, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894,2218, 896, 897, 898, 899, 900,2219,2220, 903,2221, 905,2222,2223, 2224, 909,2225, 911, 912,2226,2227, 915, 916,2228,2229, 919, 920, 921, 922, 923, -2230, 925, 926, 927, 928, 929,4236, 931, 932, 933, 934, 935,2232,2233, 938,2234, - 940,4237, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, - 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,4238, 968,4239,3643,3643, -4240,4241,4242,4243,4244,4245,4246, 978,4247,4248,4249,4250,4251,4252,4253,4254, -4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, 975, 971, 972, 973, 974, +2230, 925, 926, 927, 928, 929, 295, 931, 932, 933, 934, 935,2232,2233, 938,2234, + 940, 295, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, + 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,4366, 968,4367, 295, 295, +4368,4369,4370,4371,4372,4373,4374, 978,4375,4376,4377,4378,4379,4380,4381,4382, +4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393, 975, 971, 972, 973, 974, 975, 310, 976, 977, 978, 974,2239, 980,2240, 982,2241, 984,2242, 985,2243, 987, 988,2244, 990, 325,2244, 325, 991,2244,2245,2246, 330, 993,2247,2248,2249, 997, 998,2250,2251,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, -2252, 429,1003, 430,1013,1014, 974,1015,4266,4266,4266,4266,4266,4266,4266,4266, -4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266, -4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266,4266, +2252, 429,1003, 430,1013,1014, 974,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3287,4267,3196,3196,4268,3196,3196,3196,3196,3196,3196, 378,3196,3196,3694,3196, -3196,4269,3196,4270,4271,4272,3196,3196,3196, 370,3196,4273,4274,4275,4276,4277, -3196,4278,4279,4280,3310,3310,4281, 442,4282,3196,3196,4283,4284,4283, 676,4285, -4286,3196,3196,4287, 51,3196,3196,3196,4288,3324,4289,3196, 59,4290,4288, 62, -3196,4291,4291,4292, 67,3196, 664,4293,3196,2316,3196,4294, 70,3196,4295, 78, -4296,4297, 81,3196,3196,3196,3196,3196,3196,3196,3196, 90,3196,4298, 93,4299, -3196,3196,3196,3196,3196,3196,3196,4300,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,3196,3196,4101,3196,4301,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,4302,4302,4303,4304,4305,4306,4307,4308,4309,4310,4311,4312,4313, -4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,3196,3196,4326, 154, -4327,4328,4329,3196,3196, 160,3196,3196,3196,4330,4331,4332,4333,4334,4335,3196, -3196,4336, 160,4337,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, -3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, +3415,4395,3324,3324,4396,3324,3324,3324,3324,3324,3324, 378,3324,3324,3822,3324, +3324,4397,3324,4398,4399,4400,3324,3324,3324, 370,3324,4401,4402,4403,4404,4405, +3324,4406,4407,4408,3438,3438,4409, 442,4410,3324,3324,4411,4412,4411, 676,4413, +4414,3324,3324,4415, 51,3324,3324,3324,4416,3452,4417,3324, 59,4418,4416, 62, +3324,4419,4419,4420, 67,3324, 664,4421,3324,2316,3324,4422, 70,3324,4423, 78, +4424,4425, 81,3324,3324,3324,3324,3324,3324,3324,3324, 90,3324,4426, 93,4427, +3324,3324,3324,3324,3324,3324,3324,4428,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,4229,3324,4429,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,4430,4430,4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441, +4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,3324,3324,4454, 154, +4455,4456,4457,3324,3324, 160,3324,3324,3324,4458,4459,4460,4461,4462,4463,3324, +3324,4464, 160,4465,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, }, { -3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, -3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831, -3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847, -3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862, -3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, -3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, -3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909, -3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,4338,3921,3922,3923,3924,3925, -3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976, -3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3976,3977,3977,3977,3977,3977, -3977,3977,3977,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939, -3940,3941,3942,3660,3943,3944,3662,3945,3663,3946,3947,3665,3948,3949,3950,3951, -3952,3953,3954,3955,3956,3936,3676,3957,3958,3679,3959,3960,3961,3962,3684,3685, -3963,3964,3965,3977,3977,3977,3977,3977,3977,3976,3985,3985,3985,3985,3985,3985, -3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985, -3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985,3985, +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, +4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4466,4049,4050,4051,4052,4053, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4105, +4105,4105,4105,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, +4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, }, { -4339,4340,4341,4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354, -4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370, -4371,4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386, -4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402, -4403,4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418, -4419,4420,4421,4422,4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,4434, -4435,4436,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448,4449,4450, -4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461,4462,4463,4464,4465,4466, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806,4467,4468,4469,4470,4471, -4472,4473,4474,4475,4476,4477,4217,4478,4218,4479,4480,4481,4482,4483,4221,4484, -4485,4222,4486,4487,4224,4488,4489,4226,4490,4227,4491,4492,4493,4494,4495,4496, -4497,4498,4499,4500,4501, 320,4502,4503,4504,4505,4506,4507,4508,1374,4509,4510, -4511,4512,4513,4514,4515,4516,4517,4518, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, +4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481,4482, +4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,4494,4495,4496,4497,4498, +4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4514, +4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530, +4531,4532,4533,4534,4535,4536,4537,4538,4539,4540,4541,4542,4543,4544,4545,4546, +4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562, +4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578, +4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,4595,4596,4597,4598,4599, +4600,4601,4602,4603,4604,4605,4606,4607,4346,4608,4609,4610,4611,4612,4349,4613, +4614,4615,4616,4617,4352,4618,4619,4354,4620,4621,4622,4623,4624,4625,4626,4627, +4628,4629,4630,4631,4632, 320,4633,4634,4635,4636,4637,4638,4639,1374,4640,4641, +4642,4643,4644,4645,4646,4647,4648,4649, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -4519,4520,4521,4522,4520,4523,4524,4525,4526,4527,4528,4530,4531,4532,4533,4534, -4535,4537,4539,4532,4541,4542,4543,4544,4545,4546,4547, 295, 28, 29, 30, 31, +4650,4651,4652,4653,4651,4654,4655,4656,4657,4658,4659,4661,4662,4663,4664,4665, +4666,4668,4670,4663,4672,4673,4674,4675,4676,4677,4678, 295, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, @@ -10404,16 +10774,34 @@ const unsigned short banks[74][256] = 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,4536, 128,4538, 130, 131, 132,4540, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144,4529, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 295, 295, 295, 127,4667, 128,4669, 130, 131, 132,4671, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144,4660, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,3423,4689,4693, +3426,4694,4695,3426,4696,4697,4697,4697, 868, 869, 870,4698,4699,4700,4701,4702, +4703,4704,4705,4705,4706,4703,4707,4708,3970,4709,4710,4710,4711,4712,4713,4714, +4715,4716,4717,4717,4718,4224,3450,4719,4720,4721,4722,4723,4724,4720,4720,4725, +3457, 769,3458,3459,4726,4727,4728,4729,4730,4731,4732,4733,4734,4731,1300,4735, +4736,4737,4731,4696,4738,4720,4739,4740,3475,4741,4742,4743,4744,1314,4745,4746, +4747,4748,4749,4742,4742,1322,4750,4751,4752,4753,4754,4753,3423,4755,3970,4756, +4757,1334,4757,4758,4759,4760, 792,4761,1341,1342,1343,1344,4762,4763,4764,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4765,4765,4766,4767,4766,4768,4769,4770,4771,4772,4773,4774,4775, +4776,4777,4778,4779,4780,4781,4782,4783,4784,4777,4785,4786,4787,4788,4789,4790, +4791,4792,4793,4794,4795,4796,4797,4794,4795,4796,4798,4799,4800,4801,1375, 295, +4802,4803, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, }; -const AdlBankSetup adlbanksetup[74] = +const AdlBankSetup adlbanksetup[75] = { {0, 1, 1, 0, 0}, //Bank 0, AIL (Star Control 3, Albion, Empire 2, etc.) {0, 1, 1, 0, 0}, //Bank 1, Bisqwit (selection of 4op and 2op) @@ -10482,11 +10870,12 @@ const AdlBankSetup adlbanksetup[74] = {2, 0, 0, 0, 0}, //Bank 64, DMX (Raptor) {3, 0, 0, 0, 0}, //Bank 65, OP3 (Modded GMOPL by Wohlstand) {3, 0, 0, 0, 0}, //Bank 66, SB (Jammey O'Connel's bank) - {3, 0, 0, 0, 0}, //Bank 67, TMB (Default bank of Build Engine) + {3, 0, 0, 0, 0}, //Bank 67, TMB (Default bank of Apgee Sound System) {0, 1, 1, 0, 0}, //Bank 68, WOPL (4op bank by James Alan Nguyen and Wohlstand) {3, 0, 0, 0, 0}, //Bank 69, TMB (Blood) {3, 0, 0, 0, 0}, //Bank 70, TMB (Lee) {3, 0, 0, 0, 0}, //Bank 71, TMB (Nam) - {1, 0, 0, 0, 0}, //Bank 72, WOPL (DMXOPL3 bank by Sneakernets) - {1, 0, 0, 0, 0} //Bank 73, EA (Cartooners) + {0, 0, 0, 0, 0}, //Bank 72, WOPL (DMXOPL3 bank by Sneakernets) + {1, 0, 0, 0, 0}, //Bank 73, EA (Cartooners) + {0, 0, 1, 0, 0} //Bank 74, WOPL (Apogee IMF 90-ish) }; diff --git a/src/sound/adlmidi/adldata.hh b/src/sound/adlmidi/adldata.hh index e9ff00b25..104db82cd 100644 --- a/src/sound/adlmidi/adldata.hh +++ b/src/sound/adlmidi/adldata.hh @@ -24,8 +24,16 @@ #ifndef ADLDATA_H #define ADLDATA_H +#include #include +#pragma pack(push, 1) +#define ADLDATA_BYTE_COMPARABLE(T) \ + inline bool operator==(const T &a, const T &b) \ + { return !memcmp(&a, &b, sizeof(T)); } \ + inline bool operator!=(const T &a, const T &b) \ + { return !operator==(a, b); } + extern const struct adldata { uint32_t modulator_E862, carrier_E862; // See below @@ -34,22 +42,46 @@ extern const struct adldata int8_t finetune; } adl[]; +ADLDATA_BYTE_COMPARABLE(struct adldata) +enum { adlDefaultNumber = 189 }; extern const struct adlinsdata { - enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02 }; + enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 }; uint16_t adlno1, adlno2; uint8_t tone; uint8_t flags; uint16_t ms_sound_kon; // Number of milliseconds it produces sound; uint16_t ms_sound_koff; - double voice2_fine_tune; + double voice2_fine_tune; } adlins[]; +ADLDATA_BYTE_COMPARABLE(struct adlinsdata) int maxAdlBanks(); extern const unsigned short banks[][256]; extern const char* const banknames[]; +enum { adlNoteOnMaxTime = 40000 }; + +/** + * @brief Instrument data with operators included + */ +struct adlinsdata2 +{ + adldata adl[2]; + uint8_t tone; + uint8_t flags; + uint16_t ms_sound_kon; // Number of milliseconds it produces sound; + uint16_t ms_sound_koff; + double voice2_fine_tune; + adlinsdata2() {} + explicit adlinsdata2(const adlinsdata &d); +}; +ADLDATA_BYTE_COMPARABLE(struct adlinsdata2) + +#undef ADLDATA_BYTE_COMPARABLE +#pragma pack(pop) + /** * @brief Bank global setup */ @@ -62,4 +94,26 @@ extern const struct AdlBankSetup bool scaleModulators; } adlbanksetup[]; +/** + * @brief Conversion of storage formats + */ +inline adlinsdata2::adlinsdata2(const adlinsdata &d) + : tone(d.tone), flags(d.flags), + ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), + voice2_fine_tune(d.voice2_fine_tune) +{ + adl[0] = ::adl[d.adlno1]; + adl[1] = ::adl[d.adlno2]; +} + +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_ADLI_to_FMIns(adlinsdata2 &dst, const struct ADL_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const adlinsdata2 &src); + #endif //ADLDATA_H diff --git a/src/sound/adlmidi/adlmidi.cpp b/src/sound/adlmidi/adlmidi.cpp index db97447a2..9210b5be3 100644 --- a/src/sound/adlmidi/adlmidi.cpp +++ b/src/sound/adlmidi/adlmidi.cpp @@ -37,6 +37,13 @@ static ADL_Version adl_version = { ADLMIDI_VERSION_PATCHLEVEL }; +static const ADLMIDI_AudioFormat adl_DefaultAudioFormat = +{ + ADLMIDI_SampleType_S16, + sizeof(int16_t), + 2 * sizeof(int16_t), +}; + /*---------------------------EXPORTS---------------------------*/ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) @@ -67,7 +74,12 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards) return -2; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); +#ifdef ADLMIDI_HW_OPL + (void)numCards; + play->m_setup.NumCards = 1; +#else play->m_setup.NumCards = static_cast(numCards); +#endif if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards) { play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n"); @@ -108,7 +120,7 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) if(static_cast(bankno) >= NumBanks) { char errBuf[150]; - snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", (NumBanks - 1)); + snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", static_cast(NumBanks - 1)); play->setErrorString(errBuf); return -1; } @@ -131,6 +143,142 @@ ADLMIDI_EXPORT const char *const *adl_getBankNames() return banknames; } +ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + map.reserve(banks); + return (int)map.capacity(); +} + +ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + ADL_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + unsigned idnumber = (id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0); + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it; + if(!(flags & ADLMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = adlinsdata::Flag_NoSound; + + std::pair ir; + if(flags & ADLMIDI_Bank_CreateRt) + { + ir = map.insert(value, OPL3::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id) +{ + if(!device || !bank) + return -1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + unsigned idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0; + return 0; +} + +ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device || !bank) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_ADLI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + if(ins->version != 0) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_ADLI_to_FMIns(it->second.ins[index], *ins); + return 0; +} + ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { if(!device) @@ -165,7 +313,9 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.AdlPercussionMode = percmod; - play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode; + play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode < 0 ? + play->opl.dynamic_bank_setup.adLibPercussions : + (play->m_setup.AdlPercussionMode != 0); play->opl.updateFlags(); } @@ -174,7 +324,9 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.HighVibratoMode = hvibro; - play->opl.HighVibratoMode = play->m_setup.HighVibratoMode; + play->opl.HighVibratoMode = play->m_setup.HighVibratoMode < 0 ? + play->opl.dynamic_bank_setup.deepVibrato : + (play->m_setup.HighVibratoMode != 0); play->opl.updateDeepFlags(); } @@ -183,7 +335,9 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.HighTremoloMode = htremo; - play->opl.HighTremoloMode = play->m_setup.HighTremoloMode; + play->opl.HighTremoloMode = play->m_setup.HighTremoloMode < 0 ? + play->opl.dynamic_bank_setup.deepTremolo : + (play->m_setup.HighTremoloMode != 0); play->opl.updateDeepFlags(); } @@ -192,7 +346,16 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.ScaleModulators = smod; - play->opl.ScaleModulators = play->m_setup.ScaleModulators; + play->opl.ScaleModulators = play->m_setup.ScaleModulators < 0 ? + play->opl.dynamic_bank_setup.scaleModulators : + (play->m_setup.ScaleModulators != 0); +} + +ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness) +{ + if(!device) return; + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); } ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) @@ -202,12 +365,16 @@ ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) play->m_setup.loopingIsEnabled = (loopEn != 0); } +/* !!!DEPRECATED!!! */ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) { if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.LogarithmicVolumes = logvol; - play->opl.LogarithmicVolumes = play->m_setup.LogarithmicVolumes; + play->m_setup.LogarithmicVolumes = (logvol != 0); + if(play->m_setup.LogarithmicVolumes) + play->opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); + else + play->opl.ChangeVolumeRangesModel(static_cast(play->opl.m_volumeScale)); } ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) @@ -215,7 +382,10 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v if(!device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.VolumeModel = volumeModel; - play->opl.ChangeVolumeRangesModel(static_cast(volumeModel)); + if(play->m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + play->opl.m_volumeScale = (OPL3::VolumesScale)play->opl.dynamic_bank_setup.volumeModel; + else + play->opl.ChangeVolumeRangesModel(static_cast(volumeModel)); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) @@ -312,16 +482,64 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigne ADLMIDI_EXPORT const char *adl_emulatorName() { - #ifdef ADLMIDI_USE_DOSBOX_OPL - return "DosBox"; - #else - return "Nuked"; - #endif + return ""; } +ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) +{ + if(device) + { + #ifndef ADLMIDI_HW_OPL + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + if(play && !play->opl.cardsOP2.empty()) + return play->opl.cardsOP2[0]->emulatorName(); + #else + return "Hardware OPL3 chip on 0x330"; + #endif + } + return "Unknown"; +} + +ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator) +{ + if(device) + { + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + if(play && (emulator >= 0) && (emulator < ADLMIDI_EMU_end)) + { + play->m_setup.emulator = emulator; + adl_reset(device); + return 0; + } + play->setErrorString("OPL3 MIDI: Unknown emulation core!"); + } + return -1; +} + + +ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled) +{ + if(device) + { + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + if(play) + { + play->m_setup.runAtPcmRate = (enabled != 0); + adl_reset(device); + return 0; + } + } + return -1; +} + + ADLMIDI_EXPORT const char *adl_linkedLibraryVersion() { +#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER) return ADLMIDI_VERSION; +#else + return ADLMIDI_VERSION " (HQ)"; +#endif } ADLMIDI_EXPORT const ADL_Version *adl_linkedVersion() @@ -373,9 +591,10 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) return; MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->m_setup.tick_skip_samples_delay = 0; - play->opl.Reset(play->m_setup.PCM_RATE); + play->opl.runAtPcmRate = play->m_setup.runAtPcmRate; + play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play); play->ch.clear(); - play->ch.resize(play->opl.NumChannels); + play->ch.resize((size_t)play->opl.NumChannels); } ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) @@ -568,31 +787,156 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D play->hooks.onDebugMessage_userData = userData; } - - -inline static void SendStereoAudio(int &samples_requested, - ssize_t &in_size, - short *_in, - ssize_t out_pos, - short *_out) +#ifndef ADLMIDI_HW_OPL +template +static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) { - if(!in_size) - return; - size_t offset = static_cast(out_pos); - size_t inSamples = static_cast(in_size * 2); - size_t maxSamples = static_cast(samples_requested) - offset; - size_t toCopy = std::min(maxSamples, inSamples); - std::memcpy(_out + out_pos, _in, toCopy * sizeof(short)); + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i]; + *(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1]; + } } +template +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = (Dst)transform(src[2 * i]); + *(Dst *)(dstRight + (i * sampleOffset)) = (Dst)transform(src[(2 * i) + 1]); + } +} -ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + typedef int32_t(&pfnConvert)(int32_t); + typedef float(&ffnConvert)(int32_t); + typedef double(&dfnConvert)(int32_t); + + switch(sampleType) { + case ADLMIDI_SampleType_S8: + case ADLMIDI_SampleType_U8: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S8) ? adl_cvtS8 : adl_cvtU8; + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S16: + case ADLMIDI_SampleType_U16: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S16) ? adl_cvtS16 : adl_cvtU16; + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesRaw(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S24: + case ADLMIDI_SampleType_U24: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S24) ? adl_cvtS24 : adl_cvtU24; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S32: + case ADLMIDI_SampleType_U32: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S32) ? adl_cvtS32 : adl_cvtU32; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_F32: + { + if(containerSize != sizeof(float)) + return -1; + ffnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + case ADLMIDI_SampleType_F64: + { + if(containerSize != sizeof(double)) + return -1; + dfnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + default: + return -1; + } + + return 0; +} +#endif + +ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) { #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER #ifdef ADLMIDI_HW_OPL (void)device; (void)sampleCount; - (void)out; + (void)out_left; + (void)out_right; + (void)format; return 0; #else sampleCount -= sampleCount % 2; //Avoid even sample requests @@ -646,33 +990,23 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opl.NumCards; if(chips == 1) { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo); - in_generatedPhys = in_generatedStereo * 2; - #else - OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast(in_generatedStereo)); - #endif + player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); } else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ - for(unsigned card = 0; card < chips; ++card) - { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo); - in_generatedPhys = in_generatedStereo * 2; - #else - OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast(in_generatedStereo)); - #endif - } + for(size_t card = 0; card < chips; ++card) + player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } + /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; @@ -698,11 +1032,20 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) { #ifdef ADLMIDI_HW_OPL (void)device; (void)sampleCount; - (void)out; + (void)out_left; + (void)out_right; + (void)format; return 0; #else sampleCount -= sampleCount % 2; //Avoid even sample requests @@ -739,33 +1082,20 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opl.NumCards; if(chips == 1) - { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo); - in_generatedPhys = in_generatedStereo * 2; - #else - OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast(in_generatedStereo)); - #endif - } + player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(unsigned card = 0; card < chips; ++card) - { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo); - in_generatedPhys = in_generatedStereo * 2; - #else - OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast(in_generatedStereo)); - #endif - } + player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; diff --git a/src/sound/adlmidi/adlmidi.h b/src/sound/adlmidi/adlmidi.h index 10fe4ddff..51cd968d0 100644 --- a/src/sound/adlmidi/adlmidi.h +++ b/src/sound/adlmidi/adlmidi.h @@ -30,13 +30,14 @@ extern "C" { #define ADLMIDI_VERSION_MAJOR 1 #define ADLMIDI_VERSION_MINOR 3 -#define ADLMIDI_VERSION_PATCHLEVEL 2 +#define ADLMIDI_VERSION_PATCHLEVEL 3 -#define ADLMIDI_TOSTR(s) #s +#define ADLMIDI_TOSTR_I(s) #s +#define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s) #define ADLMIDI_VERSION \ - ADLMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \ - ADLMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \ - ADLMIDI_TOSTR(OPNMIDI_VERSION_PATCHLEVEL) + ADLMIDI_TOSTR(ADLMIDI_VERSION_MAJOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL) #include @@ -57,11 +58,34 @@ typedef short ADL_SInt16; enum ADLMIDI_VolumeModels { ADLMIDI_VolumeModel_AUTO = 0, - ADLMIDI_VolumeModel_Generic, - ADLMIDI_VolumeModel_CMF, - ADLMIDI_VolumeModel_DMX, - ADLMIDI_VolumeModel_APOGEE, - ADLMIDI_VolumeModel_9X + ADLMIDI_VolumeModel_Generic = 1, + ADLMIDI_VolumeModel_NativeOPL3 = 2, + ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3, + ADLMIDI_VolumeModel_DMX = 3, + ADLMIDI_VolumeModel_APOGEE = 4, + ADLMIDI_VolumeModel_9X = 5 +}; + +enum ADLMIDI_SampleType +{ + ADLMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */ + ADLMIDI_SampleType_S8, /* signed PCM 8-bit */ + ADLMIDI_SampleType_F32, /* float 32-bit */ + ADLMIDI_SampleType_F64, /* float 64-bit */ + ADLMIDI_SampleType_S24, /* signed PCM 24-bit */ + ADLMIDI_SampleType_S32, /* signed PCM 32-bit */ + ADLMIDI_SampleType_U8, /* unsigned PCM 8-bit */ + ADLMIDI_SampleType_U16, /* unsigned PCM 16-bit */ + ADLMIDI_SampleType_U24, /* unsigned PCM 24-bit */ + ADLMIDI_SampleType_U32, /* unsigned PCM 32-bit */ + ADLMIDI_SampleType_Count, +}; + +struct ADLMIDI_AudioFormat +{ + enum ADLMIDI_SampleType type; /* type of sample */ + unsigned containerSize; /* size in bytes of the storage type */ + unsigned sampleOffset; /* distance in bytes between consecutive samples */ }; struct ADL_MIDIPlayer @@ -87,6 +111,46 @@ extern int adl_getBanksCount(); /* Returns pointer to array of names of every bank */ extern const char *const *adl_getBankNames(); +/* Reference to dynamic bank */ +typedef struct ADL_Bank +{ + void *pointer[3]; +} ADL_Bank; + +/* Identifier of dynamic bank */ +typedef struct ADL_BankId +{ + ADL_UInt8 percussive, msb, lsb; +} ADL_BankId; + +/* Flags for dynamic bank access */ +enum ADL_BankAccessFlags +{ + ADLMIDI_Bank_Create = 1, /* create bank, allocating memory as needed */ + ADLMIDI_Bank_CreateRt = 1|2, /* create bank, never allocating memory */ +}; + +typedef struct ADL_Instrument ADL_Instrument; + +#if defined(ADLMIDI_UNSTABLE_API) +/* Preallocates a minimum number of bank slots. Returns the actual capacity. */ +extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); +/* Gets the bank designated by the identifier, optionally creating if it does not exist. */ +extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); +/* Gets the identifier of a bank. */ +extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); +/* Removes a bank. */ +extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Gets the first bank. */ +extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Iterates to the next bank. */ +extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Gets the nth intrument in the bank [0..127]. */ +extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); +/* Sets the nth intrument in the bank [0..127]. */ +extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); +#endif /* defined(ADLMIDI_UNSTABLE_API) */ + /*Sets number of 4-operator channels between all chips. By default, it is automatically re-calculating every bank change. If you want to specify custom number of four operator channels, @@ -109,10 +173,16 @@ extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo); /*Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes*/ extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod); +/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling. + By default, brightness affects sound between 0 and 64. + When this option is enabled, the range will use a full range from 0 up to 127. +*/ +extern void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness); + /*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/ extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn); -/*Enable or disable Logariphmic volume changer */ +/* !!!DEPRECATED!!! Enable or disable Logariphmic volume changer */ extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol); /*Set different volume range model */ @@ -125,15 +195,33 @@ extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) extern int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); -/*Returns name of currently used OPL3 emulator*/ +/* DEPRECATED */ extern const char *adl_emulatorName(); +/*Returns chip emulator name string*/ +extern const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device); + +enum ADL_Emulator +{ + ADLMIDI_EMU_NUKED = 0, + ADLMIDI_EMU_NUKED_174, + ADLMIDI_EMU_DOSBOX, + ADLMIDI_EMU_end +}; + +/* Switch the emulation core */ +extern int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator); + + typedef struct { ADL_UInt16 major; ADL_UInt16 minor; ADL_UInt16 patch; } ADL_Version; +/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/ +extern int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled); + /*Returns string which contains a version number*/ extern const char *adl_linkedLibraryVersion(); @@ -213,11 +301,17 @@ extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size /*Take a sample buffer and iterate MIDI timers */ -extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); +extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/*Take a sample buffer and iterate MIDI timers */ +extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); /*Generate audio output from chip emulators without iteration of MIDI timers.*/ extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); +/*Generate audio output from chip emulators without iteration of MIDI timers.*/ +extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + /** * @brief Periodic tick handler. * @param device @@ -286,6 +380,70 @@ extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook /* Set debug message hook */ extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); + +/**Instrument structures**/ + +enum +{ + ADLMIDI_InstrumentVersion = 0, +}; + +typedef enum ADL_InstrumentFlags +{ + /* Is two-operator single-voice instrument (no flags) */ + ADLMIDI_Ins_2op = 0x00, + /* Is true four-operator instrument */ + ADLMIDI_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + ADLMIDI_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + ADLMIDI_Ins_IsBlank = 0x04, + /* Mask of the flags range */ + ADLMIDI_Ins_ALL_MASK = 0x07, +} ADL_InstrumentFlags; + +typedef struct ADL_Operator +{ + /* AM/Vib/Env/Ksr/FMult characteristics */ + ADL_UInt8 avekf_20; + /* Key Scale Level / Total level register data */ + ADL_UInt8 ksl_l_40; + /* Attack / Decay */ + ADL_UInt8 atdec_60; + /* Systain and Release register data */ + ADL_UInt8 susrel_80; + /* Wave form */ + ADL_UInt8 waveform_E0; +} ADL_Operator; + +typedef struct ADL_Instrument +{ + /* Version of the instrument object */ + int version; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + ADL_SInt16 note_offset1; + /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + ADL_SInt16 note_offset2; + /* MIDI note velocity offset (taken from Apogee TMB format) */ + ADL_SInt8 midi_velocity_offset; + /* Second voice detune level (taken from DMX OP2) */ + ADL_SInt8 second_voice_detune; + /* Percussion MIDI base tone number at which this drum will be played */ + ADL_UInt8 percussion_key_number; + /* Enum ADL_InstrumentFlags */ + ADL_UInt8 inst_flags; + /* Feedback&Connection register for first and second operators */ + ADL_UInt8 fb_conn1_C0; + /* Feedback&Connection register for third and fourth operators */ + ADL_UInt8 fb_conn2_C0; + /* Operators register data */ + ADL_Operator operators[4]; + /* Millisecond delay of sounding while key is on */ + ADL_UInt16 delay_on_ms; + /* Millisecond delay of sounding after key off */ + ADL_UInt16 delay_off_ms; +} ADL_Instrument; + #ifdef __cplusplus } #endif diff --git a/src/sound/adlmidi/adlmidi.hpp b/src/sound/adlmidi/adlmidi.hpp index 63f2abd48..6d01b8d33 100644 --- a/src/sound/adlmidi/adlmidi.hpp +++ b/src/sound/adlmidi/adlmidi.hpp @@ -24,24 +24,15 @@ #ifndef ADLMIDI_HPP #define ADLMIDI_HPP -#include "adlmidi.h" - -#include -#include - -class OPL3; -class MIDIplay; +struct ADL_MIDIPlayer; class AdlInstrumentTester { - uint32_t cur_gm; - uint32_t ins_idx; - std::vector adl_ins_list; - OPL3 *opl; - MIDIplay * play; + struct Impl; + Impl *P; public: - AdlInstrumentTester(ADL_MIDIPlayer *device); + explicit AdlInstrumentTester(ADL_MIDIPlayer *device); virtual ~AdlInstrumentTester(); // Find list of adlib instruments that supposedly implement this GM @@ -51,6 +42,10 @@ public: void NextGM(int offset); void NextAdl(int offset); bool HandleInputChar(char ch); + +private: + AdlInstrumentTester(const AdlInstrumentTester &); + AdlInstrumentTester &operator=(const AdlInstrumentTester &); }; #endif //ADLMIDI_HPP diff --git a/src/sound/adlmidi/adlmidi_bankmap.h b/src/sound/adlmidi/adlmidi_bankmap.h new file mode 100644 index 000000000..e4534cd6e --- /dev/null +++ b/src/sound/adlmidi/adlmidi_bankmap.h @@ -0,0 +1,127 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_BANKMAP_H +#define ADLMIDI_BANKMAP_H + +#include +#include +#include +#include + +#include "adlmidi_ptr.hpp" + +/** + * A simple hash map which accepts bank numbers as keys, can be reserved to a + * fixed size, offers O(1) search and insertion, has a hash function to + * optimize for the worst case, and has some good cache locality properties. + */ +template +class BasicBankMap +{ +public: + typedef uint16_t key_type; /* the bank identifier */ + typedef T mapped_type; + typedef std::pair value_type; + + BasicBankMap(); + void reserve(size_t capacity); + + size_t size() const + { return m_size; } + size_t capacity() const + { return m_capacity; } + bool empty() const + { return m_size == 0; } + + class iterator; + iterator begin() const; + iterator end() const; + + struct do_not_expand_t {}; + + iterator find(key_type key); + void erase(iterator it); + std::pair insert(const value_type &value); + std::pair insert(const value_type &value, do_not_expand_t); + void clear(); + + T &operator[](key_type key); + +private: + struct Slot; + enum { minimum_allocation = 4 }; + enum + { + hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ + hash_buckets = 1 << hash_bits, + }; + +public: + class iterator + { + public: + iterator(); + value_type &operator*() const { return slot->value; } + value_type *operator->() const { return &slot->value; } + iterator &operator++(); + bool operator==(const iterator &o) const; + bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); + private: + Slot **buckets; + Slot *slot; + size_t index; + iterator(Slot **buckets, Slot *slot, size_t index); +#ifdef _MSC_VER + template + friend class BasicBankMap; +#else + friend class BasicBankMap; +#endif + }; + +private: + struct Slot { + Slot *next, *prev; + value_type value; + Slot() : next(NULL), prev(NULL) {} + }; + AdlMIDI_SPtrArray m_buckets; + std::list< AdlMIDI_SPtrArray > m_allocations; + Slot *m_freeslots; + size_t m_size; + size_t m_capacity; + static size_t hash(key_type key); + Slot *allocate_slot(); + Slot *ensure_allocate_slot(); + void free_slot(Slot *slot); + Slot *bucket_find(size_t index, key_type key); + void bucket_add(size_t index, Slot *slot); + void bucket_remove(size_t index, Slot *slot); +}; + +#include "adlmidi_bankmap.tcc" + +#endif // ADLMIDI_BANKMAP_H diff --git a/src/sound/adlmidi/adlmidi_bankmap.tcc b/src/sound/adlmidi/adlmidi_bankmap.tcc new file mode 100644 index 000000000..76e70016c --- /dev/null +++ b/src/sound/adlmidi/adlmidi_bankmap.tcc @@ -0,0 +1,283 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_bankmap.h" +#include + +template +inline BasicBankMap::BasicBankMap() + : m_freeslots(NULL), + m_size(0), + m_capacity(0) +{ + m_buckets.reset(new Slot *[hash_buckets]()); +} + +template +inline size_t BasicBankMap::hash(key_type key) +{ + // disregard the 0 high bit in LSB + key = (key & 127) | ((key >> 8) << 7); + // take low part as hash value + return key & (hash_buckets - 1); +} + +template +void BasicBankMap::reserve(size_t capacity) +{ + if(m_capacity >= capacity) + return; + + size_t need = capacity - m_capacity; + const size_t minalloc = static_cast(minimum_allocation); + need = (need < minalloc) ? minalloc : need; + + AdlMIDI_SPtrArray slotz; + slotz.reset(new Slot[need]); + m_allocations.push_back(slotz); + m_capacity += need; + + for(size_t i = need; i-- > 0;) + free_slot(&slotz[i]); +} + +template +typename BasicBankMap::iterator +BasicBankMap::begin() const +{ + iterator it(m_buckets.get(), NULL, 0); + while(it.index < hash_buckets && !(it.slot = m_buckets[it.index])) + ++it.index; + return it; +} + +template +typename BasicBankMap::iterator +BasicBankMap::end() const +{ + iterator it(m_buckets.get(), NULL, hash_buckets); + return it; +} + +template +typename BasicBankMap::iterator BasicBankMap::find(key_type key) +{ + size_t index = hash(key); + Slot *slot = bucket_find(index, key); + if(!slot) + return end(); + return iterator(m_buckets.get(), slot, index); +} + +template +void BasicBankMap::erase(iterator it) +{ + bucket_remove(it.index, it.slot); + free_slot(it.slot); + --m_size; +} + +template +inline BasicBankMap::iterator::iterator() + : buckets(NULL), slot(NULL), index(0) +{ +} + +template +inline BasicBankMap::iterator::iterator(Slot **buckets, Slot *slot, size_t index) + : buckets(buckets), slot(slot), index(index) +{ +} + +template +typename BasicBankMap::iterator & +BasicBankMap::iterator::operator++() +{ + if(slot->next) + slot = slot->next; + else { + Slot *slot = NULL; + ++index; + while(index < hash_buckets && !(slot = buckets[index])) + ++index; + this->slot = slot; + } + return *this; +} + +template +bool BasicBankMap::iterator::operator==(const iterator &o) const +{ + return buckets == o.buckets && slot == o.slot && index == o.index; +} + +template +inline bool BasicBankMap::iterator::operator!=(const iterator &o) const +{ + return !operator==(o); +} + +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) { + reserve(m_capacity + minimum_allocation); + slot = ensure_allocate_slot(); + } + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value, do_not_expand_t) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) + return std::make_pair(end(), false); + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +void BasicBankMap::clear() +{ + for(size_t i = 0; i < hash_buckets; ++i) { + Slot *slot = m_buckets[i]; + while (Slot *cur = slot) { + slot = slot->next; + free_slot(cur); + } + m_buckets[i] = NULL; + } + m_size = 0; +} + +template +inline T &BasicBankMap::operator[](key_type key) +{ + return insert(value_type(key, T())).first->second; +} + +template +typename BasicBankMap::Slot * +BasicBankMap::allocate_slot() +{ + Slot *slot = m_freeslots; + if(!slot) + return NULL; + Slot *next = slot->next; + if(next) + next->prev = NULL; + m_freeslots = next; + return slot; +} + +template +inline typename BasicBankMap::Slot * +BasicBankMap::ensure_allocate_slot() +{ + Slot *slot = allocate_slot(); + assert(slot); + return slot; +} + +template +void BasicBankMap::free_slot(Slot *slot) +{ + Slot *next = m_freeslots; + if(next) + next->prev = slot; + slot->prev = NULL; + slot->next = next; + m_freeslots = slot; + m_freeslots->value.second = T(); +} + +template +typename BasicBankMap::Slot * +BasicBankMap::bucket_find(size_t index, key_type key) +{ + Slot *slot = m_buckets[index]; + while(slot && slot->value.first != key) + slot = slot->next; + return slot; +} + +template +void BasicBankMap::bucket_add(size_t index, Slot *slot) +{ + assert(slot); + Slot *next = m_buckets[index]; + if(next) + next->prev = slot; + slot->next = next; + m_buckets[index] = slot; +} + +template +void BasicBankMap::bucket_remove(size_t index, Slot *slot) +{ + assert(slot); + Slot *prev = slot->prev; + Slot *next = slot->next; + if(!prev) + m_buckets[index] = next; + else + prev->next = next; + if(next) + next->prev = prev; +} diff --git a/src/sound/adlmidi/adlmidi_load.cpp b/src/sound/adlmidi/adlmidi_load.cpp index 00a4ce3de..7f69e259f 100644 --- a/src/sound/adlmidi/adlmidi_load.cpp +++ b/src/sound/adlmidi/adlmidi_load.cpp @@ -22,6 +22,7 @@ */ #include "adlmidi_private.hpp" +#include "wopl/wopl_file.h" #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER # ifndef ADLMIDI_DISABLE_MUS_SUPPORT @@ -32,6 +33,7 @@ # endif//XMI #endif //ADLMIDI_DISABLE_MIDI_SEQUENCER +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER uint64_t MIDIplay::ReadBEint(const void *buffer, size_t nbytes) { uint64_t result = 0; @@ -54,6 +56,8 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes) return result; } +#endif + bool MIDIplay::LoadBank(const std::string &filename) { fileReader file; @@ -68,282 +72,223 @@ bool MIDIplay::LoadBank(const void *data, size_t size) return LoadBank(file); } - - -/* WOPL-needed misc functions */ -static uint16_t toUint16LE(const uint8_t *arr) +template +static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) { - uint16_t num = arr[0]; - num |= ((arr[1] << 8) & 0xFF00); - return num; -} - -static uint16_t toUint16BE(const uint8_t *arr) -{ - uint16_t num = arr[1]; - num |= ((arr[0] << 8) & 0xFF00); - return num; -} - -static int16_t toSint16BE(const uint8_t *arr) -{ - int16_t num = *reinterpret_cast(&arr[0]); - num *= 1 << 8; - num |= arr[1]; - return num; -} - -static const char *wopl3_magic = "WOPL3-BANK\0"; -static const uint16_t wopl_latest_version = 3; - -#define WOPL_INST_SIZE_V2 62 -#define WOPL_INST_SIZE_V3 66 - -enum WOPL_InstrumentFlags -{ - WOPL_Flags_NONE = 0, - WOPL_Flag_Enable4OP = 0x01, - WOPL_Flag_Pseudo4OP = 0x02, - WOPL_Flag_NoSound = 0x04, -}; - -struct WOPL_Inst -{ - bool fourOps; - char padding[7]; - struct adlinsdata adlins; - struct adldata op[2]; - uint16_t ms_sound_kon; - uint16_t ms_sound_koff; -}; - -static bool readInstrument(MIDIplay::fileReader &file, WOPL_Inst &ins, uint16_t &version, bool isPercussion = false) -{ - uint8_t idata[WOPL_INST_SIZE_V3]; - if(version >= 3) - { - if(file.read(idata, 1, WOPL_INST_SIZE_V3) != WOPL_INST_SIZE_V3) - return false; - } - else - { - if(file.read(idata, 1, WOPL_INST_SIZE_V2) != WOPL_INST_SIZE_V2) - return false; - } - - //strncpy(ins.name, char_p(idata), 32); - ins.op[0].finetune = (int8_t)toSint16BE(idata + 32); - ins.op[1].finetune = (int8_t)toSint16BE(idata + 34); - //ins.velocity_offset = int8_t(idata[36]); - - ins.adlins.voice2_fine_tune = 0.0; - int8_t voice2_fine_tune = int8_t(idata[37]); + ins.voice2_fine_tune = 0.0; + int8_t voice2_fine_tune = in.second_voice_detune; if(voice2_fine_tune != 0) { if(voice2_fine_tune == 1) - ins.adlins.voice2_fine_tune = 0.000025; + ins.voice2_fine_tune = 0.000025; else if(voice2_fine_tune == -1) - ins.adlins.voice2_fine_tune = -0.000025; + ins.voice2_fine_tune = -0.000025; else - ins.adlins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0); + ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); } - ins.adlins.tone = isPercussion ? idata[38] : 0; + ins.tone = in.percussion_key_number; + ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0; - uint8_t flags = idata[39]; - ins.adlins.flags = (flags & WOPL_Flag_Enable4OP) && (flags & WOPL_Flag_Pseudo4OP) ? adlinsdata::Flag_Pseudo4op : 0; - ins.adlins.flags|= (flags & WOPL_Flag_NoSound) ? adlinsdata::Flag_NoSound : 0; - ins.fourOps = (flags & WOPL_Flag_Enable4OP) || (flags & WOPL_Flag_Pseudo4OP); - - ins.op[0].feedconn = (idata[40]); - ins.op[1].feedconn = (idata[41]); - - for(size_t op = 0, slt = 0; op < 4; op++, slt++) + bool fourOps = (in.inst_flags & WOPL_Ins_4op) || (in.inst_flags & WOPL_Ins_Pseudo4op); + for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) { - size_t off = 42 + size_t(op) * 5; - // ins.setAVEKM(op, idata[off + 0]);//AVEKM - // ins.setAtDec(op, idata[off + 2]);//AtDec - // ins.setSusRel(op, idata[off + 3]);//SusRel - // ins.setWaveForm(op, idata[off + 4]);//WaveForm - // ins.setKSLL(op, idata[off + 1]);//KSLL - ins.op[slt].carrier_E862 = - ((static_cast(idata[off + 4]) << 24) & 0xFF000000) //WaveForm - | ((static_cast(idata[off + 3]) << 16) & 0x00FF0000) //SusRel - | ((static_cast(idata[off + 2]) << 8) & 0x0000FF00) //AtDec - | ((static_cast(idata[off + 0]) << 0) & 0x000000FF); //AVEKM - ins.op[slt].carrier_40 = idata[off + 1];//KSLL + ins.adl[slt].carrier_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL op++; - off = 42 + size_t(op) * 5; - ins.op[slt].modulator_E862 = - ((static_cast(idata[off + 4]) << 24) & 0xFF000000) //WaveForm - | ((static_cast(idata[off + 3]) << 16) & 0x00FF0000) //SusRel - | ((static_cast(idata[off + 2]) << 8) & 0x0000FF00) //AtDec - | ((static_cast(idata[off + 0]) << 0) & 0x000000FF); //AVEKM - ins.op[slt].modulator_40 = idata[off + 1];//KSLL + ins.adl[slt].modulator_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL } - if(version >= 3) + ins.adl[0].finetune = static_cast(in.note_offset1); + ins.adl[0].feedconn = in.fb_conn1_C0; + if(!fourOps) + ins.adl[1] = ins.adl[0]; + else { - ins.ms_sound_kon = toUint16BE(idata + 62); - ins.ms_sound_koff = toUint16BE(idata + 64); + ins.adl[1].finetune = static_cast(in.note_offset2); + ins.adl[1].feedconn = in.fb_conn2_C0; + } + + ins.ms_sound_kon = in.delay_on_ms; + ins.ms_sound_koff = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in) +{ + ins.second_voice_detune = 0; + double voice2_fine_tune = in.voice2_fine_tune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025) + ins.second_voice_detune = 1; + else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025) + ins.second_voice_detune = -1; + else + { + long value = lround(voice2_fine_tune * (1000.0 / 15.625)); + value = (value < -128) ? -128 : value; + value = (value > +127) ? +127 : value; + ins.second_voice_detune = static_cast(value); + } + } + + ins.percussion_key_number = in.tone; + bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1]; + ins.inst_flags = fourOps ? WOPL_Ins_4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; + + for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) + { + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + + op++; + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + } + + ins.note_offset1 = in.adl[0].finetune; + ins.fb_conn1_C0 = in.adl[0].feedconn; + if(!fourOps) + { + ins.operators[2] = ins.operators[0]; + ins.operators[3] = ins.operators[1]; } else { - ins.ms_sound_kon = 1000; - ins.ms_sound_koff = 500; + ins.note_offset2 = in.adl[1].finetune; + ins.fb_conn2_C0 = in.adl[1].feedconn; } - return true; + ins.delay_on_ms = in.ms_sound_kon; + ins.delay_off_ms = in.ms_sound_koff; +} + +void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in) +{ + return cvt_generic_to_FMIns(ins, in); +} + +void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in) +{ + cvt_FMIns_to_generic(ins, in); } bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) { + int err = 0; + WOPLFile *wopl = NULL; + char *raw_file_data = NULL; size_t fsize; - ADL_UNUSED(fsize); if(!fr.isValid()) { errorStringOut = "Custom bank: Invalid data stream!"; return false; } - char magic[32]; - std::memset(magic, 0, 32); - - uint16_t version = 0; - - uint16_t count_melodic_banks = 1; - uint16_t count_percusive_banks = 1; - - if(fr.read(magic, 1, 11) != 11) + // Read complete bank file into the memory + fr.seek(0, SEEK_END); + fsize = fr.tell(); + fr.seek(0, SEEK_SET); + // Allocate necessary memory block + raw_file_data = (char*)malloc(fsize); + if(!raw_file_data) { - errorStringOut = "Custom bank: Can't read magic number!"; + errorStringOut = "Custom bank: Out of memory before of read!"; return false; } + fr.read(raw_file_data, 1, fsize); - if(std::strncmp(magic, wopl3_magic, 11) != 0) + // Parse bank file from the memory + wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err); + //Free the buffer no more needed + free(raw_file_data); + + // Check for any erros + if(!wopl) { - errorStringOut = "Custom bank: Invalid magic number!"; - return false; + switch(err) + { + case WOPL_ERR_BAD_MAGIC: + errorStringOut = "Custom bank: Invalid magic!"; + return false; + case WOPL_ERR_UNEXPECTED_ENDING: + errorStringOut = "Custom bank: Unexpected ending!"; + return false; + case WOPL_ERR_INVALID_BANKS_COUNT: + errorStringOut = "Custom bank: Invalid banks count!"; + return false; + case WOPL_ERR_NEWER_VERSION: + errorStringOut = "Custom bank: Version is newer than supported by this library!"; + return false; + case WOPL_ERR_OUT_OF_MEMORY: + errorStringOut = "Custom bank: Out of memory!"; + return false; + default: + errorStringOut = "Custom bank: Unknown error!"; + return false; + } } - uint8_t version_raw[2]; - if(fr.read(version_raw, 1, 2) != 2) - { - errorStringOut = "Custom bank: Can't read version!"; - return false; - } - - version = toUint16LE(version_raw); - if(version > wopl_latest_version) - { - errorStringOut = "Custom bank: Unsupported WOPL version!"; - return false; - } - - uint8_t head[6]; - std::memset(head, 0, 6); - if(fr.read(head, 1, 6) != 6) - { - errorStringOut = "Custom bank: Can't read header!"; - return false; - } - - count_melodic_banks = toUint16BE(head); - count_percusive_banks = toUint16BE(head + 2); - - if((count_melodic_banks < 1) || (count_percusive_banks < 1)) - { - errorStringOut = "Custom bank: Too few banks in this file!"; - return false; - } - - /*UNUSED YET*/ - bool default_deep_vibrato = ((head[4]>>0) & 0x01); - bool default_deep_tremolo = ((head[4]>>1) & 0x01); - - //5'th byte reserved for Deep-Tremolo and Deep-Vibrato flags - m_setup.HighTremoloMode = default_deep_tremolo; - m_setup.HighVibratoMode = default_deep_vibrato; - //6'th byte reserved for ADLMIDI's default volume model - m_setup.VolumeModel = (int)head[5]; - - opl.dynamic_melodic_banks.clear(); - opl.dynamic_percussion_banks.clear(); + opl.dynamic_bank_setup.adLibPercussions = false; + opl.dynamic_bank_setup.scaleModulators = false; + opl.dynamic_bank_setup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + opl.dynamic_bank_setup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + opl.dynamic_bank_setup.volumeModel = wopl->volume_model; + m_setup.HighTremoloMode = -1; + m_setup.HighVibratoMode = -1; + m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; opl.setEmbeddedBank(m_setup.AdlBank); - if(version >= 2)//Read bank meta-entries + uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion}; + WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive }; + + for(unsigned ss = 0; ss < 2; ss++) { - for(uint16_t i = 0; i < count_melodic_banks; i++) + for(unsigned i = 0; i < slots_counts[ss]; i++) { - uint8_t bank_meta[34]; - if(fr.read(bank_meta, 1, 34) != 34) + unsigned bankno = + (slots_src_ins[ss][i].bank_midi_msb * 256) + + slots_src_ins[ss][i].bank_midi_lsb + + (ss ? OPL3::PercussionTag : 0); + OPL3::Bank &bank = opl.dynamic_banks[bankno]; + for(int j = 0; j < 128; j++) { - errorStringOut = "Custom bank: Fail to read melodic bank meta-data!"; - return false; + adlinsdata2 &ins = bank.ins[j]; + std::memset(&ins, 0, sizeof(adlinsdata2)); + WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j]; + cvt_generic_to_FMIns(ins, inIns); } - uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); - size_t offset = opl.dynamic_melodic_banks.size(); - opl.dynamic_melodic_banks[bank] = offset; - //strncpy(bankMeta.name, char_p(bank_meta), 32); } - - for(uint16_t i = 0; i < count_percusive_banks; i++) - { - uint8_t bank_meta[34]; - if(fr.read(bank_meta, 1, 34) != 34) - { - errorStringOut = "Custom bank: Fail to read percussion bank meta-data!"; - return false; - } - uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); - size_t offset = opl.dynamic_percussion_banks.size(); - opl.dynamic_percussion_banks[bank] = offset; - //strncpy(bankMeta.name, char_p(bank_meta), 32); - } - } - - uint16_t total = 128 * count_melodic_banks; - bool readPercussion = false; - -tryAgain: - for(uint16_t i = 0; i < total; i++) - { - WOPL_Inst ins; - std::memset(&ins, 0, sizeof(WOPL_Inst)); - if(!readInstrument(fr, ins, version, readPercussion)) - { - opl.setEmbeddedBank(m_setup.AdlBank); - errorStringOut = "Custom bank: Fail to read instrument!"; - return false; - } - ins.adlins.ms_sound_kon = ins.ms_sound_kon; - ins.adlins.ms_sound_koff = ins.ms_sound_koff; - ins.adlins.adlno1 = static_cast(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag); - opl.dynamic_instruments.push_back(ins.op[0]); - ins.adlins.adlno2 = ins.adlins.adlno1; - if(ins.fourOps) - { - ins.adlins.adlno2 = static_cast(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag); - opl.dynamic_instruments.push_back(ins.op[1]); - } - opl.dynamic_metainstruments.push_back(ins.adlins); - } - - if(!readPercussion) - { - total = 128 * count_percusive_banks; - readPercussion = true; - goto tryAgain; } opl.AdlBank = ~0u; // Use dynamic banks! //Percussion offset is count of instruments multipled to count of melodic banks - opl.dynamic_percussion_offset = 128 * count_melodic_banks; - applySetup(); + WOPL_Free(wopl); + return true; } @@ -373,7 +318,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) errorString.clear(); #ifdef DISABLE_EMBEDDED_BANKS - if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256)) + if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty()) { errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; return false; @@ -497,8 +442,7 @@ riffskip: #endif //ADLMIDI_DISABLE_XMI_SUPPORT else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0) { - opl.dynamic_instruments.clear(); - opl.dynamic_metainstruments.clear(); + opl.dynamic_banks.clear(); // Creative Music Format (CMF). // When playing CTMF files, use the following commandline: // adlmidi song8.ctmf -p -v 1 1 0 @@ -520,13 +464,19 @@ riffskip: //std::printf("%u instruments\n", ins_count); for(unsigned i = 0; i < ins_count; ++i) { + unsigned bank = i / 256; + bank = (bank & 127) + ((bank >> 7) << 8); + if(bank > 127 + (127 << 8)) + break; + bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag; + unsigned char InsData[16]; fr.read(InsData, 1, 16); /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7], InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/ - struct adldata adl; - struct adlinsdata adlins; + adlinsdata2 &adlins = opl.dynamic_banks[bank].ins[i % 128]; + adldata adl; adl.modulator_E862 = ((static_cast(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm | ((static_cast(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release @@ -541,15 +491,13 @@ riffskip: adl.carrier_40 = InsData[3]; adl.feedconn = InsData[10] & 0x0F; adl.finetune = 0; - adlins.adlno1 = static_cast(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag); - adlins.adlno2 = adlins.adlno1; + adlins.adl[0] = adl; + adlins.adl[1] = adl; adlins.ms_sound_kon = 1000; adlins.ms_sound_koff = 500; adlins.tone = 0; adlins.flags = 0; adlins.voice2_fine_tune = 0.0; - opl.dynamic_metainstruments.push_back(adlins); - opl.dynamic_instruments.push_back(adl); } fr.seeku(mus_start, SEEK_SET); @@ -557,10 +505,9 @@ riffskip: DeltaTicks = (size_t)ticks; opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); - opl.LogarithmicVolumes = true; opl.AdlPercussionMode = true; opl.m_musicMode = OPL3::MODE_CMF; - opl.m_volumeScale = OPL3::VOLUME_CMF; + opl.m_volumeScale = OPL3::VOLUME_NATIVE; } else { @@ -575,10 +522,9 @@ riffskip: fr.seek(0x7D, SEEK_SET); TrackCount = 1; DeltaTicks = 60; - opl.LogarithmicVolumes = true; //opl.CartoonersVolumes = true; opl.m_musicMode = OPL3::MODE_RSXX; - opl.m_volumeScale = OPL3::VOLUME_CMF; + opl.m_volumeScale = OPL3::VOLUME_NATIVE; } } @@ -636,11 +582,11 @@ riffskip: TrackData.clear(); TrackData.resize(TrackCount, std::vector()); - //CurrentPosition.track.clear(); - //CurrentPosition.track.resize(TrackCount); InvDeltaTicks = fraction(1, 1000000l * static_cast(DeltaTicks)); - //Tempo = 1000000l * InvDeltaTicks; - Tempo = fraction(1, static_cast(DeltaTicks)); + if(is_CMF || is_RSXX) + Tempo = fraction(1, static_cast(DeltaTicks)); + else + Tempo = fraction(1, static_cast(DeltaTicks) * 2); static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00}; size_t totalGotten = 0; @@ -761,14 +707,14 @@ riffskip: return false; } - //Build new MIDI events table (ALPHA!!!) + //Build new MIDI events table if(!buildTrackData()) { errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; return false; } - opl.Reset(m_setup.PCM_RATE); // Reset AdLib + opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) ch.clear(); ch.resize(opl.NumChannels); diff --git a/src/sound/adlmidi/adlmidi_midiplay.cpp b/src/sound/adlmidi/adlmidi_midiplay.cpp index 13e1008c0..89c1a73ad 100644 --- a/src/sound/adlmidi/adlmidi_midiplay.cpp +++ b/src/sound/adlmidi/adlmidi_midiplay.cpp @@ -25,7 +25,7 @@ // Mapping from MIDI volume level to OPL level value. -static const uint32_t DMX_volume_mapping_table[] = +static const uint8_t DMX_volume_mapping_table[128] = { 0, 1, 3, 5, 6, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, @@ -43,23 +43,6 @@ static const uint32_t DMX_volume_mapping_table[] = 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127, - //Protection entries to avoid crash if value more than 127 - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, }; static const uint8_t W9X_volume_mapping_table[32] = @@ -117,18 +100,17 @@ inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) void MIDIplay::AdlChannel::AddAge(int64_t ms) { - if(users.empty()) - koff_time_until_neglible = - std::max(int64_t(koff_time_until_neglible - ms), static_cast(-0x1FFFFFFFl)); + const int64_t neg = static_cast(-0x1FFFFFFFl); + if(users_empty()) + koff_time_until_neglible = std::max(int64_t(koff_time_until_neglible - ms), neg); else { koff_time_until_neglible = 0; - - for(users_t::iterator i = users.begin(); i != users.end(); ++i) + for(LocationData *i = users_first; i; i = i->next) { - i->second.kon_time_until_neglible = - std::max(i->second.kon_time_until_neglible - ms, static_cast(-0x1FFFFFFFl)); - i->second.vibdelay += ms; + if(!i->fixed_sustain) + i->kon_time_until_neglible = std::max(i->kon_time_until_neglible - ms, neg); + i->vibdelay += ms; } } } @@ -222,7 +204,9 @@ void MIDIplay::MidiTrackRow::sortEvents(bool *noteStates) j = noteOffs.erase(j); markAsOn.erase(note_i); continue; - } else { + } + else + { //When same row has many note-offs on same row //that means a zero-length note follows previous note //it must be shuted down @@ -451,17 +435,17 @@ bool MIDIplay::buildTrackData() if(track.empty()) continue;//Empty track is useless! - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); std::fflush(stdout); - #endif +#endif MidiTrackRow *posPrev = &(*(track.begin()));//First element for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) { - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION bool tempoChanged = false; - #endif +#endif MidiTrackRow &pos = *it; if((posPrev != &pos) && //Skip first event (!tempos.empty()) && //Only when in-track tempo events are available @@ -507,9 +491,9 @@ bool MIDIplay::buildTrackData() //Apply next tempo currentTempo = points[j].tempo; - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION tempoChanged = true; - #endif +#endif } //Then calculate time between last tempo change point and end point TempoChangePoint tailTempo = points.back(); @@ -552,10 +536,10 @@ bool MIDIplay::buildTrackData() loopEndTime = pos.time; } - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); std::fflush(stdout); - #endif +#endif abs_position += pos.delay; posPrev = &pos; @@ -575,7 +559,7 @@ bool MIDIplay::buildTrackData() //Resolve "hell of all times" of too short drum notes: //move too short percussion note-offs far far away as possible /********************************************************************************/ - #if 1 //Use this to record WAVEs for comparison before/after implementing of this +#if 1 //Use this to record WAVEs for comparison before/after implementing of this if(opl.m_musicMode == OPL3::MODE_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF { //! Minimal real time in seconds @@ -625,8 +609,8 @@ bool MIDIplay::buildTrackData() } bool percussion = (et->channel == 9) || - banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) - banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) + banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) + banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) if(!percussion) continue; @@ -687,7 +671,7 @@ bool MIDIplay::buildTrackData() #undef DRUM_NOTE_MIN_TIME #undef DRUM_NOTE_MIN_TICKS } - #endif +#endif return true; } @@ -695,7 +679,9 @@ bool MIDIplay::buildTrackData() MIDIplay::MIDIplay(unsigned long sampleRate): - cmf_percussion_mode(false) + cmf_percussion_mode(false), + m_arpeggioCounter(0), + m_audioTickCounter(0) #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER , fullSongTimeLength(0.0), postSongWaitDelay(1.0), @@ -710,6 +696,9 @@ MIDIplay::MIDIplay(unsigned long sampleRate): { devices.clear(); + m_setup.emulator = ADLMIDI_EMU_NUKED; + m_setup.runAtPcmRate = false; + m_setup.PCM_RATE = sampleRate; m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; @@ -725,6 +714,7 @@ MIDIplay::MIDIplay(unsigned long sampleRate): //m_setup.SkipForward = 0; m_setup.loopingIsEnabled = false; m_setup.ScaleModulators = -1; + m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; @@ -737,26 +727,44 @@ MIDIplay::MIDIplay(unsigned long sampleRate): void MIDIplay::applySetup() { m_setup.tick_skip_samples_delay = 0; - opl.HighTremoloMode = m_setup.HighTremoloMode == -1 ? adlbanksetup[m_setup.AdlBank].deepTremolo : (bool)m_setup.HighTremoloMode; - opl.HighVibratoMode = m_setup.HighVibratoMode == -1 ? adlbanksetup[m_setup.AdlBank].deepVibrato : (bool)m_setup.HighVibratoMode; - opl.AdlPercussionMode = m_setup.AdlPercussionMode == -1 ? adlbanksetup[m_setup.AdlBank].adLibPercussions : (bool)m_setup.AdlPercussionMode; - opl.ScaleModulators = m_setup.ScaleModulators == -1 ? adlbanksetup[m_setup.AdlBank].scaleModulators : (bool)m_setup.ScaleModulators; - opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; - //opl.CartoonersVolumes = false; + + opl.runAtPcmRate = m_setup.runAtPcmRate; + + if(opl.AdlBank != ~0u) + opl.dynamic_bank_setup = adlbanksetup[m_setup.AdlBank]; + + opl.HighTremoloMode = m_setup.HighTremoloMode < 0 ? + opl.dynamic_bank_setup.deepTremolo : + (m_setup.HighTremoloMode != 0); + opl.HighVibratoMode = m_setup.HighVibratoMode < 0 ? + opl.dynamic_bank_setup.deepVibrato : + (m_setup.HighVibratoMode != 0); + opl.AdlPercussionMode = m_setup.AdlPercussionMode < 0 ? + opl.dynamic_bank_setup.adLibPercussions : + (m_setup.AdlPercussionMode != 0); + opl.ScaleModulators = m_setup.ScaleModulators < 0 ? + opl.dynamic_bank_setup.scaleModulators : + (m_setup.ScaleModulators != 0); + if(m_setup.LogarithmicVolumes) + opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); opl.m_musicMode = OPL3::MODE_MIDI; opl.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - opl.m_volumeScale = (OPL3::VolumesScale)adlbanksetup[m_setup.AdlBank].volumeModel; + opl.m_volumeScale = (OPL3::VolumesScale)opl.dynamic_bank_setup.volumeModel; opl.NumCards = m_setup.NumCards; opl.NumFourOps = m_setup.NumFourOps; cmf_percussion_mode = false; - opl.Reset(m_setup.PCM_RATE); + opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); ch.clear(); ch.resize(opl.NumChannels); + + // Reset the arpeggio counter + m_arpeggioCounter = 0; } +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) { uint64_t result = 0; @@ -789,13 +797,12 @@ uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) return result; } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER double MIDIplay::Tick(double s, double granularity) { s *= tempoMultiplier; - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING if(CurrentPositionNew.began) - #endif +#endif CurrentPositionNew.wait -= s; CurrentPositionNew.absTimePosition += s; @@ -824,7 +831,7 @@ double MIDIplay::Tick(double s, double granularity) return CurrentPositionNew.wait; } -#endif +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ void MIDIplay::TickIteratos(double s) { @@ -941,27 +948,19 @@ void MIDIplay::setTempo(double tempo) { tempoMultiplier = tempo; } -#endif +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ void MIDIplay::realTime_ResetState() { for(size_t ch = 0; ch < Ch.size(); ch++) { MIDIchannel &chan = Ch[ch]; + chan.resetAllControllers(); chan.volume = (opl.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; - chan.expression = 127; - chan.panning = 0x30; - chan.vibrato = 0; - chan.sustain = 0; - chan.bend = 0.0; - chan.bendsense = 2 / 8192.0; chan.vibpos = 0.0; - chan.vibdepth = 0.5 / 127.0; - chan.vibdelay = 0; chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; - chan.brightness = 127; NoteUpdate_All(uint16_t(ch), Upd_All); NoteUpdate_All(uint16_t(ch), Upd_Off); } @@ -969,13 +968,16 @@ void MIDIplay::realTime_ResetState() bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { + if(note >= 128) + note = 127; + if((opl.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch - MIDIchannel::activenoteiterator i = Ch[channel].activenotes.find(note); - if(i != Ch[channel].activenotes.end()) + MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(i) { - i->second.vol = velocity; + i->vol = velocity; NoteUpdate(channel, i, Upd_Volume); return false; } @@ -991,14 +993,16 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(velocity == 0) return false; - size_t midiins = Ch[channel].patch; - bool isPercussion = (channel % 16 == 9); + MIDIchannel &midiChan = Ch[channel]; + + size_t midiins = midiChan.patch; + bool isPercussion = (channel % 16 == 9); bool isXgPercussion = false; uint16_t bank = 0; - if(Ch[channel].bank_msb || Ch[channel].bank_lsb) + if(midiChan.bank_msb || midiChan.bank_lsb) { - bank = (uint16_t(Ch[channel].bank_msb) * 256) + uint16_t(Ch[channel].bank_lsb); + bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) //0x7F00 - XG Percussion channel (16256 signed decimal) if(bank == 0x7E00 || bank == 0x7F00) @@ -1006,7 +1010,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) //Let XG Percussion bank will use (0...127 range in WOPN file) bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); // MIDI instrument defines the patch - midiins = opl.dynamic_percussion_offset + note; // Percussion instrument + midiins = note; // Percussion instrument isXgPercussion = true; isPercussion = false; } @@ -1015,43 +1019,43 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(isPercussion) { bank = (uint16_t)midiins; // MIDI instrument defines the patch - midiins = opl.dynamic_percussion_offset + note; // Percussion instrument + midiins = note; // Percussion instrument } + if(isPercussion || isXgPercussion) + bank += OPL3::PercussionTag; + + const adlinsdata2 *ains = &OPL3::emptyInstrument; //Set bank bank - if(bank > 0) + const OPL3::Bank *bnk = NULL; + if((bank & ~(uint16_t)OPL3::PercussionTag) > 0) { - if(isPercussion || isXgPercussion) + OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank); + if(b != opl.dynamic_banks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + else if(hooks.onDebugMessage) { - OPL3::BankMap::iterator b = opl.dynamic_percussion_banks.find(bank); - if(b != opl.dynamic_percussion_banks.end()) - midiins += b->second * 128; - else - if(hooks.onDebugMessage) - { - if(!caugh_missing_banks_melodic.count(bank)) - { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing percussion MIDI bank %i (patch %i)", channel, bank, midiins); - caugh_missing_banks_melodic.insert(bank); - } - } - } - else - { - OPL3::BankMap::iterator b = opl.dynamic_melodic_banks.find(bank); - if(b != opl.dynamic_melodic_banks.end()) - midiins += b->second * 128; - else - if(hooks.onDebugMessage) - { - if(!caugh_missing_banks_percussion.count(bank)) - { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing melodic MIDI bank %i (patch %i)", channel, bank, midiins); - caugh_missing_banks_percussion.insert(bank); - } - } + std::set &missing = (isPercussion || isXgPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion || isXgPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); } } + //Or fall back to first bank + if(ains->flags & adlinsdata::Flag_NoSound) + { + OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank & OPL3::PercussionTag); + if(b != opl.dynamic_banks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + } /* if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50)) @@ -1062,8 +1066,6 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) //if(midiins == 56) vol = vol*6/10; // HACK //int meta = banks[opl.AdlBank][midiins]; - size_t meta = opl.GetAdlMetaNumber(midiins); - const adlinsdata *ains = &opl.GetAdlMetaIns(meta); int16_t tone = note; if(!isPercussion && !isXgPercussion && (bank > 0)) // For non-zero banks @@ -1072,23 +1074,18 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { if(hooks.onDebugMessage) { - if(!caugh_missing_instruments.count(static_cast(midiins))) - { + if(caugh_missing_instruments.insert(static_cast(midiins)).second) hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); - caugh_missing_instruments.insert(static_cast(midiins)); - } } bank = 0; - midiins = Ch[channel].patch; - meta = opl.GetAdlMetaNumber(midiins); - ains = &opl.GetAdlMetaIns(meta); + midiins = midiChan.patch; } } if(ains->tone) { - /*if(ains.tone < 20) - tone += ains.tone; + /*if(ains->tone < 20) + tone += ains->tone; else*/ if(ains->tone < 128) tone = ains->tone; @@ -1096,30 +1093,38 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) tone -= ains->tone - 128; } - //uint16_t i[2] = { ains.adlno1, ains.adlno2 }; + //uint16_t i[2] = { ains->adlno1, ains->adlno2 }; bool pseudo_4op = ains->flags & adlinsdata::Flag_Pseudo4op; - MIDIchannel::NoteInfo::Phys voices[2] = +#ifndef __WATCOMC__ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = { - {ains->adlno1, false}, - {ains->adlno2, pseudo_4op} + {0, ains->adl[0], false}, + {0, ains->adl[1], pseudo_4op} }; +#else /* Unfortunately, WatCom can't brace-initialize structure that incluses structure fields */ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans]; + voices[0].chip_chan = 0; + voices[0].ains = ains->adl[0]; + voices[0].pseudo4op = false; + voices[1].chip_chan = 0; + voices[1].ains = ains->adl[1]; + voices[1].pseudo4op = pseudo_4op; +#endif /* __WATCOMC__ */ if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) voices[1] = voices[0];//i[1] = i[0]; if(hooks.onDebugMessage) { - if(!caugh_missing_instruments.count(static_cast(midiins)) && (ains->flags & adlinsdata::Flag_NoSound)) - { + if((ains->flags & adlinsdata::Flag_NoSound) && + caugh_missing_instruments.insert(static_cast(midiins)).second) hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); - caugh_missing_instruments.insert(static_cast(midiins)); - } } // Allocate AdLib channel (the physical sound channel for the note) - int32_t adlchannel[2] = { -1, -1 }; + int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; - for(uint32_t ccount = 0; ccount < 2; ++ccount) + for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { if(ccount == 1) { @@ -1137,7 +1142,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary - if(voices[0].insId == voices[1].insId || pseudo_4op/*i[0] == i[1] || pseudo_4op*/) + if(voices[0].ains == voices[1].ains || pseudo_4op/*i[0] == i[1] || pseudo_4op*/) { // Only use regular channels uint8_t expected_mode = 0; @@ -1182,7 +1187,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "ignored unplaceable note [bank %i, inst %i, note %i, MIDI channel %i]", - bank, Ch[channel].patch, note, channel); + bank, midiChan.patch, note, channel); continue; // Could not play this note. Ignore it. } @@ -1201,19 +1206,21 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) // Allocate active note for MIDI channel std::pair - ir = Ch[channel].activenotes.insert(std::make_pair(note, MIDIchannel::NoteInfo())); - ir.first->second.vol = velocity; - ir.first->second.tone = tone; - ir.first->second.midiins = midiins; - ir.first->second.insmeta = meta; + ir = midiChan.activenotes_insert(note); + ir.first->vol = velocity; + ir.first->vibrato = midiChan.noteAftertouch[note]; + ir.first->tone = tone; + ir.first->midiins = midiins; + ir.first->ains = ains; + ir.first->chip_channels_count = 0; - for(unsigned ccount = 0; ccount < 2; ++ccount) + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; if(c < 0) continue; uint16_t chipChan = static_cast(adlchannel[ccount]); - ir.first->second.phys[chipChan] = voices[ccount]; + ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); return true; @@ -1228,31 +1235,28 @@ void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) { channel = channel % 16; - MIDIchannel::activenoteiterator - i = Ch[channel].activenotes.find(note); - if(i == Ch[channel].activenotes.end()) + MIDIchannel &chan = Ch[channel]; + MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(i) { - // Ignore touch if note is not active - return; + i->vibrato = atVal; + } + + uint8_t oldAtVal = chan.noteAftertouch[note % 128]; + if(atVal != oldAtVal) + { + chan.noteAftertouch[note % 128] = atVal; + bool inUse = atVal != 0; + for(unsigned n = 0; !inUse && n < 128; ++n) + inUse = chan.noteAftertouch[n] != 0; + chan.noteAfterTouchInUse = inUse; } - i->second.vol = 127 - atVal; - NoteUpdate(channel, i, Upd_Volume); } void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) { - // TODO: Verify, is this correct action? channel = channel % 16; - for(MIDIchannel::activenoteiterator - i = Ch[channel].activenotes.begin(); - i != Ch[channel].activenotes.end(); - ++i) - { - // Set this pressure to all active notes on the channel - i->second.vol = 127 - atVal; - } - - NoteUpdate_All(channel, Upd_Volume); + Ch[channel].aftertouch = atVal; } void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) @@ -1312,30 +1316,24 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) case 10: // Change panning Ch[channel].panning = 0x00; - if(value < 64 + 32) Ch[channel].panning |= 0x10; - if(value >= 64 - 32) Ch[channel].panning |= 0x20; + if(value < 64 + 32) Ch[channel].panning |= OPL_PANNING_LEFT; + if(value >= 64 - 32) Ch[channel].panning |= OPL_PANNING_RIGHT; NoteUpdate_All(channel, Upd_Pan); break; case 121: // Reset all controllers - Ch[channel].bend = 0; - Ch[channel].volume = 100; - Ch[channel].expression = 127; - Ch[channel].sustain = 0; - Ch[channel].vibrato = 0; - Ch[channel].vibspeed = 2 * 3.141592653 * 5.0; - Ch[channel].vibdepth = 0.5 / 127; - Ch[channel].vibdelay = 0; - Ch[channel].panning = 0x30; - Ch[channel].portamento = 0; - Ch[channel].brightness = 127; + Ch[channel].resetAllControllers(); //UpdatePortamento(MidCh); NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes KillSustainingNotes(channel); break; + case 120: // All sounds off + NoteUpdate_All(channel, Upt_OffMute); + break; + case 123: // All notes off NoteUpdate_All(channel, Upd_Off); break; @@ -1405,14 +1403,14 @@ void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) { channel = channel % 16; - Ch[channel].bend = (uint32_t(pitch) - 8192) * Ch[channel].bendsense; + Ch[channel].bend = int(pitch) - 8192; NoteUpdate_All(channel, Upd_Pitch); } void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) { channel = channel % 16; - Ch[channel].bend = (int(lsb) + int(msb) * 128 - 8192) * Ch[channel].bendsense; + Ch[channel].bend = int(lsb) + int(msb) * 128 - 8192; NoteUpdate_All(channel, Upd_Pitch); } @@ -1441,85 +1439,97 @@ void MIDIplay::realTime_panic() KillSustainingNotes(-1, -1); } +void MIDIplay::AudioTick(uint32_t chipId, uint32_t /*rate*/) +{ + if(chipId != 0) // do first chip ticks only + return; + + /*uint32_t tickNumber = */m_audioTickCounter++; +} void MIDIplay::NoteUpdate(uint16_t MidCh, MIDIplay::MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn) { - MIDIchannel::NoteInfo &info = i->second; + MIDIchannel::NoteInfo &info = *i; const int16_t tone = info.tone; const uint8_t vol = info.vol; - const int midiins = info.midiins; - const size_t insmeta = info.insmeta; - const adlinsdata &ains = opl.GetAdlMetaIns(insmeta); + const int midiins = static_cast(info.midiins); + const adlinsdata2 &ains = *info.ains; AdlChannel::Location my_loc; my_loc.MidCh = MidCh; - my_loc.note = i->first; + my_loc.note = info.note; - for(MIDIchannel::NoteInfo::PhysMap::iterator - jnext = info.phys.begin(); - jnext != info.phys.end(); - ) + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) { - MIDIchannel::NoteInfo::PhysMap::iterator j(jnext++); - uint16_t c = j->first; - const MIDIchannel::NoteInfo::Phys &ins = j->second; + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; if(select_adlchn >= 0 && c != select_adlchn) continue; if(props_mask & Upd_Patch) { - opl.Patch(c, ins.insId); - AdlChannel::LocationData &d = ch[c].users[my_loc]; - d.sustained = false; // inserts if necessary - d.vibdelay = 0; - d.kon_time_until_neglible = ains.ms_sound_kon; - d.ins = ins; + opl.Patch(c, ins.ains); + AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + if(d) // inserts if necessary + { + d->sustained = false; + d->vibdelay = 0; + d->fixed_sustain = (ains.ms_sound_kon == static_cast(adlNoteOnMaxTime)); + d->kon_time_until_neglible = ains.ms_sound_kon; + d->ins = ins; + } } } - for(MIDIchannel::NoteInfo::PhysMap::iterator - jnext = info.phys.begin(); - jnext != info.phys.end(); - ) + for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) { - MIDIchannel::NoteInfo::PhysMap::iterator j(jnext++); - uint16_t c = j->first; - const MIDIchannel::NoteInfo::Phys &ins = j->second; + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; - if(select_adlchn >= 0 && c != select_adlchn) continue; + if(select_adlchn >= 0 && c != select_adlchn) + continue; if(props_mask & Upd_Off) // note off { if(Ch[MidCh].sustain == 0) { - AdlChannel::users_t::iterator k = ch[c].users.find(my_loc); + AdlChannel::LocationData *k = ch[c].users_find(my_loc); - if(k != ch[c].users.end()) - ch[c].users.erase(k); + if(k) + ch[c].users_erase(k); if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, tone, midiins, 0, 0.0); - if(ch[c].users.empty()) + if(ch[c].users_empty()) { opl.NoteOff(c); - ch[c].koff_time_until_neglible = - ains.ms_sound_koff; + if(props_mask & Upd_Mute) // Mute the note + { + opl.Touch_Real(c, 0); + ch[c].koff_time_until_neglible = 0; + } + else + { + ch[c].koff_time_until_neglible = ains.ms_sound_koff; + } } } else { // Sustain: Forget about the note, but don't key it off. // Also will avoid overwriting it very soon. - AdlChannel::LocationData &d = ch[c].users[my_loc]; - d.sustained = true; // note: not erased! + AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + if(d) + d->sustained = true; // note: not erased! if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, tone, midiins, -1, 0.0); } - info.phys.erase(j); + info.phys_erase_at(&ins); // decrements channel count + --ccount; // adjusts index accordingly continue; } @@ -1532,10 +1542,19 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, bool is_percussion = (MidCh == 9) || Ch[MidCh].is_xg_percussion; uint8_t brightness = is_percussion ? 127 : Ch[MidCh].brightness; + if(!m_setup.fullRangeBrightnessCC74) + { + // Simulate post-High-Pass filter result which affects sounding by half level only + if(brightness >= 64) + brightness = 127; + else + brightness *= 2; + } + switch(opl.m_volumeScale) { + case OPL3::VOLUME_Generic: - case OPL3::VOLUME_CMF: { volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; @@ -1548,25 +1567,29 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, */ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); - if(opl.LogarithmicVolumes) - volume = volume * 127 / (127 * 127 * 127) / 2; - else - { - // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - volume = volume > 8725 ? static_cast(std::log(static_cast(volume)) * 11.541561 + (0.5 - 104.22845)) : 0; - // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) - //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); - } + // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) + volume = volume > 8725 ? static_cast(std::log(static_cast(volume)) * 11.541561 + (0.5 - 104.22845)) : 0; + // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) + //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + opl.Touch_Real(c, volume, brightness); //opl.Touch(c, volume); } break; + case OPL3::VOLUME_NATIVE: + { + volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = volume * 127 / (127 * 127 * 127) / 2; + opl.Touch_Real(c, volume, brightness); + } + break; + case OPL3::VOLUME_DMX: { volume = 2 * ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; - volume = (DMX_volume_mapping_table[vol] * volume) >> 9; + volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; opl.Touch_Real(c, volume, brightness); } break; @@ -1606,37 +1629,40 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Pitch) { - AdlChannel::LocationData &d = ch[c].users[my_loc]; + AdlChannel::LocationData *d = ch[c].users_find(my_loc); // Don't bend a sustained note - if(!d.sustained) + if(!d || !d->sustained) { - double bend = Ch[MidCh].bend + opl.GetAdlIns(ins.insId).finetune; + double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; + double bend = midibend + ins.ains.finetune; double phase = 0.0; + uint8_t vibrato = std::max(Ch[MidCh].vibrato, Ch[MidCh].aftertouch); + vibrato = std::max(vibrato, i->vibrato); if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins.pseudo4op) { phase = ains.voice2_fine_tune;//0.125; // Detune the note slightly (this is what Doom does) } - if(Ch[MidCh].vibrato && d.vibdelay >= Ch[MidCh].vibdelay) - bend += Ch[MidCh].vibrato * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); + if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) + bend += static_cast(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); - #ifdef ADLMIDI_USE_DOSBOX_OPL -#define BEND_COEFFICIENT 172.00093 - #else -#define BEND_COEFFICIENT 172.4387 - #endif - opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (tone + bend + phase))); +#ifdef ADLMIDI_USE_DOSBOX_OPL +# define BEND_COEFFICIENT 172.00093 +#else +# define BEND_COEFFICIENT 172.4387 +#endif + opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast(tone) + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, vol, Ch[MidCh].bend); + hooks.onNote(hooks.onNote_userData, c, tone, midiins, vol, midibend); } } } - if(info.phys.empty()) - Ch[MidCh].activenotes.erase(i); + if(info.chip_channels_count == 0) + Ch[MidCh].activenotes_erase(i); } #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -1651,9 +1677,9 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) const size_t TrackCount = CurrentPositionNew.track.size(); const PositionNew RowBeginPosition(CurrentPositionNew); - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION double maxTime = 0.0; - #endif +#endif for(size_t tk = 0; tk < TrackCount; ++tk) { @@ -1671,10 +1697,10 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) for(size_t i = 0; i < track.pos->events.size(); i++) { const MidiEvent &evt = track.pos->events[i]; - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) CurrentPositionNew.began = true; - #endif +#endif if(isSeek && (evt.type == MidiEvent::T_NOTEON)) continue; HandleEvent(tk, evt, track.status); @@ -1682,10 +1708,10 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) break;//Stop event handling on catching loopEnd event! } - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION if(maxTime < track.pos->time) maxTime = track.pos->time; - #endif +#endif // Read next event time (unless the track just ended) if(track.status >= 0) { @@ -1695,11 +1721,11 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) } } - #ifdef DEBUG_TIME_CALCULATION +#ifdef DEBUG_TIME_CALCULATION std::fprintf(stdout, " \r"); std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition); std::fflush(stdout); - #endif +#endif // Find shortest delay from all track uint64_t shortest = 0; @@ -1723,9 +1749,9 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) fraction t = shortest * Tempo; - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING if(CurrentPositionNew.began) - #endif +#endif CurrentPositionNew.wait += t.value(); //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); @@ -1751,9 +1777,7 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) return true;//Has events in queue } -#endif -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &status) { uint8_t *&ptr = *pptr; @@ -1801,6 +1825,14 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat evt.subtype = evtype; evt.data.insert(evt.data.begin(), data.begin(), data.end()); +#if 0 /* Print all tempo events */ + if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); + } +#endif + /* TODO: Store those meta-strings separately and give ability to read them * by external functions (to display song title and copyright in the player) */ if(evt.subtype == MidiEvent::ST_COPYRIGHT) @@ -1931,9 +1963,16 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat evt.data.push_back(*(ptr++)); evt.data.push_back(*(ptr++)); - if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) + /* TODO: Implement conversion of RSXX's note volumes out of synthesizer */ + /*if((opl.m_musicMode == OPL3::MODE_RSXX) && (evType == MidiEvent::T_NOTEON) && (evt.data[1] != 0)) + { + //NOT WORKING YET + evt.type = MidiEvent::T_NOTETOUCH; + } + else */if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) + { evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! - //111'th loopStart controller (RPG Maker and others) + } //111'th loopStart controller (RPG Maker and others) else if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) { //Change event type to custom Loop Start event and clear data @@ -1957,7 +1996,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat return evt; } -#endif +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ const std::string &MIDIplay::getErrorString() { @@ -2046,7 +2085,7 @@ void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &statu v |= 0x30; //std::printf("OPL poke %02X, %02X\n", i, v); //std::fflush(stdout); - opl.PokeN(0, i, v); + opl.Poke(0, i, v); return; } @@ -2126,43 +2165,40 @@ void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &statu } } } -#endif +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ -int64_t MIDIplay::CalculateAdlChannelGoodness(unsigned c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const +int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const { int64_t s = -ch[c].koff_time_until_neglible; // Same midi-instrument = some stability //if(c == MidCh) s += 4; - for(AdlChannel::users_t::const_iterator - j = ch[c].users.begin(); - j != ch[c].users.end(); - ++j) + for(AdlChannel::LocationData *j = ch[c].users_first; j; j = j->next) { s -= 4000; - if(!j->second.sustained) - s -= j->second.kon_time_until_neglible; + if(!j->sustained) + s -= j->kon_time_until_neglible; else - s -= (j->second.kon_time_until_neglible / 2); + s -= (j->kon_time_until_neglible / 2); - MIDIchannel::activenotemap_t::const_iterator - k = Ch[j->first.MidCh].activenotes.find(j->first.note); + MIDIchannel::activenoteiterator + k = const_cast(Ch[j->loc.MidCh]).activenotes_find(j->loc.note); - if(k != Ch[j->first.MidCh].activenotes.end()) + if(k) { // Same instrument = good - if(j->second.ins == ins) + if(j->ins == ins) { s += 300; // Arpeggio candidate = even better - if(j->second.vibdelay < 70 - || j->second.kon_time_until_neglible > 20000) + if(j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) s += 0; } // Percussion is inferior to melody - s += 50 * (int64_t)(k->second.midiins / 128); + s += 50 * (int64_t)(k->midiins / 128); /* if(k->second.midiins >= 25 && k->second.midiins < 40 @@ -2178,21 +2214,18 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(unsigned c, const MIDIchannel::Not // increase the score slightly. unsigned n_evacuation_stations = 0; - for(unsigned c2 = 0; c2 < opl.NumChannels; ++c2) + for(size_t c2 = 0; c2 < static_cast(opl.NumChannels); ++c2) { if(c2 == c) continue; if(opl.four_op_category[c2] != opl.four_op_category[c]) continue; - for(AdlChannel::users_t::const_iterator - m = ch[c2].users.begin(); - m != ch[c2].users.end(); - ++m) + for(AdlChannel::LocationData *m = ch[c2].users_first; m; m = m->next) { - if(m->second.sustained) continue; - if(m->second.vibdelay >= 200) continue; - if(m->second.ins != j->second.ins) continue; + if(m->sustained) continue; + if(m->vibdelay >= 200) continue; + if(m->ins != j->ins) continue; n_evacuation_stations += 1; } } @@ -2206,27 +2239,25 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(unsigned c, const MIDIchannel::Not void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) { - if(ch[c].users.empty()) return; // Nothing to do + if(ch[c].users_empty()) return; // Nothing to do //bool doing_arpeggio = false; - for(AdlChannel::users_t::iterator - jnext = ch[c].users.begin(); - jnext != ch[c].users.end(); - ) + for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) { - AdlChannel::users_t::iterator j(jnext++); + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; - if(!j->second.sustained) + if(!j->sustained) { // Collision: Kill old note, // UNLESS we're going to do arpeggio MIDIchannel::activenoteiterator i - (Ch[j->first.MidCh].activenotes.find(j->first.note)); + (Ch[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); // Check if we can do arpeggio. - if((j->second.vibdelay < 70 - || j->second.kon_time_until_neglible > 20000) - && j->second.ins == ins) + if((j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) + && j->ins == ins) { // Do arpeggio together with this note. //doing_arpeggio = true; @@ -2245,11 +2276,13 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. - if(ch[c].users.empty()) + if(ch[c].users_empty()) opl.NoteOff(c); } -void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator j, MIDIplay::MIDIchannel::activenoteiterator i) +void MIDIplay::KillOrEvacuate(size_t from_channel, + AdlChannel::LocationData *j, + MIDIplay::MIDIchannel::activenoteiterator i) { // Before killing the note, check if it can be // evacuated to another channel as an arpeggio @@ -2267,32 +2300,34 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator if(opl.four_op_category[c] != opl.four_op_category[from_channel]) continue; - for(AdlChannel::users_t::iterator - m = ch[c].users.begin(); - m != ch[c].users.end(); - ++m) + AdlChannel &adlch = ch[c]; + if(adlch.users_size == AdlChannel::users_max) + continue; // no room for more arpeggio on channel + + for(AdlChannel::LocationData *m = adlch.users_first; m; m = m->next) { - if(m->second.vibdelay >= 200 - && m->second.kon_time_until_neglible < 10000) continue; - if(m->second.ins != j->second.ins) + if(m->vibdelay >= 200 + && m->kon_time_until_neglible < 10000) continue; + if(m->ins != j->ins) continue; if(hooks.onNote) { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->second.tone, - i->second.midiins, 0, 0.0); + i->tone, + static_cast(i->midiins), 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->second.tone, - i->second.midiins, - i->second.vol, 0.0); + i->tone, + static_cast(i->midiins), + i->vol, 0.0); } - i->second.phys.erase(static_cast(from_channel)); - i->second.phys[cs] = j->second.ins; - ch[cs].users.insert(*j); - ch[from_channel].users.erase(j); + i->phys_erase(static_cast(from_channel)); + i->phys_ensure_find_or_create(cs)->assign(j->ins); + if(!ch[cs].users_insert(*j)) + assert(false); + ch[from_channel].users_erase(j); return; } } @@ -2305,7 +2340,7 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator ins );*/ // Kill it - NoteUpdate(j->first.MidCh, + NoteUpdate(j->loc.MidCh, i, Upd_Off, static_cast(from_channel)); @@ -2332,27 +2367,25 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) for(unsigned c = first; c < last; ++c) { - if(ch[c].users.empty()) continue; // Nothing to do + if(ch[c].users_empty()) continue; // Nothing to do - for(AdlChannel::users_t::iterator - jnext = ch[c].users.begin(); - jnext != ch[c].users.end(); - ) + for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) { - AdlChannel::users_t::iterator j(jnext++); + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; - if((MidCh < 0 || j->first.MidCh == MidCh) - && j->second.sustained) + if((MidCh < 0 || j->loc.MidCh == MidCh) + && j->sustained) { int midiins = '?'; if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, (int)c, j->first.note, midiins, 0, 0.0); - ch[c].users.erase(j); + hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); + ch[c].users_erase(j); } } // Keyoff the channel, if there are no users left. - if(ch[c].users.empty()) + if(ch[c].users_empty()) opl.NoteOff(c); } } @@ -2365,7 +2398,12 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) switch(addr + nrpn * 0x10000 + MSB * 0x20000) { case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity - Ch[MidCh].bendsense = value / 8192.0; + Ch[MidCh].bendsense_msb = value; + Ch[MidCh].updateBendSensitivity(); + break; + case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB + Ch[MidCh].bendsense_lsb = value; + Ch[MidCh].updateBendSensitivity(); break; case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed if(value == 64) Ch[MidCh].vibspeed = 1.0; @@ -2398,9 +2436,7 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) { for(MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes.begin(); - i != Ch[MidCh].activenotes.end(); - ) + i = Ch[MidCh].activenotes_begin(); i;) { MIDIchannel::activenoteiterator j(i++); NoteUpdate(MidCh, j, props_mask); @@ -2410,9 +2446,9 @@ void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) void MIDIplay::NoteOff(uint16_t MidCh, uint8_t note) { MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes.find(note); + i = Ch[MidCh].activenotes_find(note); - if(i != Ch[MidCh].activenotes.end()) + if(i) NoteUpdate(MidCh, i, Upd_Off); } @@ -2421,7 +2457,7 @@ void MIDIplay::UpdateVibrato(double amount) { for(size_t a = 0, b = Ch.size(); a < b; ++a) { - if(Ch[a].vibrato && !Ch[a].activenotes.empty()) + if(Ch[a].hasVibrato() && !Ch[a].activenotes_empty()) { NoteUpdate_All(static_cast(a), Upd_Pitch); Ch[a].vibpos += amount * Ch[a].vibspeed; @@ -2451,9 +2487,9 @@ void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed { // If there is an adlib channel that has multiple notes // simulated on the same channel, arpeggio them. - #if 0 +#if 0 const unsigned desired_arpeggio_rate = 40; // Hz (upper limit) - #if 1 +# if 1 static unsigned cache = 0; amount = amount; // Ignore amount. Assume we get a constant rate. cache += MaxSamplesAtTime * desired_arpeggio_rate; @@ -2461,17 +2497,17 @@ void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed if(cache < PCM_RATE) return; cache %= PCM_RATE; - #else +# else static double arpeggio_cache = 0; arpeggio_cache += amount * desired_arpeggio_rate; if(arpeggio_cache < 1.0) return; arpeggio_cache = 0.0; - #endif - #endif - static unsigned arpeggio_counter = 0; - ++arpeggio_counter; +# endif +#endif + + ++m_arpeggioCounter; for(uint32_t c = 0; c < opl.NumChannels; ++c) { @@ -2479,11 +2515,11 @@ retry_arpeggio: if(c > uint32_t(std::numeric_limits::max())) break; - size_t n_users = ch[c].users.size(); + size_t n_users = ch[c].users_size; if(n_users > 1) { - AdlChannel::users_t::const_iterator i = ch[c].users.begin(); + AdlChannel::LocationData *i = ch[c].users_first; size_t rate_reduction = 3; if(n_users >= 3) @@ -2492,23 +2528,25 @@ retry_arpeggio: if(n_users >= 4) rate_reduction = 1; - std::advance(i, (arpeggio_counter / rate_reduction) % n_users); + for(size_t count = (m_arpeggioCounter / rate_reduction) % n_users, + n = 0; n < count; ++n) + i = i->next; - if(i->second.sustained == false) + if(i->sustained == false) { - if(i->second.kon_time_until_neglible <= 0l) + if(i->kon_time_until_neglible <= 0l) { NoteUpdate( - i->first.MidCh, - Ch[ i->first.MidCh ].activenotes.find(i->first.note), + i->loc.MidCh, + Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Off, static_cast(c)); goto retry_arpeggio; } NoteUpdate( - i->first.MidCh, - Ch[ i->first.MidCh ].activenotes.find(i->first.note), + i->loc.MidCh, + Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Pitch | Upd_Volume | Upd_Pan, static_cast(c)); } @@ -2519,36 +2557,48 @@ retry_arpeggio: #ifndef ADLMIDI_DISABLE_CPP_EXTRAS -ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) +struct AdlInstrumentTester::Impl { - cur_gm = 0; - ins_idx = 0; - play = reinterpret_cast(device->adl_midiPlayer); - if(!play) - return; - opl = &play->opl; + uint32_t cur_gm; + uint32_t ins_idx; + std::vector adl_ins_list; + OPL3 *opl; + MIDIplay *play; +}; + +ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) + : P(new Impl) +{ + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + P->cur_gm = 0; + P->ins_idx = 0; + P->play = play; + P->opl = play ? &play->opl : NULL; } ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() -{} +{ + delete P; +} ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() { const unsigned NumBanks = (unsigned)adl_getBanksCount(); std::set adl_ins_set; for(unsigned bankno = 0; bankno < NumBanks; ++bankno) - adl_ins_set.insert(banks[bankno][cur_gm]); - adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); - ins_idx = 0; + adl_ins_set.insert(banks[bankno][P->cur_gm]); + P->adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); + P->ins_idx = 0; NextAdl(0); - opl->Silence(); + P->opl->Silence(); } ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 { - if(opl->LogarithmicVolumes) + OPL3 *opl = P->opl; + if(opl->m_volumeScale == OPL3::VOLUME_NATIVE) opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); else { @@ -2561,11 +2611,13 @@ ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // V ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) { - if(adl_ins_list.empty()) FindAdlList(); - const unsigned meta = adl_ins_list[ins_idx]; - const adlinsdata &ains = opl->GetAdlMetaIns(meta); + MIDIplay *play = P->play; + OPL3 *opl = P->opl; + if(P->adl_ins_list.empty()) FindAdlList(); + const unsigned meta = P->adl_ins_list[P->ins_idx]; + const adlinsdata2 ains(adlins[meta]); - int tone = (cur_gm & 128) ? (cur_gm & 127) : (note + 50); + int tone = (P->cur_gm & 128) ? (P->cur_gm & 127) : (note + 50); if(ains.tone) { /*if(ains.tone < 20) @@ -2577,16 +2629,15 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) tone -= ains.tone - 128; } double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0)); - int i[2] = { ains.adlno1, ains.adlno2 }; int32_t adlchannel[2] = { 0, 3 }; - if(i[0] == i[1]) + if(ains.adl[0] == ains.adl[1]) { adlchannel[1] = -1; adlchannel[0] = 6; // single-op if(play->hooks.onDebugMessage) { play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, - "noteon at %d(%d) for %g Hz\n", adlchannel[0], i[0], hertz); + "noteon at %d for %g Hz\n", adlchannel[0], hertz); } } else @@ -2594,7 +2645,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) if(play->hooks.onDebugMessage) { play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, - "noteon at %d(%d) and %d(%d) for %g Hz\n", adlchannel[0], i[0], adlchannel[1], i[1], hertz); + "noteon at %d and %d for %g Hz\n", adlchannel[0], adlchannel[1], hertz); } } @@ -2604,7 +2655,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) for(unsigned c = 0; c < 2; ++c) { if(adlchannel[c] < 0) continue; - opl->Patch((uint16_t)adlchannel[c], (uint16_t)i[c]); + opl->Patch((uint16_t)adlchannel[c], ains.adl[c]); opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100); opl->Pan((uint16_t)adlchannel[c], 0x30); opl->NoteOn((uint16_t)adlchannel[c], hertz); @@ -2613,17 +2664,18 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset) { - cur_gm = (cur_gm + 256 + (uint32_t)offset) & 0xFF; + P->cur_gm = (P->cur_gm + 256 + (uint32_t)offset) & 0xFF; FindAdlList(); } ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) { - if(adl_ins_list.empty()) FindAdlList(); + //OPL3 *opl = P->opl; + if(P->adl_ins_list.empty()) FindAdlList(); const unsigned NumBanks = (unsigned)adl_getBanksCount(); - ins_idx = (uint32_t)((int32_t)ins_idx + (int32_t)adl_ins_list.size() + offset) % adl_ins_list.size(); + P->ins_idx = (uint32_t)((int32_t)P->ins_idx + (int32_t)P->adl_ins_list.size() + offset) % P->adl_ins_list.size(); - #if 0 +#if 0 UI.Color(15); std::fflush(stderr); std::printf("SELECTED G%c%d\t%s\n", @@ -2632,12 +2684,12 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) std::fflush(stdout); UI.Color(7); std::fflush(stderr); - #endif +#endif - for(unsigned a = 0; a < adl_ins_list.size(); ++a) + for(unsigned a = 0, n = P->adl_ins_list.size(); a < n; ++a) { - const unsigned i = adl_ins_list[a]; - const adlinsdata &ains = opl->GetAdlMetaIns(i); + const unsigned i = P->adl_ins_list[a]; + const adlinsdata2 ains(adlins[i]); char ToneIndication[8] = " "; if(ains.tone) @@ -2652,13 +2704,13 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) } std::printf("%s%s%s%u\t", ToneIndication, - ains.adlno1 != ains.adlno2 ? "[2]" : " ", - (ins_idx == a) ? "->" : "\t", + ains.adl[0] != ains.adl[1] ? "[2]" : " ", + (P->ins_idx == a) ? "->" : "\t", i ); for(unsigned bankno = 0; bankno < NumBanks; ++bankno) - if(banks[bankno][cur_gm] == i) + if(banks[bankno][P->cur_gm] == i) std::printf(" %u", bankno); std::printf("\n"); @@ -2692,9 +2744,9 @@ ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) NextGM(+1); break; case 3: - #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__)) +#if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__)) case 27: - #endif +#endif return false; default: const char *p = std::strchr(notes, ch); @@ -2704,4 +2756,130 @@ ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) return true; } -#endif//ADLMIDI_DISABLE_CPP_EXTRAS +#endif /* ADLMIDI_DISABLE_CPP_EXTRAS */ + +// Implement the user map data structure. + +bool MIDIplay::AdlChannel::users_empty() const +{ + return !users_first; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_find(Location loc) +{ + LocationData *user = NULL; + for(LocationData *curr = users_first; !user && curr; curr = curr->next) + if(curr->loc == loc) + user = curr; + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_allocate() +{ + // remove free cells front + LocationData *user = users_free_cells; + if(!user) + return NULL; + users_free_cells = user->next; + if(users_free_cells) + users_free_cells->prev = NULL; + // add to users front + if(users_first) + users_first->prev = user; + user->prev = NULL; + user->next = users_first; + users_first = user; + ++users_size; + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_find_or_create(Location loc) +{ + LocationData *user = users_find(loc); + if(!user) + { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = LocationData(); + user->prev = prev; + user->next = next; + user->loc = loc; + } + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_insert(const LocationData &x) +{ + LocationData *user = users_find(x.loc); + if(!user) + { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = x; + user->prev = prev; + user->next = next; + } + return user; +} + +void MIDIplay::AdlChannel::users_erase(LocationData *user) +{ + if(user->prev) + user->prev->next = user->next; + if(user->next) + user->next->prev = user->prev; + if(user == users_first) + users_first = user->next; + user->prev = NULL; + user->next = users_free_cells; + users_free_cells = user; + --users_size; +} + +void MIDIplay::AdlChannel::users_clear() +{ + users_first = NULL; + users_free_cells = users_cells; + users_size = 0; + for(size_t i = 0; i < users_max; ++i) + { + users_cells[i].prev = (i > 0) ? &users_cells[i - 1] : NULL; + users_cells[i].next = (i + 1 < users_max) ? &users_cells[i + 1] : NULL; + } +} + +void MIDIplay::AdlChannel::users_assign(const LocationData *users, size_t count) +{ + ADL_UNUSED(count);//Avoid warning for release builds + assert(count <= users_max); + if(users == users_first && users) + { + // self assignment + assert(users_size == count); + return; + } + users_clear(); + const LocationData *src_cell = users; + // move to the last + if(src_cell) + { + while(src_cell->next) + src_cell = src_cell->next; + } + // push cell copies in reverse order + while(src_cell) + { + LocationData *dst_cell = users_allocate(); + assert(dst_cell); + LocationData *prev = dst_cell->prev, *next = dst_cell->next; + *dst_cell = *src_cell; + dst_cell->prev = prev; + dst_cell->next = next; + src_cell = src_cell->prev; + } + assert(users_size == count); +} diff --git a/src/sound/adlmidi/adlmidi_opl3.cpp b/src/sound/adlmidi/adlmidi_opl3.cpp index 7ac8f309a..49e2e76c5 100644 --- a/src/sound/adlmidi/adlmidi_opl3.cpp +++ b/src/sound/adlmidi/adlmidi_opl3.cpp @@ -25,6 +25,21 @@ #ifdef ADLMIDI_HW_OPL static const unsigned OPLBase = 0x388; +#else +# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR) +# error "No emulators enabled. You must enable at least one emulator to use this library!" +# endif + +// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU +# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR +# include "chips/nuked_opl3.h" +# include "chips/nuked_opl3_v174.h" +# endif + +// DosBox 0.74 OPL3 emulator, Well-accurate and fast +# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR +# include "chips/dosbox_opl3.h" +# endif #endif #ifdef DISABLE_EMBEDDED_BANKS @@ -114,87 +129,56 @@ static const unsigned short Channels[23] = Ports: ??? */ - -const adlinsdata &OPL3::GetAdlMetaIns(size_t n) -{ - return (n & DynamicMetaInstrumentTag) ? - dynamic_metainstruments[n & ~DynamicMetaInstrumentTag] - : adlins[n]; -} - -size_t OPL3::GetAdlMetaNumber(size_t midiins) -{ - return (AdlBank == ~0u) ? - (midiins | DynamicMetaInstrumentTag) - : banks[AdlBank][midiins]; -} - -const adldata &OPL3::GetAdlIns(size_t insno) -{ - return (insno & DynamicInstrumentTag) - ? dynamic_instruments[insno & ~DynamicInstrumentTag] - : adl[insno]; -} - void OPL3::setEmbeddedBank(unsigned int bank) { AdlBank = bank; //Embedded banks are supports 128:128 GM set only - dynamic_percussion_offset = 128; - dynamic_melodic_banks.clear(); - dynamic_percussion_banks.clear(); + dynamic_banks.clear(); + + if(bank >= static_cast(maxAdlBanks())) + return; + + Bank *bank_pair[2] = + { + &dynamic_banks[0], + &dynamic_banks[PercussionTag] + }; + + for(unsigned i = 0; i < 256; ++i) + { + size_t meta = banks[bank][i]; + adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128]; + ins = adlinsdata2(adlins[meta]); + } } +static adlinsdata2 makeEmptyInstrument() +{ + adlinsdata2 ins; + memset(&ins, 0, sizeof(adlinsdata2)); + ins.flags = adlinsdata::Flag_NoSound; + return ins; +} + +const adlinsdata2 OPL3::emptyInstrument = makeEmptyInstrument(); OPL3::OPL3() : - dynamic_percussion_offset(128), - DynamicInstrumentTag(0x8000u), - DynamicMetaInstrumentTag(0x4000000u), NumCards(1), - AdlBank(0), NumFourOps(0), HighTremoloMode(false), HighVibratoMode(false), AdlPercussionMode(false), - LogarithmicVolumes(false), - //CartoonersVolumes(false), m_musicMode(MODE_MIDI), m_volumeScale(VOLUME_Generic) -{} - -void OPL3::Poke(size_t card, uint32_t index, uint32_t value) { - #ifdef ADLMIDI_HW_OPL - (void)card; - unsigned o = index >> 8; - unsigned port = OPLBase + o * 2; - - #ifdef __DJGPP__ - outportb(port, index); - for(unsigned c = 0; c < 6; ++c) inportb(port); - outportb(port + 1, value); - for(unsigned c = 0; c < 35; ++c) inportb(port); - #endif//__DJGPP__ - - #ifdef __WATCOMC__ - outp(port, index); - for(uint16_t c = 0; c < 6; ++c) inp(port); - outp(port + 1, value); - for(uint16_t c = 0; c < 35; ++c) inp(port); - #endif//__WATCOMC__ - - #else//ADLMIDI_HW_OPL - - #ifdef ADLMIDI_USE_DOSBOX_OPL - cards[card].WriteReg(index, static_cast(value)); - #else - OPL3_WriteReg(&cards[card], static_cast(index), static_cast(value)); - #endif - - #endif//ADLMIDI_HW_OPL +#ifdef DISABLE_EMBEDDED_BANKS + AdlBank = ~0u; +#else + setEmbeddedBank(0); +#endif } -void OPL3::PokeN(size_t card, uint16_t index, uint8_t value) +void OPL3::Poke(size_t card, uint16_t index, uint8_t value) { #ifdef ADLMIDI_HW_OPL (void)card; @@ -216,11 +200,7 @@ void OPL3::PokeN(size_t card, uint16_t index, uint8_t value) #endif//__WATCOMC__ #else - #ifdef ADLMIDI_USE_DOSBOX_OPL - cards[card].WriteReg(static_cast(index), value); - #else - OPL3_WriteReg(&cards[card], index, value); - #endif + cardsOP2[card]->writeReg(index, value); #endif } @@ -277,10 +257,9 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) volume = 63; size_t card = c / 23, cc = c % 23; - size_t i = ins[c]; + const adldata &adli = ins[c]; uint16_t o1 = Operators[cc * 2 + 0]; uint16_t o2 = Operators[cc * 2 + 1]; - const adldata &adli = GetAdlIns(i); uint8_t x = adli.modulator_40, y = adli.carrier_40; uint16_t mode = 1; // 2-op AM @@ -290,22 +269,22 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) } else if(four_op_category[c] == 1 || four_op_category[c] == 2) { - size_t i0, i1; + const adldata *i0, *i1; if(four_op_category[c] == 1) { - i0 = i; - i1 = ins[c + 3]; + i0 = &adli; + i1 = &ins[c + 3]; mode = 2; // 4-op xx-xx ops 1&2 } else { - i0 = ins[c - 3]; - i1 = i; + i0 = &ins[c - 3]; + i1 = &adli; mode = 6; // 4-op xx-xx ops 3&4 } - mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2; + mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2; } static const bool do_ops[10][2] = @@ -372,14 +351,13 @@ void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 } }*/ -void OPL3::Patch(uint16_t c, size_t i) +void OPL3::Patch(uint16_t c, const adldata &adli) { uint16_t card = c / 23, cc = c % 23; static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; - ins[c] = i; + ins[c] = adli; uint16_t o1 = Operators[cc * 2 + 0]; uint16_t o2 = Operators[cc * 2 + 1]; - const adldata &adli = GetAdlIns(i); unsigned x = adli.modulator_E862, y = adli.carrier_E862; for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8) @@ -395,7 +373,7 @@ void OPL3::Pan(unsigned c, unsigned value) unsigned card = c / 23, cc = c % 23; if(Channels[cc] != 0xFFF) - Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value); + Poke(card, 0xC0 + Channels[cc], ins[c].feedconn | value); } void OPL3::Silence() // Silence all OPL channels. @@ -479,9 +457,8 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) m_volumeScale = OPL3::VOLUME_Generic; break; - case ADLMIDI_VolumeModel_CMF: - LogarithmicVolumes = true; - m_volumeScale = OPL3::VOLUME_CMF; + case ADLMIDI_VolumeModel_NativeOPL3: + m_volumeScale = OPL3::VOLUME_NATIVE; break; case ADLMIDI_VolumeModel_DMX: @@ -498,26 +475,36 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) } } -void OPL3::Reset(unsigned long PCM_RATE) +#ifndef ADLMIDI_HW_OPL +void OPL3::ClearChips() { - #ifndef ADLMIDI_HW_OPL - #ifdef ADLMIDI_USE_DOSBOX_OPL - DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields - #else - _opl3_chip emptyChip; - std::memset(&emptyChip, 0, sizeof(_opl3_chip)); - #endif - cards.clear(); - #endif + for(size_t i = 0; i < cardsOP2.size(); i++) + cardsOP2[i].reset(NULL); + cardsOP2.clear(); +} +#endif + +void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +{ +#ifndef ADLMIDI_HW_OPL + ClearChips(); +#else + (void)emulator; (void)PCM_RATE; +#endif +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + (void)audioTickHandler; +#endif ins.clear(); pit.clear(); regBD.clear(); - #ifndef ADLMIDI_HW_OPL - cards.resize(NumCards, emptyChip); - #endif + +#ifndef ADLMIDI_HW_OPL + cardsOP2.resize(NumCards, AdlMIDI_SPtr()); +#endif + NumChannels = NumCards * 23; - ins.resize(NumChannels, 189); + ins.resize(NumChannels, adl[adlDefaultNumber]); pit.resize(NumChannels, 0); regBD.resize(NumCards, 0); four_op_category.resize(NumChannels, 0); @@ -536,24 +523,45 @@ void OPL3::Reset(unsigned long PCM_RATE) }; unsigned fours = NumFourOps; - for(unsigned card = 0; card < NumCards; ++card) + for(size_t i = 0; i < NumCards; ++i) { - #ifndef ADLMIDI_HW_OPL - # ifdef ADLMIDI_USE_DOSBOX_OPL - cards[card].Init(PCM_RATE); - # else - OPL3_Reset(&cards[card], static_cast(PCM_RATE)); - # endif - #endif +#ifndef ADLMIDI_HW_OPL + OPLChipBase *chip; + switch(emulator) + { + default: +#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR + case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */ + chip = new NukedOPL3; + break; + case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */ + chip = new NukedOPL3v174; + break; +#endif +#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR + case ADLMIDI_EMU_DOSBOX: + chip = new DosBoxOPL3; + break; +#endif + } + cardsOP2[i].reset(chip); + chip->setChipId(i); + chip->setRate((uint32_t)PCM_RATE); + if(runAtPcmRate) + chip->setRunningAtPcmRate(true); +# if defined(ADLMIDI_AUDIO_TICK_HANDLER) + chip->setAudioTickHandlerInstance(audioTickHandler); +# endif +#endif // ADLMIDI_HW_OPL - for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00); + for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00); for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2) - PokeN(card, data[a], static_cast(data[a + 1])); - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); + Poke(i, data[a], static_cast(data[a + 1])); + Poke(i, 0x0BD, regBD[i] = (HighTremoloMode * 0x80 + + HighVibratoMode * 0x40 + + AdlPercussionMode * 0x20)); unsigned fours_this_card = std::min(fours, 6u); - Poke(card, 0x104, (1 << fours_this_card) - 1); + Poke(i, 0x104, (1 << fours_this_card) - 1); //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card); fours -= fours_this_card; } diff --git a/src/sound/adlmidi/adlmidi_private.cpp b/src/sound/adlmidi/adlmidi_private.cpp index 1f3cb7ddd..1ee7c4a33 100644 --- a/src/sound/adlmidi/adlmidi_private.cpp +++ b/src/sound/adlmidi/adlmidi_private.cpp @@ -25,6 +25,13 @@ std::string ADLMIDI_ErrorString; +// Generator callback on audio rate ticks + +void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) +{ + reinterpret_cast(instance)->AudioTick(chipId, rate); +} + int adlRefreshNumCards(ADL_MIDIPlayer *device) { unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; @@ -34,19 +41,22 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) if(play->opl.AdlBank == ~0u) { //For custom bank - for(size_t a = 0; a < play->opl.dynamic_metainstruments.size(); ++a) + OPL3::BankMap::iterator it = play->opl.dynamic_banks.begin(); + OPL3::BankMap::iterator end = play->opl.dynamic_banks.end(); + for(; it != end; ++it) { - size_t div = (a >= play->opl.dynamic_percussion_offset) ? 1 : 0; - ++n_total[div]; - adlinsdata &ins = play->opl.dynamic_metainstruments[a]; - if((ins.adlno1 != ins.adlno2) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0)) - ++n_fourop[div]; + uint16_t bank = it->first; + unsigned div = (bank & OPL3::PercussionTag) ? 1 : 0; + for(unsigned i = 0; i < 128; ++i) + { + adlinsdata2 &ins = it->second.ins[i]; + if(ins.flags & adlinsdata::Flag_NoSound) + continue; + if((ins.adl[0] != ins.adl[1]) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0)) + ++n_fourop[div]; + ++n_total[div]; + } } - - play->m_setup.NumFourOps = - (n_fourop[0] >= 128 * 7 / 8) ? play->m_setup.NumCards * 6 - : (n_fourop[0] < 128 * 1 / 8) ? (n_fourop[1] > 0 ? 4 : 0) - : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); } else { @@ -57,27 +67,34 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) if(insno == 198) continue; ++n_total[a / 128]; - const adlinsdata &ins = adlins[insno]; - if((ins.adlno1 != ins.adlno2) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0)) + adlinsdata2 ins(adlins[insno]); + if(ins.flags & adlinsdata::Flag_Real4op) ++n_fourop[a / 128]; } - - play->m_setup.NumFourOps = - (n_fourop[0] >= (n_total[0] % 128) * 7 / 8) ? play->m_setup.NumCards * 6 - : (n_fourop[0] < (n_total[0] % 128) * 1 / 8) ? 0 - : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); } - play->opl.NumFourOps = play->m_setup.NumFourOps; + unsigned numFourOps = 0; - if(n_fourop[0] >= n_total[0] * 15 / 16 && play->m_setup.NumFourOps == 0) - { - play->setErrorString("ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n" - " The results (silence + much cpu load) would be probably\n" - " not what you want, therefore ignoring the request.\n"); - return -1; - } + // All 2ops (no 4ops) + if((n_fourop[0] == 0) && (n_fourop[1] == 0)) + numFourOps = 0; + // All 2op melodics and Some (or All) 4op drums + else if((n_fourop[0] == 0) && (n_fourop[1] > 0)) + numFourOps = 2; + // Many 4op melodics + else if((n_fourop[0] >= (n_total[0] * 7) / 8)) + numFourOps = 6; + // Few 4op melodics + else if(n_fourop[0] > 0) + numFourOps = 4; + +/* //Old formula + unsigned NumFourOps = ((n_fourop[0] == 0) && (n_fourop[1] == 0)) ? 0 + : (n_fourop[0] >= (n_total[0] * 7) / 8) ? play->m_setup.NumCards * 6 + : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); +*/ + + play->opl.NumFourOps = play->m_setup.NumFourOps = (numFourOps * play->m_setup.NumCards); return 0; } - diff --git a/src/sound/adlmidi/adlmidi_private.hpp b/src/sound/adlmidi/adlmidi_private.hpp index eace3a736..0e63b5a61 100644 --- a/src/sound/adlmidi/adlmidi_private.hpp +++ b/src/sound/adlmidi/adlmidi_private.hpp @@ -35,6 +35,9 @@ # endif #endif +// Require declarations of unstable API for extern "C" +#define ADLMIDI_UNSTABLE_API + #ifdef _WIN32 #define NOMINMAX #endif @@ -87,12 +90,14 @@ typedef int32_t ssize_t; #include #include #include +#include #include // vector #include // deque #include // exp, log, ceil #if defined(__WATCOMC__) #include // round, sqrt #endif +#include #include #include #include // numeric_limit @@ -104,22 +109,31 @@ typedef int32_t ssize_t; #include #include -#ifdef _MSC_VER -#pragma warning(disable:4319) -#pragma warning(disable:4267) -#pragma warning(disable:4244) -#pragma warning(disable:4146) -#pragma warning(disable:4800) +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT8_MIN +#define INT8_MIN (-0x7f - 1) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-0x7fffffff - 1) +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7f +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff #endif #include "fraction.hpp" - #ifndef ADLMIDI_HW_OPL -#ifdef ADLMIDI_USE_DOSBOX_OPL -#include "dbopl.h" -#else -#include "nukedopl3.h" -#endif +#include "chips/opl_chip_base.h" #endif #include "adldata.hh" @@ -127,46 +141,63 @@ typedef int32_t ssize_t; #ifndef ADLMIDI_DISABLE_CPP_EXTRAS #include "adlmidi.hpp" //Extra C++ API #endif +#include "adlmidi_ptr.hpp" +#include "adlmidi_bankmap.h" #define ADL_UNUSED(x) (void)x +#define OPL_PANNING_LEFT 0x10 +#define OPL_PANNING_RIGHT 0x20 +#define OPL_PANNING_BOTH 0x30 + extern std::string ADLMIDI_ErrorString; /* - Smart pointer for C heaps, created with malloc() call. - FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it + Sample conversions to various formats */ -template -class AdlMIDI_CPtr +template +inline Real adl_cvtReal(int32_t x) { - PTR *m_p; -public: - AdlMIDI_CPtr() : m_p(NULL) {} - ~AdlMIDI_CPtr() - { - reset(NULL); - } + return x * ((Real)1 / INT16_MAX); +} - void reset(PTR *p = NULL) - { - if(m_p) - free(m_p); - m_p = p; - } +inline int32_t adl_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? (INT16_MIN) : x; + x = (x > INT16_MAX) ? (INT16_MAX) : x; + return x; +} - PTR *get() - { - return m_p; - } - PTR &operator*() - { - return *m_p; - } - PTR *operator->() - { - return m_p; - } -}; +inline int32_t adl_cvtS8(int32_t x) +{ + return adl_cvtS16(x) / 256; +} +inline int32_t adl_cvtS24(int32_t x) +{ + return adl_cvtS16(x) * 256; +} +inline int32_t adl_cvtS32(int32_t x) +{ + return adl_cvtS16(x) * 65536; +} +inline int32_t adl_cvtU16(int32_t x) +{ + return adl_cvtS16(x) - INT16_MIN; +} +inline int32_t adl_cvtU8(int32_t x) +{ + return (adl_cvtS16(x) / 256) - INT8_MIN; +} +inline int32_t adl_cvtU24(int32_t x) +{ + enum { int24_min = -(1 << 23) }; + return adl_cvtS24(x) - int24_min; +} +inline int32_t adl_cvtU32(int32_t x) +{ + // unsigned operation because overflow on signed integers is undefined + return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN; +} class MIDIplay; struct ADL_MIDIPlayer; @@ -179,34 +210,26 @@ public: char ____padding[4]; ADL_MIDIPlayer *_parent; #ifndef ADLMIDI_HW_OPL -# ifdef ADLMIDI_USE_DOSBOX_OPL - std::vector cards; -# else - std::vector<_opl3_chip> cards; -# endif + std::vector > cardsOP2; #endif private: - std::vector ins; // index to adl[], cached, needed by Touch() + std::vector ins; // patch data, cached, needed by Touch() std::vector pit; // value poked to B0, cached, needed by NoteOff)( std::vector regBD; friend int adlRefreshNumCards(ADL_MIDIPlayer *device); - //! Meta information about every instrument - std::vector dynamic_metainstruments; // Replaces adlins[] when CMF file - //! Raw instrument data ready to be sent to the chip - std::vector dynamic_instruments; // Replaces adl[] when CMF file - size_t dynamic_percussion_offset; - - typedef std::map BankMap; - BankMap dynamic_melodic_banks; - BankMap dynamic_percussion_banks; - const unsigned DynamicInstrumentTag /* = 0x8000u*/, - DynamicMetaInstrumentTag /* = 0x4000000u*/; - const adlinsdata &GetAdlMetaIns(size_t n); - size_t GetAdlMetaNumber(size_t midiins); - const adldata &GetAdlIns(size_t insno); +public: + struct Bank + { + adlinsdata2 ins[128]; + }; + typedef BasicBankMap BankMap; + BankMap dynamic_banks; + AdlBankSetup dynamic_bank_setup; public: void setEmbeddedBank(unsigned int bank); + static const adlinsdata2 emptyInstrument; + enum { PercussionTag = 1 << 15 }; //! Total number of running concurrent emulated chips unsigned int NumCards; @@ -222,8 +245,10 @@ public: bool AdlPercussionMode; //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. bool ScaleModulators; - //! Required to play CMF files. Can be turned on by using of "CMF" volume model - bool LogarithmicVolumes; + //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. + bool runAtPcmRate; + // ! Required to play CMF files. Can be turned on by using of "CMF" volume model + //bool LogarithmicVolumes; //[REPLACED WITH "m_volumeScale == VOLUME_NATIVE", DEPRECATED!!!] // ! Required to play EA-MUS files [REPLACED WITH "m_musicMode", DEPRECATED!!!] //bool CartoonersVolumes; enum MusicMode @@ -239,7 +264,7 @@ public: enum VolumesScale { VOLUME_Generic, - VOLUME_CMF, + VOLUME_NATIVE, VOLUME_DMX, VOLUME_APOGEE, VOLUME_9X @@ -255,21 +280,23 @@ public: // 7 = percussion Hihat // 8 = percussion slave - void Poke(size_t card, uint32_t index, uint32_t value); - void PokeN(size_t card, uint16_t index, uint8_t value); + void Poke(size_t card, uint16_t index, uint8_t value); void NoteOff(size_t c); void NoteOn(unsigned c, double hertz); void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127); //void Touch(unsigned c, unsigned volume) - void Patch(uint16_t c, size_t i); + void Patch(uint16_t c, const adldata &adli); void Pan(unsigned c, unsigned value); void Silence(); void updateFlags(); void updateDeepFlags(); void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel); - void Reset(unsigned long PCM_RATE); + #ifndef ADLMIDI_HW_OPL + void ClearChips(); + #endif + void Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); }; @@ -455,7 +482,7 @@ public: bool eof() { if(fp) - return std::feof(fp); + return (std::feof(fp) != 0); else return mp_tell >= mp_size; } @@ -473,9 +500,15 @@ public: uint8_t bank_lsb, bank_msb; uint8_t patch; uint8_t volume, expression; - uint8_t panning, vibrato, sustain; + uint8_t panning, vibrato, aftertouch, sustain; + //! Per note Aftertouch values + uint8_t noteAftertouch[128]; + //! Is note aftertouch has any non-zero value + bool noteAfterTouchInUse; char ____padding[6]; - double bend, bendsense; + int bend; + double bendsense; + int bendsense_lsb, bendsense_msb; double vibpos, vibspeed, vibdepth; int64_t vibdelay; uint8_t lastlrpn, lastmrpn; @@ -484,66 +517,220 @@ public: bool is_xg_percussion; struct NoteInfo { + uint8_t note; + bool active; // Current pressure uint8_t vol; - char ____padding[1]; + // Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; // Tone selected on noteon: int16_t tone; char ____padding2[4]; - // Patch selected on noteon; index to banks[AdlBank][] + // Patch selected on noteon; index to bank.ins[] size_t midiins; - // Index to physical adlib data structure, adlins[] - size_t insmeta; + // Patch selected + const adlinsdata2 *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans, + }; struct Phys { + //! Destination chip channel + uint16_t chip_chan; //! ins, inde to adl[] - size_t insId; + adldata ains; //! Is this voice must be detunable? bool pseudo4op; + void assign(const Phys &oth) + { + ains = oth.ains; + pseudo4op = oth.pseudo4op; + } bool operator==(const Phys &oth) const { - return (insId == oth.insId) && (pseudo4op == oth.pseudo4op); + return (ains == oth.ains) && (pseudo4op == oth.pseudo4op); } bool operator!=(const Phys &oth) const { return !operator==(oth); } }; - typedef std::map PhysMap; - // List of OPL3 channels it is currently occupying. - std::map phys; + //! List of OPL3 channels it is currently occupying. + Phys chip_channels[MaxNumPhysItemCount]; + //! Count of used channels. + unsigned chip_channels_count; + // + Phys *phys_find(unsigned chip_chan) + { + Phys *ph = NULL; + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + return ph; + } + Phys *phys_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = chip_chan; + } + } + return ph; + } + Phys *phys_ensure_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos < static_cast(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + phys_erase_at(ph); + } }; - typedef std::map activenotemap_t; - typedef activenotemap_t::iterator activenoteiterator; char ____padding2[5]; - activenotemap_t activenotes; + NoteInfo activenotes[128]; + + struct activenoteiterator + { + explicit activenoteiterator(NoteInfo *info = 0) + : ptr(info) {} + activenoteiterator &operator++() + { + if(ptr->note == 127) + ptr = 0; + else + for(++ptr; ptr && !ptr->active;) + ptr = (ptr->note == 127) ? 0 : (ptr + 1); + return *this; + } + activenoteiterator operator++(int) + { + activenoteiterator pos = *this; + ++*this; + return pos; + } + NoteInfo &operator*() const + { return *ptr; } + NoteInfo *operator->() const + { return ptr; } + bool operator==(activenoteiterator other) const + { return ptr == other.ptr; } + bool operator!=(activenoteiterator other) const + { return ptr != other.ptr; } + operator NoteInfo *() const + { return ptr; } + private: + NoteInfo *ptr; + }; + + activenoteiterator activenotes_begin() + { + activenoteiterator it(activenotes); + return (it->active) ? it : ++it; + } + + activenoteiterator activenotes_find(uint8_t note) + { + assert(note < 128); + return activenoteiterator( + activenotes[note].active ? &activenotes[note] : 0); + } + + activenoteiterator activenotes_ensure_find(uint8_t note) + { + activenoteiterator it = activenotes_find(note); + assert(it); + return it; + } + + std::pair activenotes_insert(uint8_t note) + { + assert(note < 128); + NoteInfo &info = activenotes[note]; + bool inserted = !info.active; + if(inserted) info.active = true; + return std::pair(activenoteiterator(&info), inserted); + } + + void activenotes_erase(activenoteiterator pos) + { + if(pos) + pos->active = false; + } + + bool activenotes_empty() + { + return !activenotes_begin(); + } + + void activenotes_clear() + { + for(uint8_t i = 0; i < 128; ++i) { + activenotes[i].note = i; + activenotes[i].active = false; + } + } + void reset() { - portamento = 0; + resetAllControllers(); + patch = 0; + vibpos = 0; bank_lsb = 0; bank_msb = 0; - patch = 0; - volume = 100; - expression = 127; - panning = 0x30; - vibrato = 0; - sustain = 0; - bend = 0.0; - bendsense = 2 / 8192.0; - vibpos = 0; - vibspeed = 2 * 3.141592653 * 5.0; - vibdepth = 0.5 / 127; - vibdelay = 0; lastlrpn = 0; lastmrpn = 0; nrpn = false; - brightness = 127; is_xg_percussion = false; } - MIDIchannel() - : activenotes() + void resetAllControllers() { + bend = 0; + bendsense_msb = 2; + bendsense_lsb = 0; + updateBendSensitivity(); + volume = 100; + expression = 127; + sustain = 0; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay = 0; + panning = OPL_PANNING_BOTH; + portamento = 0; + brightness = 127; + } + bool hasVibrato() + { + return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; + } + void updateBendSensitivity() + { + int cent = bendsense_msb * 128 + bendsense_lsb; + bendsense = cent * (1.0 / (128 * 8192)); + } + MIDIchannel() + { + activenotes_clear(); reset(); } }; @@ -551,36 +738,70 @@ public: // Additional information about OPL3 channels struct AdlChannel { - // For collisions struct Location { uint16_t MidCh; uint8_t note; - bool operator==(const Location &b) const - { - return MidCh == b.MidCh && note == b.note; - } - bool operator< (const Location &b) const - { - return MidCh < b.MidCh || (MidCh == b.MidCh && note < b.note); - } - char ____padding[1]; + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } }; struct LocationData { + LocationData *prev, *next; + Location loc; bool sustained; char ____padding[7]; MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on int64_t kon_time_until_neglible; int64_t vibdelay; }; - typedef std::map users_t; - users_t users; // If the channel is keyoff'd int64_t koff_time_until_neglible; + + enum { users_max = 128 }; + LocationData *users_first, *users_free_cells; + LocationData users_cells[users_max]; + unsigned users_size; + + bool users_empty() const; + LocationData *users_find(Location loc); + LocationData *users_allocate(); + LocationData *users_find_or_create(Location loc); + LocationData *users_insert(const LocationData &x); + void users_erase(LocationData *user); + void users_clear(); + void users_assign(const LocationData *users, size_t count); + // For channel allocation: - AdlChannel(): users(), koff_time_until_neglible(0) { } + AdlChannel(): koff_time_until_neglible(0) + { + users_clear(); + } + + AdlChannel(const AdlChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible) + { + if(oth.users_first) + { + users_first = NULL; + users_assign(oth.users_first, oth.users_size); + } + else + users_clear(); + } + + AdlChannel &operator=(const AdlChannel &oth) + { + koff_time_until_neglible = oth.koff_time_until_neglible; + users_assign(oth.users_first, oth.users_size); + return *this; + } + void AddAge(int64_t ms); }; @@ -713,6 +934,8 @@ public: struct Setup { + int emulator; + bool runAtPcmRate; unsigned int AdlBank; unsigned int NumFourOps; unsigned int NumCards; @@ -724,6 +947,7 @@ public: //unsigned int SkipForward; bool loopingIsEnabled; int ScaleModulators; + bool fullRangeBrightnessCC74; double delay; double carry; @@ -760,6 +984,11 @@ private: char ____padding[7]; std::vector ch; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; + + //! Audio tick counter + uint32_t m_audioTickCounter; #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER std::vector > TrackData; @@ -832,11 +1061,25 @@ public: #endif OPL3 opl; - int16_t outBuf[1024]; + int32_t outBuf[1024]; Setup m_setup; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief Utility function to read Big-Endian integer from raw binary data + * @param buffer Pointer to raw binary buffer + * @param nbytes Count of bytes to parse integer + * @return Extracted unsigned integer + */ static uint64_t ReadBEint(const void *buffer, size_t nbytes); + + /** + * @brief Utility function to read Little-Endian integer from raw binary data + * @param buffer Pointer to raw binary buffer + * @param nbytes Count of bytes to parse integer + * @return Extracted unsigned integer + */ static uint64_t ReadLEint(const void *buffer, size_t nbytes); /** @@ -853,6 +1096,7 @@ public: * @return Unsigned integer that conains parsed variable-length value */ uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok); +#endif bool LoadBank(const std::string &filename); bool LoadBank(const void *data, size_t size); @@ -943,6 +1187,9 @@ public: void realTime_panic(); + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); + private: enum { @@ -951,7 +1198,9 @@ private: Upd_Volume = 0x4, Upd_Pitch = 0x8, Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, - Upd_Off = 0x20 + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upt_OffMute = Upd_Off + Upd_Mute }; void NoteUpdate(uint16_t MidCh, @@ -966,7 +1215,7 @@ private: // Determine how good a candidate this adlchannel // would be for playing a note from this instrument. - int64_t CalculateAdlChannelGoodness(unsigned c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; + int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; // A new note will be played on this channel using this instrument. // Kill existing notes on this channel (or don't, if we do arpeggio) @@ -974,7 +1223,7 @@ private: void KillOrEvacuate( size_t from_channel, - AdlChannel::users_t::iterator j, + AdlChannel::LocationData *j, MIDIchannel::activenoteiterator i); void Panic(); void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); @@ -1009,6 +1258,7 @@ struct FourChars }; */ +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); extern int adlRefreshNumCards(ADL_MIDIPlayer *device); diff --git a/src/sound/adlmidi/adlmidi_ptr.hpp b/src/sound/adlmidi/adlmidi_ptr.hpp new file mode 100644 index 000000000..7d1086b5f --- /dev/null +++ b/src/sound/adlmidi/adlmidi_ptr.hpp @@ -0,0 +1,217 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PTR_HPP_THING +#define ADLMIDI_PTR_HPP_THING + +#include // swap +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct ADLMIDI_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; +template +struct ADLMIDI_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; +struct ADLMIDI_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_UPtr +{ + T *m_p; +public: + explicit AdlMIDI_UPtr(T *p) + : m_p(p) {} + ~AdlMIDI_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p) { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + void swap(AdlMIDI_UPtr &other) + { + std::swap(m_p, other.m_p); + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +private: + AdlMIDI_UPtr(const AdlMIDI_UPtr &); + AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &); +}; + +template +void swap(AdlMIDI_UPtr &a, AdlMIDI_UPtr &b) +{ + a.swap(b); +} + +/** + Unique pointer for arrays. + */ +template +class AdlMIDI_UPtrArray : + public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_UPtrArray(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +/** + Unique pointer for C memory. + */ +template +class AdlMIDI_CPtr : + public AdlMIDI_UPtr< T, ADLMIDI_CDelete > +{ +public: + explicit AdlMIDI_CPtr(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {} +}; + +/* + Shared pointer with non-atomic counter + FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_SPtr +{ + T *m_p; + size_t *m_counter; +public: + explicit AdlMIDI_SPtr(T *p = NULL) + : m_p(p), m_counter(p ? new size_t(1) : NULL) {} + ~AdlMIDI_SPtr() + { + reset(NULL); + } + + AdlMIDI_SPtr(const AdlMIDI_SPtr &other) + : m_p(other.m_p), m_counter(other.m_counter) + { + if(m_counter) + ++*m_counter; + } + + AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other) + { + if(this == &other) + return *this; + reset(); + m_p = other.m_p; + m_counter = other.m_counter; + if(m_counter) + ++*m_counter; + return *this; + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p && --*m_counter == 0) { + Deleter del; + del(m_p); + if(!p) { + delete m_counter; + m_counter = NULL; + } + } + m_p = p; + if(p) { + if(!m_counter) + m_counter = new size_t; + *m_counter = 1; + } + } + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +}; + +/** + Shared pointer for arrays. + */ +template +class AdlMIDI_SPtrArray : + public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_SPtrArray(T *p = NULL) + : AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +#endif //ADLMIDI_PTR_HPP_THING diff --git a/src/sound/adlmidi/chips/dosbox/dbopl.cpp b/src/sound/adlmidi/chips/dosbox/dbopl.cpp new file mode 100644 index 000000000..7d78c5f05 --- /dev/null +++ b/src/sound/adlmidi/chips/dosbox/dbopl.cpp @@ -0,0 +1,1618 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. + Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 + Except for the table generation it's all integer math + Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms + The generation was based on the MAME implementation but tried to have it use less memory and be faster in general + MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times + + //TODO Don't delay first operator 1 sample in opl3 mode + //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter + //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not + + //DUNNO Keyon in 4op, switch to 2op without keyoff. +*/ + + + +#include +#include +#include +#include "dbopl.h" + +#if defined(__GNUC__) && __GNUC__ > 3 +#define INLINE inline __attribute__((__always_inline__)) +#elif defined(_MSC_VER) +#define INLINE __forceinline +#else +#define INLINE inline +#endif + +#if defined(__GNUC__) +#if !defined(__clang__) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#else // !defined(__clang__) +#if !defined (__c2__) && defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#endif // __has_builtin(__builtin_expect) +#endif // !defined (__c2__) && defined(__has_builtin) +#endif // !defined(__clang__) +#endif // defined(__GNUC__) + +#if !defined(GCC_LIKELY) +#define GCC_LIKELY(x) (x) +#define GCC_UNLIKELY(x) (x) +#endif + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +namespace DBOPL { + +#define OPLRATE ((double)(14318180.0 / 288.0)) +#define TREMOLO_TABLE 52 + +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION +//Wave bits available in the top of the 32bit range +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif +#define WAVE_SH ( 32 - WAVE_BITS ) +#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) + +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + +//Maximum amount of attenuation bits +//Envelope goes to 511, 9 bits +#if (DBOPL_WAVE == WAVE_TABLEMUL ) +//Uses the value directly +#define ENV_BITS ( 9 ) +#else +//Add 3 bits here for more accuracy and would have to be shifted up either way +#define ENV_BITS ( 9 ) +#endif +//Limits of the envelope with those bits and when the envelope goes silent +#define ENV_MIN 0 +#define ENV_EXTRA ( ENV_BITS - 9 ) +#define ENV_MAX ( 511 << ENV_EXTRA ) +#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) +#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) + +//Attack/decay/release rate counter shift +#define RATE_SH 24 +#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) +//Has to fit within 16bit lookuptable +#define MUL_SH 16 + +//Check some ranges +#if ENV_EXTRA > 3 +#error Too many envelope bits +#endif + + +//How much to substract from the base value for the final attenuation +static const Bit8u KslCreateTable[16] = { + //0 will always be be lower than 7 * 8 + 64, 32, 24, 19, + 16, 12, 11, 10, + 8, 6, 5, 4, + 3, 2, 1, 0, +}; + +#define M(_X_) ((Bit8u)( (_X_) * 2)) +static const Bit8u FreqCreateTable[16] = { + M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), + M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) +}; +#undef M + +//We're not including the highest attack rate, that gets a special value +static const Bit8u AttackSamplesTable[13] = { + 69, 55, 46, 40, + 35, 29, 23, 20, + 19, 15, 11, 10, + 9 +}; +//On a real opl these values take 8 samples to reach and are based upon larger tables +static const Bit8u EnvelopeIncreaseTable[13] = { + 4, 5, 6, 7, + 8, 10, 12, 14, + 16, 20, 24, 28, + 32, +}; + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) +static Bit16u ExpTable[ 256 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +//PI table used by WAVEHANDLER +static Bit16u SinTable[ 512 ]; +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Layout of the waveform table in 512 entry intervals +//With overlapping waves we reduce the table to half it's size + +// | |//\\|____|WAV7|//__|/\ |____|/\/\| +// |\\//| | |WAV7| | \/| | | +// |06 |0126|17 |7 |3 |4 |4 5 |5 | + +//6 is just 0 shifted and masked + +static Bit16s WaveTable[ 8 * 512 ]; +//Distance into WaveTable the wave starts +static const Bit16u WaveBaseTable[8] = { + 0x000, 0x200, 0x200, 0x800, + 0xa00, 0xc00, 0x100, 0x400, + +}; +//Mask the counter with this +static const Bit16u WaveMaskTable[8] = { + 1023, 1023, 511, 511, + 1023, 1023, 512, 1023, +}; + +//Where to start the counter on at keyon +static const Bit16u WaveStartTable[8] = { + 512, 0, 0, 0, + 0, 512, 512, 256, +}; +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) +static Bit16u MulTable[ 384 ]; +#endif + +static Bit8u KslTable[ 8 * 16 ]; +static Bit8u TremoloTable[ TREMOLO_TABLE ]; +//Start of a channel behind the chip struct start +static Bit16u ChanOffsetTable[32]; +//Start of an operator behind the chip struct start +static Bit16u OpOffsetTable[64]; + +//The lower bits are the shift of the operator vibrato value +//The highest bit is right shifted to generate -1 or 0 for negation +//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 +static const Bit8s VibratoTable[ 8 ] = { + 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, + 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 +}; + +//Shift strength for the ksl value determined by ksl strength +static const Bit8u KslShiftTable[4] = { + 31,1,2,0 +}; + +//Generate a table index and table shift value using input value from a selected rate +static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { + if ( val < 13 * 4 ) { //Rate 0 - 12 + shift = 12 - ( val >> 2 ); + index = val & 3; + } else if ( val < 15 * 4 ) { //rate 13 - 14 + shift = 0; + index = val - 12 * 4; + } else { //rate 15 and up + shift = 0; + index = 12; + } +} + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +/* + Generate the different waveforms out of the sine/exponetial table using handlers +*/ +static inline Bits MakeVolume( Bitu wave, Bitu volume ) { + Bitu total = wave + volume; + Bitu index = total & 0xff; + Bitu sig = ExpTable[ index ]; + Bitu exp = total >> 8; +#if 0 + //Check if we overflow the 31 shift limit + if ( exp >= 32 ) { + LOG_MSG( "WTF %d %d", total, exp ); + } +#endif + return (sig >> exp); +}; + +static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { + Bit32u wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 511]; + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 255]; + wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + return (MakeVolume( 0, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { + //Negative is reversed here + Bits neg = (( i >> 9) & 1) - 1; + Bitu wave = (i << 3); + //When negative the volume also runs backwards + wave = ((wave ^ neg) - neg) & 4095; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} + +static const WaveHandler WaveHandlerTable[8] = { + WaveForm0, WaveForm1, WaveForm2, WaveForm3, + WaveForm4, WaveForm5, WaveForm6, WaveForm7 +}; + +#endif + +/* + Operator +*/ + +//We zero out when rate == 0 +inline void Operator::UpdateAttack( const Chip* chip ) { + Bit8u rate = reg60 >> 4; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + attackAdd = chip->attackRates[ val ]; + rateZero &= ~(1 << ATTACK); + } else { + attackAdd = 0; + rateZero |= (1 << ATTACK); + } +} +inline void Operator::UpdateDecay( const Chip* chip ) { + Bit8u rate = reg60 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + decayAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << DECAY); + } else { + decayAdd = 0; + rateZero |= (1 << DECAY); + } +} +inline void Operator::UpdateRelease( const Chip* chip ) { + Bit8u rate = reg80 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + releaseAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << RELEASE); + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero &= ~( 1 << SUSTAIN ); + } + } else { + rateZero |= (1 << RELEASE); + releaseAdd = 0; + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero |= ( 1 << SUSTAIN ); + } + } +} + +inline void Operator::UpdateAttenuation( ) { + Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); + Bit32u tl = reg40 & 0x3f; + Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; + //Make sure the attenuation goes to the right bits + totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max + totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; +} + +void Operator::UpdateFrequency( ) { + Bit32u freq = chanData & (( 1 << 10 ) - 1); + Bit32u block = (chanData >> 10) & 0xff; +#ifdef WAVE_PRECISION + block = 7 - block; + waveAdd = ( freq * freqMul ) >> block; +#else + waveAdd = ( freq << block ) * freqMul; +#endif + if ( reg20 & MASK_VIBRATO ) { + vibStrength = (Bit8u)(freq >> 7); + +#ifdef WAVE_PRECISION + vibrato = ( vibStrength * freqMul ) >> block; +#else + vibrato = ( vibStrength << block ) * freqMul; +#endif + } else { + vibStrength = 0; + vibrato = 0; + } +} + +void Operator::UpdateRates( const Chip* chip ) { + //Mame seems to reverse this where enabling ksr actually lowers + //the rate, but pdf manuals says otherwise? + Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); + if ( !( reg20 & MASK_KSR ) ) { + newKsr >>= 2; + } + if ( ksr == newKsr ) + return; + ksr = newKsr; + UpdateAttack( chip ); + UpdateDecay( chip ); + UpdateRelease( chip ); +} + +INLINE Bit32s Operator::RateForward( Bit32u add ) { + rateIndex += add; + Bit32s ret = rateIndex >> RATE_SH; + rateIndex = rateIndex & RATE_MASK; + return ret; +} + +template< Operator::State yes> +Bits Operator::TemplateVolume( ) { + Bit32s vol = volume; + Bit32s change; + switch ( yes ) { + case OFF: + return ENV_MAX; + case ATTACK: + change = RateForward( attackAdd ); + if ( !change ) + return vol; + vol += ( (~vol) * change ) >> 3; + if ( vol < ENV_MIN ) { + volume = ENV_MIN; + rateIndex = 0; + SetState( DECAY ); + return ENV_MIN; + } + break; + case DECAY: + vol += RateForward( decayAdd ); + if ( GCC_UNLIKELY(vol >= sustainLevel) ) { + //Check if we didn't overshoot max attenuation, then just go off + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + //Continue as sustain + rateIndex = 0; + SetState( SUSTAIN ); + } + break; + case SUSTAIN: + if ( reg20 & MASK_SUSTAIN ) { + return vol; + } + //In sustain phase, but not sustaining, do regular release + case RELEASE: + vol += RateForward( releaseAdd );; + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + break; + } + volume = vol; + return vol; +} + +static const VolumeHandler VolumeHandlerTable[5] = { + &Operator::TemplateVolume< Operator::OFF >, + &Operator::TemplateVolume< Operator::RELEASE >, + &Operator::TemplateVolume< Operator::SUSTAIN >, + &Operator::TemplateVolume< Operator::DECAY >, + &Operator::TemplateVolume< Operator::ATTACK > +}; + +INLINE Bitu Operator::ForwardVolume() { + return currentLevel + (this->*volHandler)(); +} + + +INLINE Bitu Operator::ForwardWave() { + waveIndex += waveCurrent; + return waveIndex >> WAVE_SH; +} + +void Operator::Write20( const Chip* chip, Bit8u val ) { + Bit8u change = (reg20 ^ val ); + if ( !change ) + return; + reg20 = val; + //Shift the tremolo bit over the entire register, saved a branch, YES! + tremoloMask = (Bit8s)(val) >> 7; + tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); + //Update specific features based on changes + if ( change & MASK_KSR ) { + UpdateRates( chip ); + } + //With sustain enable the volume doesn't change + if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { + rateZero |= ( 1 << SUSTAIN ); + } else { + rateZero &= ~( 1 << SUSTAIN ); + } + //Frequency multiplier or vibrato changed + if ( change & (0xf | MASK_VIBRATO) ) { + freqMul = chip->freqMul[ val & 0xf ]; + UpdateFrequency(); + } +} + +void Operator::Write40( const Chip* /*chip*/, Bit8u val ) { + if (!(reg40 ^ val )) + return; + reg40 = val; + UpdateAttenuation( ); +} + +void Operator::Write60( const Chip* chip, Bit8u val ) { + Bit8u change = reg60 ^ val; + reg60 = val; + if ( change & 0x0f ) { + UpdateDecay( chip ); + } + if ( change & 0xf0 ) { + UpdateAttack( chip ); + } +} + +void Operator::Write80( const Chip* chip, Bit8u val ) { + Bit8u change = (reg80 ^ val ); + if ( !change ) + return; + reg80 = val; + Bit8u sustain = val >> 4; + //Turn 0xf into 0x1f + sustain |= ( sustain + 1) & 0x10; + sustainLevel = sustain << ( ENV_BITS - 5 ); + if ( change & 0x0f ) { + UpdateRelease( chip ); + } +} + +void Operator::WriteE0( const Chip* chip, Bit8u val ) { + if ( !(regE0 ^ val) ) + return; + //in opl3 mode you can always selet 7 waveforms regardless of waveformselect + Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); + regE0 = val; +#if ( DBOPL_WAVE == WAVE_HANDLER ) + waveHandler = WaveHandlerTable[ waveForm ]; +#else + waveBase = WaveTable + WaveBaseTable[ waveForm ]; + waveStart = WaveStartTable[ waveForm ] << WAVE_SH; + waveMask = WaveMaskTable[ waveForm ]; +#endif +} + +INLINE void Operator::SetState( Bit8u s ) { + state = s; + volHandler = VolumeHandlerTable[ s ]; +} + +INLINE bool Operator::Silent() const { + if ( !ENV_SILENT( totalLevel + volume ) ) + return false; + if ( !(rateZero & ( 1 << state ) ) ) + return false; + return true; +} + +INLINE void Operator::Prepare( const Chip* chip ) { + currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); + waveCurrent = waveAdd; + if ( vibStrength >> chip->vibratoShift ) { + Bit32s add = vibrato >> chip->vibratoShift; + //Sign extend over the shift value + Bit32s neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + waveCurrent += add; + } +} + +void Operator::KeyOn( Bit8u mask ) { + if ( !keyOn ) { + //Restart the frequency generator +#if ( DBOPL_WAVE > WAVE_HANDLER ) + waveIndex = waveStart; +#else + waveIndex = 0; +#endif + rateIndex = 0; + SetState( ATTACK ); + } + keyOn |= mask; +} + +void Operator::KeyOff( Bit8u mask ) { + keyOn &= ~mask; + if ( !keyOn ) { + if ( state != OFF ) { + SetState( RELEASE ); + } + } +} + +INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { +#if ( DBOPL_WAVE == WAVE_HANDLER ) + return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); +#elif ( DBOPL_WAVE == WAVE_TABLEMUL ) + return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; +#elif ( DBOPL_WAVE == WAVE_TABLELOG ) + Bit32s wave = waveBase[ index & waveMask ]; + Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); + Bit32s sig = ExpTable[ total & 0xff ]; + Bit32u exp = total >> 8; + Bit32s neg = wave >> 16; + return ((sig ^ neg) - neg) >> exp; +#else +#error "No valid wave routine" +#endif +} + +Bits INLINE Operator::GetSample( Bits modulation ) { + Bitu vol = ForwardVolume(); + if ( ENV_SILENT( vol ) ) { + //Simply forward the wave + waveIndex += waveCurrent; + return 0; + } else { + Bitu index = ForwardWave(); + index += modulation; + return GetWave( index, vol ); + } +} + +Operator::Operator() { + chanData = 0; + freqMul = 0; + waveIndex = 0; + waveAdd = 0; + waveCurrent = 0; + keyOn = 0; + ksr = 0; + reg20 = 0; + reg40 = 0; + reg60 = 0; + reg80 = 0; + regE0 = 0; + SetState( OFF ); + rateZero = (1 << OFF); + sustainLevel = ENV_MAX; + currentLevel = ENV_MAX; + totalLevel = ENV_MAX; + volume = ENV_MAX; + releaseAdd = 0; +} + +/* + Channel +*/ + +Channel::Channel() { + old[0] = old[1] = 0; + chanData = 0; + regB0 = 0; + regC0 = 0; + maskLeft = -1; + maskRight = -1; + feedback = 31; + fourMask = 0; + synthHandler = &Channel::BlockTemplate< sm2FM >; +} + +void Channel::SetChanData( const Chip* chip, Bit32u data ) { + Bit32u change = chanData ^ data; + chanData = data; + Op( 0 )->chanData = data; + Op( 1 )->chanData = data; + //Since a frequency update triggered this, always update frequency + Op( 0 )->UpdateFrequency(); + Op( 1 )->UpdateFrequency(); + if ( change & ( 0xff << SHIFT_KSLBASE ) ) { + Op( 0 )->UpdateAttenuation(); + Op( 1 )->UpdateAttenuation(); + } + if ( change & ( 0xff << SHIFT_KEYCODE ) ) { + Op( 0 )->UpdateRates( chip ); + Op( 1 )->UpdateRates( chip ); + } +} + +void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) { + //Extrace the frequency bits + Bit32u data = chanData & 0xffff; + Bit32u kslBase = KslTable[ data >> 6 ]; + Bit32u keyCode = ( data & 0x1c00) >> 9; + if ( chip->reg08 & 0x40 ) { + keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ + } else { + keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ + } + //Add the keycode and ksl into the highest bits of chanData + data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); + ( this + 0 )->SetChanData( chip, data ); + if ( fourOp & 0x3f ) { + ( this + 1 )->SetChanData( chip, data ); + } +} + +void Channel::WriteA0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bit32u change = (chanData ^ val ) & 0xff; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } +} + +void Channel::WriteB0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } + //Check for a change in the keyon/off state + if ( !(( val ^ regB0) & 0x20)) + return; + regB0 = val; + if ( val & 0x20 ) { + Op(0)->KeyOn( 0x1 ); + Op(1)->KeyOn( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOn( 1 ); + ( this + 1 )->Op(1)->KeyOn( 1 ); + } + } else { + Op(0)->KeyOff( 0x1 ); + Op(1)->KeyOff( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOff( 1 ); + ( this + 1 )->Op(1)->KeyOff( 1 ); + } + } +} + +void Channel::WriteC0(const Chip* chip, Bit8u val) { + Bit8u change = val ^ regC0; + if (!change) + return; + regC0 = val; + feedback = (regC0 >> 1) & 7; + if (feedback) { + //We shift the input to the right 10 bit wave index value + feedback = 9 - feedback; + } + else { + feedback = 31; + } + UpdateSynth(chip); +} + +void Channel::UpdateSynth( const Chip* chip ) { + //Select the new synth mode + if ( chip->opl3Active ) { + //4-op mode enabled for this channel + if ( (chip->reg104 & fourMask) & 0x3f ) { + Channel* chan0, *chan1; + //Check if it's the 2nd channel in a 4-op + if ( !(fourMask & 0x80 ) ) { + chan0 = this; + chan1 = this + 1; + } else { + chan0 = this - 1; + chan1 = this; + } + + Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); + switch ( synth ) { + case 0: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; + break; + case 1: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; + break; + case 2: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; + break; + case 3: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; + break; + } + //Disable updating percussion channels + } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm3AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm3FM >; + } + maskLeft = (regC0 & 0x10 ) ? -1 : 0; + maskRight = (regC0 & 0x20 ) ? -1 : 0; + //opl2 active + } else { + //Disable updating percussion channels + if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm2AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm2FM >; + } + } +} + +template< bool opl3Mode> +INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { + Channel* chan = this; + + //BassDrum + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + + //When bassdrum is in AM mode first operator is ignoed + if ( chan->regC0 & 1 ) { + mod = 0; + } else { + mod = old[0]; + } + Bit32s sample = static_cast(Op(1)->GetSample( mod )); + + + //Precalculate stuff used by other outputs + Bit32u noiseBit = chip->ForwardNoise() & 0x1; + Bit32u c2 = static_cast(Op(2)->ForwardWave()); + Bit32u c5 = static_cast(Op(5)->ForwardWave()); + Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; + + //Hi-Hat + Bit32u hhVol = static_cast(Op(2)->ForwardVolume()); + if ( !ENV_SILENT( hhVol ) ) { + Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); + sample += static_cast(Op(2)->GetWave( hhIndex, hhVol )); + } + //Snare Drum + Bit32u sdVol = static_cast(Op(3)->ForwardVolume()); + if ( !ENV_SILENT( sdVol ) ) { + Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); + sample += static_cast(Op(3)->GetWave( sdIndex, sdVol )); + } + //Tom-tom + sample += static_cast(Op(4)->GetSample( 0 )); + + //Top-Cymbal + Bit32u tcVol = static_cast(Op(5)->ForwardVolume()); + if ( !ENV_SILENT( tcVol ) ) { + Bit32u tcIndex = (1 + phaseBit) << 8; + sample += static_cast(Op(5)->GetWave( tcIndex, tcVol )); + } + sample <<= 1; + if ( opl3Mode ) { + output[0] += sample; + output[1] += sample; + } else { + output[0] += sample; + } +} + +template +Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { + switch( mode ) { + case sm2AM: + case sm3AM: + if ( Op(0)->Silent() && Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm2FM: + case sm3FM: + if ( Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm3FMFM: + if ( Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMFM: + if ( Op(0)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3FMAM: + if ( Op(1)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMAM: + if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + default: + break; + } + //Init the operators with the the current vibrato and tremolo values + Op( 0 )->Prepare( chip ); + Op( 1 )->Prepare( chip ); + if ( mode > sm4Start ) { + Op( 2 )->Prepare( chip ); + Op( 3 )->Prepare( chip ); + } + if ( mode > sm6Start ) { + Op( 4 )->Prepare( chip ); + Op( 5 )->Prepare( chip ); + } + for ( Bitu i = 0; i < samples; i++ ) { + //Early out for percussion handlers + if ( mode == sm2Percussion ) { + GeneratePercussion( chip, output + i ); + continue; //Prevent some unitialized value bitching + } else if ( mode == sm3Percussion ) { + GeneratePercussion( chip, output + i * 2 ); + continue; //Prevent some unitialized value bitching + } + + //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + Bit32s sample; + Bit32s out0 = old[0]; + if ( mode == sm2AM || mode == sm3AM ) { + sample = static_cast(out0 + Op(1)->GetSample( 0 )); + } else if ( mode == sm2FM || mode == sm3FM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + } else if ( mode == sm3FMFM ) { + Bits next = Op(1)->GetSample( out0 ); + next = Op(2)->GetSample( next ); + sample = static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMFM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + next = Op(2)->GetSample( next ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3FMAM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + Bits next = Op(2)->GetSample( 0 ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMAM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + sample += static_cast(Op(2)->GetSample( next )); + sample += static_cast(Op(3)->GetSample( 0 )); + } + switch( mode ) { + case sm2AM: + case sm2FM: + output[ i ] += sample; + break; + case sm3AM: + case sm3FM: + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + output[ i * 2 + 0 ] += sample & maskLeft; + output[ i * 2 + 1 ] += sample & maskRight; + break; + default: + break; + } + } + switch( mode ) { + case sm2AM: + case sm2FM: + case sm3AM: + case sm3FM: + return ( this + 1 ); + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + return( this + 2 ); + case sm2Percussion: + case sm3Percussion: + return( this + 3 ); + } + return 0; +} + +/* + Chip +*/ + +Chip::Chip() { + reg08 = 0; + reg04 = 0; + regBD = 0; + reg104 = 0; + opl3Active = 0; +} + +INLINE Bit32u Chip::ForwardNoise() { + noiseCounter += noiseAdd; + Bitu count = noiseCounter >> LFO_SH; + noiseCounter &= WAVE_MASK; + for ( ; count > 0; --count ) { + //Noise calculation from mame + noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); + noiseValue >>= 1; + } + return noiseValue; +} + +INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) { + //Current vibrato value, runs 4x slower than tremolo + vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; + vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; + tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; + + //Check hom many samples there can be done before the value changes + Bit32u todo = LFO_MAX - lfoCounter; + Bit32u count = (todo + lfoAdd - 1) / lfoAdd; + if ( count > samples ) { + count = samples; + lfoCounter += count * lfoAdd; + } else { + lfoCounter += count * lfoAdd; + lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + vibratoIndex = ( vibratoIndex + 1 ) & 31; + //Clip tremolo to the the table size + if ( tremoloIndex + 1 < TREMOLO_TABLE ) + ++tremoloIndex; + else + tremoloIndex = 0; + } + return count; +} + + +void Chip::WriteBD( Bit8u val ) { + Bit8u change = regBD ^ val; + if ( !change ) + return; + regBD = val; + //TODO could do this with shift and xor? + vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + tremoloStrength = (val & 0x80) ? 0x00 : 0x02; + if ( val & 0x20 ) { + //Drum was just enabled, make sure channel 6 has the right synth + if ( change & 0x20 ) { + if ( opl3Active ) { + chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; + } else { + chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; + } + } + //Bass Drum + if ( val & 0x10 ) { + chan[6].op[0].KeyOn( 0x2 ); + chan[6].op[1].KeyOn( 0x2 ); + } else { + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + } + //Hi-Hat + if ( val & 0x1 ) { + chan[7].op[0].KeyOn( 0x2 ); + } else { + chan[7].op[0].KeyOff( 0x2 ); + } + //Snare + if ( val & 0x8 ) { + chan[7].op[1].KeyOn( 0x2 ); + } else { + chan[7].op[1].KeyOff( 0x2 ); + } + //Tom-Tom + if ( val & 0x4 ) { + chan[8].op[0].KeyOn( 0x2 ); + } else { + chan[8].op[0].KeyOff( 0x2 ); + } + //Top Cymbal + if ( val & 0x2 ) { + chan[8].op[1].KeyOn( 0x2 ); + } else { + chan[8].op[1].KeyOff( 0x2 ); + } + //Toggle keyoffs when we turn off the percussion + } else if ( change & 0x20 ) { + //Trigger a reset to setup the original synth handler + //This makes it call + chan[6].UpdateSynth( this ); + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + chan[7].op[0].KeyOff( 0x2 ); + chan[7].op[1].KeyOff( 0x2 ); + chan[8].op[0].KeyOff( 0x2 ); + chan[8].op[1].KeyOff( 0x2 ); + } +} + + +#define REGOP( _FUNC_ ) \ + index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ + if ( OpOffsetTable[ index ] ) { \ + Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ + regOp->_FUNC_( this, val ); \ + } + +#define REGCHAN( _FUNC_ ) \ + index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ + if ( ChanOffsetTable[ index ] ) { \ + Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ + regChan->_FUNC_( this, val ); \ + } + +//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers +void Chip::UpdateSynths() { + for (int i = 0; i < 18; i++) { + chan[i].UpdateSynth(this); + } +} + + +void Chip::WriteReg( Bit32u reg, Bit8u val ) { + Bitu index; + switch ( (reg & 0xf0) >> 4 ) { + case 0x00 >> 4: + if ( reg == 0x01 ) { + waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; + } else if ( reg == 0x104 ) { + //Only detect changes in lowest 6 bits + if ( !((reg104 ^ val) & 0x3f) ) + return; + //Always keep the highest bit enabled, for checking > 0x80 + reg104 = 0x80 | ( val & 0x3f ); + //Switch synths when changing the 4op combinations + UpdateSynths(); + } else if ( reg == 0x105 ) { + //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register + if ( !((opl3Active ^ val) & 1 ) ) + return; + opl3Active = ( val & 1 ) ? 0xff : 0; + //Just tupdate the synths now that opl3 most have been enabled + //This isn't how the real card handles it but need to switch to stereo generating handlers + UpdateSynths(); + } else if ( reg == 0x08 ) { + reg08 = val; + } + case 0x10 >> 4: + break; + case 0x20 >> 4: + case 0x30 >> 4: + REGOP( Write20 ); + break; + case 0x40 >> 4: + case 0x50 >> 4: + REGOP( Write40 ); + break; + case 0x60 >> 4: + case 0x70 >> 4: + REGOP( Write60 ); + break; + case 0x80 >> 4: + case 0x90 >> 4: + REGOP( Write80 ); + break; + case 0xa0 >> 4: + REGCHAN( WriteA0 ); + break; + case 0xb0 >> 4: + if ( reg == 0xbd ) { + WriteBD( val ); + } else { + REGCHAN( WriteB0 ); + } + break; + case 0xc0 >> 4: + REGCHAN( WriteC0 ); + case 0xd0 >> 4: + break; + case 0xe0 >> 4: + case 0xf0 >> 4: + REGOP( WriteE0 ); + break; + } +} + + +Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { + switch ( port & 3 ) { + case 0: + return val; + case 2: + if ( opl3Active || (val == 0x05) ) + return 0x100 | val; + else + return val; + } + return 0; +} + +void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock2_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples *2); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip::GenerateBlock3_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip::Setup( Bit32u rate ) { + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); + } +#else + Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif + + //-3 since the real envelope takes 8 steps to reach the single value we supply + for ( Bit8u i = 0; i < 76; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); + } +// Bit32s attackDiffs[62]; + //Generate the best matching attack rate + for ( Bit8u i = 0; i < 62; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + //Original amount of samples the attack would take + Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); + + Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); + Bit32s bestAdd = guessAdd; + Bit32u bestDiff = 1 << 30; + for( Bit32u passes = 0; passes < 16; passes ++ ) { + Bit32s volume = ENV_MAX; + Bit32s samples = 0; + Bit32u count = 0; + while ( volume > 0 && samples < original * 2 ) { + count += guessAdd; + Bit32s change = count >> RATE_SH; + count &= RATE_MASK; + if ( GCC_UNLIKELY(change) ) { // less than 1 % + volume += ( ~volume * change ) >> 3; + } + samples++; + + } + Bit32s diff = original - samples; + Bit32u lDiff = labs( diff ); + //Init last on first pass + if ( lDiff < bestDiff ) { + bestDiff = lDiff; + bestAdd = guessAdd; + //We hit an exactly matching sample count + if ( !bestDiff ) + break; + } + //Linear correction factor, not exactly perfect but seems to work + double correct = (original - diff) / (double)original; + guessAdd = (Bit32u)(guessAdd * correct); + //Below our target + if ( diff < 0 ) { + //Always add one here for rounding, an overshoot will get corrected by another pass decreasing + guessAdd++; + } + } + attackRates[i] = bestAdd; + //Keep track of the diffs for some debugging +// attackDiffs[i] = bestDiff; + } + for ( Bit8u i = 62; i < 76; i++ ) { + //This should provide instant volume maximizing + attackRates[i] = 8 << RATE_SH; + } + //Setup the channels with the correct four op flags + //Channels are accessed through a table so they appear linear here + chan[ 0].fourMask = 0x00 | ( 1 << 0 ); + chan[ 1].fourMask = 0x80 | ( 1 << 0 ); + chan[ 2].fourMask = 0x00 | ( 1 << 1 ); + chan[ 3].fourMask = 0x80 | ( 1 << 1 ); + chan[ 4].fourMask = 0x00 | ( 1 << 2 ); + chan[ 5].fourMask = 0x80 | ( 1 << 2 ); + + chan[ 9].fourMask = 0x00 | ( 1 << 3 ); + chan[10].fourMask = 0x80 | ( 1 << 3 ); + chan[11].fourMask = 0x00 | ( 1 << 4 ); + chan[12].fourMask = 0x80 | ( 1 << 4 ); + chan[13].fourMask = 0x00 | ( 1 << 5 ); + chan[14].fourMask = 0x80 | ( 1 << 5 ); + + //mark the percussion channels + chan[ 6].fourMask = 0x40; + chan[ 7].fourMask = 0x40; + chan[ 8].fourMask = 0x40; + + //Clear Everything in opl3 mode + WriteReg( 0x105, 0x1 ); + for ( int i = 0; i < 512; i++ ) { + if ( i == 0x105 ) + continue; + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } + WriteReg( 0x105, 0x0 ); + //Clear everything in opl2 mode + for ( int i = 0; i < 255; i++ ) { + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } +} + +static bool doneTables = false; +void InitTables( void ) { + if ( doneTables ) + return; + doneTables = true; +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + //Exponential volume table, same as the real adlib + for ( int i = 0; i < 256; i++ ) { + //Save them in reverse + ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); + ExpTable[i] += 1024; //or remove the -1 oh well :) + //Preshift to the left once so the final volume can shift to the right + ExpTable[i] *= 2; + } +#endif +#if ( DBOPL_WAVE == WAVE_HANDLER ) + //Add 0.5 for the trunc rounding of the integer cast + //Do a PI sinetable instead of the original 0.5 PI + for ( int i = 0; i < 512; i++ ) { + SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + //Multiplication based tables + for ( int i = 0; i < 384; i++ ) { + int s = i * 8; + //TODO maybe keep some of the precision errors of the original table? + double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); + MulTable[i] = (Bit16u)(val); + } + + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); + WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = i * 8; + WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; + } +#endif + + // | |//\\|____|WAV7|//__|/\ |____|/\/\| + // |\\//| | |WAV7| | \/| | | + // |06 |0126|27 |7 |3 |4 |4 5 |5 | + +#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) + for ( int i = 0; i < 256; i++ ) { + //Fill silence gaps + WaveTable[ 0x400 + i ] = WaveTable[0]; + WaveTable[ 0x500 + i ] = WaveTable[0]; + WaveTable[ 0x900 + i ] = WaveTable[0]; + WaveTable[ 0xc00 + i ] = WaveTable[0]; + WaveTable[ 0xd00 + i ] = WaveTable[0]; + //Replicate sines in other pieces + WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; + //double speed sines + WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; + WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; + } +#endif + + //Create the ksl table + for ( int oct = 0; oct < 8; oct++ ) { + int base = oct * 8; + for ( int i = 0; i < 16; i++ ) { + int val = base - KslCreateTable[i]; + if ( val < 0 ) + val = 0; + //*4 for the final range to match attenuation range + KslTable[ oct * 16 + i ] = val * 4; + } + } + //Create the Tremolo table, just increase and decrease a triangle wave + for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) { + Bit8u val = i << ENV_EXTRA; + TremoloTable[i] = val; + TremoloTable[TREMOLO_TABLE - 1 - i] = val; + } + //Create a table with offsets of the channels from the start of the chip + DBOPL::Chip* chip = 0; + for ( Bitu i = 0; i < 32; i++ ) { + Bitu index = i & 0xf; + if ( index >= 9 ) { + ChanOffsetTable[i] = 0; + continue; + } + //Make sure the four op channels follow eachother + if ( index < 6 ) { + index = (index % 3) * 2 + ( index / 3 ); + } + //Add back the bits for highest ones + if ( i >= 16 ) + index += 9; + Bitu blah = reinterpret_cast( &(chip->chan[ index ]) ); + ChanOffsetTable[i] = static_cast(blah); + } + //Same for operators + for ( Bitu i = 0; i < 64; i++ ) { + if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { + OpOffsetTable[i] = 0; + continue; + } + Bitu chNum = (i / 8) * 3 + (i % 8) % 3; + //Make sure we use 16 and up for the 2nd range to match the chanoffset gap + if ( chNum >= 12 ) + chNum += 16 - 12; + Bitu opNum = ( i % 8 ) / 3; + DBOPL::Channel* chan = 0; + Bitu blah = reinterpret_cast( &(chan->op[opNum]) ); + OpOffsetTable[i] = static_cast(ChanOffsetTable[ chNum ] + blah); + } +#if 0 + //Stupid checks if table's are correct + for ( Bitu i = 0; i < 18; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); + for ( Bitu c = 0; c < 32; c++ ) { + if ( ChanOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } + for ( Bitu i = 0; i < 36; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); + for ( Bitu c = 0; c < 64; c++ ) { + if ( OpOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } +#endif +} + +Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) { + return chip.WriteAddr( port, val ); + +} +void Handler::WriteReg( Bit32u addr, Bit8u val ) { + chip.WriteReg( addr, val ); +} + +#define DB_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define DB_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define DBOPL_CLAMP(V, MIN, MAX) DB_MAX(DB_MIN(V, (MAX)), (MIN)) + +void Handler::GenerateArr(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out); + else + chip.GenerateBlock3(*samples, out); +} + +void Handler::GenerateArr(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] = static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::GenerateArrMix(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2_Mix(*samples, out); + else + chip.GenerateBlock3_Mix(*samples, out); +} + +void Handler::GenerateArrMix(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] += static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::Init( Bitu rate ) { + InitTables(); + chip.Setup( static_cast(rate) ); +} + + +} //Namespace DBOPL diff --git a/src/sound/adlmidi/chips/dosbox/dbopl.h b/src/sound/adlmidi/chips/dosbox/dbopl.h new file mode 100644 index 000000000..73c0aa9a1 --- /dev/null +++ b/src/sound/adlmidi/chips/dosbox/dbopl.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#if defined(__GNUC__) && defined(__i386__) +#define DB_FASTCALL __attribute__((fastcall)) +#elif defined(_MSC_VER) +#define DB_FASTCALL __fastcall +#else +#define DB_FASTCALL +#endif + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume +#define WAVE_HANDLER 10 +//Use a logarithmic wavetable with an exponential table for volume +#define WAVE_TABLELOG 11 +//Use a linear wavetable with a multiply table for volume +#define WAVE_TABLEMUL 12 + +//Select the type of wave generator routine +#define DBOPL_WAVE WAVE_TABLEMUL + +namespace DBOPL { + +struct Chip; +struct Operator; +struct Channel; + +#if (DBOPL_WAVE == WAVE_HANDLER) +typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); +#endif + +typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); +typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); + +//Different synth modes that can generate blocks of data +typedef enum { + sm2AM, + sm2FM, + sm3AM, + sm3FM, + sm4Start, + sm3FMFM, + sm3AMFM, + sm3FMAM, + sm3AMAM, + sm6Start, + sm2Percussion, + sm3Percussion, +} SynthMode; + +//Shifts for the values contained in chandata variable +enum { + SHIFT_KSLBASE = 16, + SHIFT_KEYCODE = 24, +}; + +struct Operator { +public: + //Masks for operator 20 values + enum { + MASK_KSR = 0x10, + MASK_SUSTAIN = 0x20, + MASK_VIBRATO = 0x40, + MASK_TREMOLO = 0x80, + }; + + typedef enum { + OFF, + RELEASE, + SUSTAIN, + DECAY, + ATTACK, + } State; + + VolumeHandler volHandler; + +#if (DBOPL_WAVE == WAVE_HANDLER) + WaveHandler waveHandler; //Routine that generate a wave +#else + Bit16s* waveBase; + Bit32u waveMask; + Bit32u waveStart; +#endif + Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index + Bit32u waveAdd; //The base frequency without vibrato + Bit32u waveCurrent; //waveAdd + vibratao + + Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this + Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? + Bit32u vibrato; //Scaled up vibrato strength + Bit32s sustainLevel; //When stopping at sustain level stop here + Bit32s totalLevel; //totalLevel is added to every generated volume + Bit32u currentLevel; //totalLevel + tremolo + Bit32s volume; //The currently active volume + + Bit32u attackAdd; //Timers for the different states of the envelope + Bit32u decayAdd; + Bit32u releaseAdd; + Bit32u rateIndex; //Current position of the evenlope + + Bit8u rateZero; //Bits for the different states of the envelope having no changes + Bit8u keyOn; //Bitmask of different values that can generate keyon + //Registers, also used to check for changes + Bit8u reg20, reg40, reg60, reg80, regE0; + //Active part of the envelope we're in + Bit8u state; + //0xff when tremolo is enabled + Bit8u tremoloMask; + //Strength of the vibrato + Bit8u vibStrength; + //Keep track of the calculated KSR so we can check for changes + Bit8u ksr; +private: + void SetState( Bit8u s ); + void UpdateAttack( const Chip* chip ); + void UpdateRelease( const Chip* chip ); + void UpdateDecay( const Chip* chip ); +public: + void UpdateAttenuation(); + void UpdateRates( const Chip* chip ); + void UpdateFrequency( ); + + void Write20( const Chip* chip, Bit8u val ); + void Write40( const Chip* chip, Bit8u val ); + void Write60( const Chip* chip, Bit8u val ); + void Write80( const Chip* chip, Bit8u val ); + void WriteE0( const Chip* chip, Bit8u val ); + + bool Silent() const; + void Prepare( const Chip* chip ); + + void KeyOn( Bit8u mask); + void KeyOff( Bit8u mask); + + template< State state> + Bits TemplateVolume( ); + + Bit32s RateForward( Bit32u add ); + Bitu ForwardWave(); + Bitu ForwardVolume(); + + Bits GetSample( Bits modulation ); + Bits GetWave( Bitu index, Bitu vol ); +public: + Operator(); +}; + +struct Channel { + Operator op[2]; + inline Operator* Op( Bitu index ) { + return &( ( this + (index >> 1) )->op[ index & 1 ]); + } + SynthHandler synthHandler; + Bit32u chanData; //Frequency/octave and derived values + Bit32s old[2]; //Old data for feedback + + Bit8u feedback; //Feedback shift + Bit8u regB0; //Register values to check for changes + Bit8u regC0; + //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel + Bit8u fourMask; + Bit8s maskLeft; //Sign extended values for both channel's panning + Bit8s maskRight; + + //Forward the channel data to the operators of the channel + void SetChanData( const Chip* chip, Bit32u data ); + //Change in the chandata, check for new values and if we have to forward to operators + void UpdateFrequency( const Chip* chip, Bit8u fourOp ); + void UpdateSynth(const Chip* chip); + void WriteA0( const Chip* chip, Bit8u val ); + void WriteB0( const Chip* chip, Bit8u val ); + void WriteC0( const Chip* chip, Bit8u val ); + + //call this for the first channel + template< bool opl3Mode > + void GeneratePercussion( Chip* chip, Bit32s* output ); + + //Generate blocks of data in specific modes + template + Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); + Channel(); +}; + +struct Chip { + //This is used as the base counter for vibrato and tremolo + Bit32u lfoCounter; + Bit32u lfoAdd; + + + Bit32u noiseCounter; + Bit32u noiseAdd; + Bit32u noiseValue; + + //Frequency scales for the different multiplications + Bit32u freqMul[16]; + //Rates for decay and release for rate of this chip + Bit32u linearRates[76]; + //Best match attack rates for the rate of this chip + Bit32u attackRates[76]; + + //18 channels with 2 operators each + Channel chan[18]; + + Bit8u reg104; + Bit8u reg08; + Bit8u reg04; + Bit8u regBD; + Bit8u vibratoIndex; + Bit8u tremoloIndex; + Bit8s vibratoSign; + Bit8u vibratoShift; + Bit8u tremoloValue; + Bit8u vibratoStrength; + Bit8u tremoloStrength; + //Mask for allowed wave forms + Bit8u waveFormMask; + //0 or -1 when enabled + Bit8s opl3Active; + + //Return the maximum amount of samples before and LFO change + Bit32u ForwardLFO( Bit32u samples ); + Bit32u ForwardNoise(); + + void WriteBD( Bit8u val ); + void WriteReg(Bit32u reg, Bit8u val ); + + Bit32u WriteAddr( Bit32u port, Bit8u val ); + + void GenerateBlock2( Bitu samples, Bit32s* output ); + void GenerateBlock2_Mix( Bitu samples, Bit32s* output ); + void GenerateBlock3( Bitu samples, Bit32s* output ); + void GenerateBlock3_Mix( Bitu samples, Bit32s* output ); + + //Update the synth handlers in all channels + void UpdateSynths(); + void Generate( Bit32u samples ); + void Setup( Bit32u r ); + + Chip(); +}; + +struct Handler { + DBOPL::Chip chip; + Bit32u WriteAddr( Bit32u port, Bit8u val ); + void WriteReg( Bit32u addr, Bit8u val ); + void GenerateArr(Bit32s *out, Bitu *samples); + void GenerateArr(Bit16s *out, Bitu *samples); + void GenerateArrMix(Bit32s *out, Bitu *samples); + void GenerateArrMix(Bit16s *out, Bitu *samples); + void Init( Bitu rate ); +}; + + +} //Namespace diff --git a/src/sound/adlmidi/chips/dosbox_opl3.cpp b/src/sound/adlmidi/chips/dosbox_opl3.cpp new file mode 100644 index 000000000..30fa38ea5 --- /dev/null +++ b/src/sound/adlmidi/chips/dosbox_opl3.cpp @@ -0,0 +1,54 @@ +#include "dosbox_opl3.h" +#include "dosbox/dbopl.h" +#include +#include +#include + +DosBoxOPL3::DosBoxOPL3() : + OPLChipBaseBufferedT(), + m_chip(new DBOPL::Handler) +{ + reset(); +} + +DosBoxOPL3::~DosBoxOPL3() +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void DosBoxOPL3::setRate(uint32_t rate) +{ + OPLChipBaseBufferedT::setRate(rate); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::reset() +{ + OPLChipBaseBufferedT::reset(); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->WriteReg(static_cast(addr), data); +} + +void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + Bitu frames_i = frames; + chip_r->GenerateArr(output, &frames_i); +} + +const char *DosBoxOPL3::emulatorName() +{ + return "DosBox 0.74-r4111 OPL3"; +} diff --git a/src/sound/adlmidi/chips/dosbox_opl3.h b/src/sound/adlmidi/chips/dosbox_opl3.h new file mode 100644 index 000000000..192802666 --- /dev/null +++ b/src/sound/adlmidi/chips/dosbox_opl3.h @@ -0,0 +1,23 @@ +#ifndef DOSBOX_OPL3_H +#define DOSBOX_OPL3_H + +#include "opl_chip_base.h" + +class DosBoxOPL3 final : public OPLChipBaseBufferedT +{ + void *m_chip; +public: + DosBoxOPL3(); + ~DosBoxOPL3() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif // DOSBOX_OPL3_H diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3.c b/src/sound/adlmidi/chips/nuked/nukedopl3.c new file mode 100644 index 000000000..87d321257 --- /dev/null +++ b/src/sound/adlmidi/chips/nuked/nukedopl3.c @@ -0,0 +1,1391 @@ +/* + * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#include +#include +#include +#include "nukedopl3.h" + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +/* + * logsin table + */ + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* + * exp table + */ + +static const Bit16u exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +/* + * freq mult table multiplied by 2 + * + * 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 + */ + +static const Bit8u mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + * ksl table + */ + +static const Bit8u kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const Bit8u kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + * envelope generator constants + */ + +static const Bit8u eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +/* + * address decoding + */ + +static const Bit8s ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const Bit8u ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +/* + * Envelope generator + */ + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +static Bit16s OPL3_EnvelopeCalcExp(Bit32u level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope) +{ + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + Bit8u nonzero; + Bit8u rate; + Bit8u rate_hi; + Bit8u rate_lo; + Bit8u reg_rate = 0; + Bit8u ks; + Bit8u eg_shift, shift; + Bit16u eg_rout; + Bit16s eg_inc; + Bit8u eg_off; + Bit8u reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ((~slot->eg_rout) << shift) >> 4; + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) +{ + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +{ + slot->key &= ~type; +} + +/* + * Phase Generator + */ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + Bit16u f_num; + Bit32u basefreq; + Bit8u rm_xor, n_bit; + Bit32u noise; + Bit16u phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + Bit8s range; + Bit8u vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (Bit16u)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + /* Rhythm mode */ + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) /* hh */ + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) /* tc */ + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: /* hh */ + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: /* sd */ + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: /* tc */ + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } + } + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); +} + +/* + * Slot + */ + +static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; +} + +static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; +} + +static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + * Channel + */ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit8u chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + /* hh */ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); + } + /* tc */ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); + } + /* tom */ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); + } + /* sd */ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); + } + /* bd */ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->chip->zeromod; + return; + } + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = (Bit16u)~0; + } +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +{ + Bit8u bit; + Bit8u chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static Bit16s OPL3_ClipSample(Bit32s sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (Bit16s)sample; +} + +void OPL3_Generate(opl3_chip *chip, Bit16s *buf) +{ + Bit8u ii; + Bit8u jj; + Bit16s accm; + Bit8u shift = 0; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < 15; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[0] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + + for (ii = 15; ii < 18; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + + for (ii = 18; ii < 33; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[1] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + + for (ii = 33; ii < 36; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == (uint64_t)0xfffffffffU) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + { + break; + } + chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) +{ + Bit8u slotnum; + Bit8u channum; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + chip->slot[slotnum].chip = chip; + chip->slot[slotnum].mod = &chip->zeromod; + chip->slot[slotnum].eg_rout = 0x1ff; + chip->slot[slotnum].eg_out = 0x1ff; + chip->slot[slotnum].eg_gen = envelope_gen_num_release; + chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod; + chip->slot[slotnum].slot_num = slotnum; + } + for (channum = 0; channum < 18; channum++) + { + chip->channel[channum].slotz[0] = &chip->slot[ch_slot[channum]]; + chip->channel[channum].slotz[1] = &chip->slot[ch_slot[channum] + 3]; + chip->slot[ch_slot[channum]].channel = &chip->channel[channum]; + chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + if ((channum % 9) < 3) + { + chip->channel[channum].pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + chip->channel[channum].pair = &chip->channel[channum - 3]; + } + chip->channel[channum].chip = chip; + chip->channel[channum].out[0] = &chip->zeromod; + chip->channel[channum].out[1] = &chip->zeromod; + chip->channel[channum].out[2] = &chip->zeromod; + chip->channel[channum].out[3] = &chip->zeromod; + chip->channel[channum].chtype = ch_2op; + chip->channel[channum].cha = 0xffff; + chip->channel[channum].chb = 0xffff; + chip->channel[channum].ch_num = channum; + OPL3_ChannelSetupAlg(&chip->channel[channum]); + } + chip->noise = 1; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit64u time1, time2; + + if (chip->writebuf[chip->writebuf_last].reg & 0x200) + { + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + } + + chip->writebuf[chip->writebuf_last].reg = reg | 0x200; + chip->writebuf[chip->writebuf_last].data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} + +void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + Bit16s sample[2]; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sample); + sndptr[0] += sample[0]; + sndptr[1] += sample[1]; + sndptr += 2; + } +} diff --git a/src/sound/adlmidi/nukedopl3.h b/src/sound/adlmidi/chips/nuked/nukedopl3.h similarity index 75% rename from src/sound/adlmidi/nukedopl3.h rename to src/sound/adlmidi/chips/nuked/nukedopl3.h index 254f27584..d57cf5fb5 100644 --- a/src/sound/adlmidi/nukedopl3.h +++ b/src/sound/adlmidi/chips/nuked/nukedopl3.h @@ -1,19 +1,16 @@ /* - * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Nuked OPL3 emulator. * Thanks: @@ -23,21 +20,21 @@ * Tremolo and phase generator calculation information. * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. * - * version: 1.7.4 + * version: 1.8 */ #ifndef OPL_OPL3_H #define OPL_OPL3_H #include -#include #ifdef __cplusplus extern "C" { #endif - #define OPL_WRITEBUF_SIZE 1024 #define OPL_WRITEBUF_DELAY 2 @@ -82,16 +79,14 @@ struct _opl3_slot { Bit8u reg_rr; Bit8u reg_wf; Bit8u key; + Bit32u pg_reset; Bit32u pg_phase; - Bit32u timer; - - Bit16u maskzero; - Bit8u signpos; - Bit8u phaseshift; + Bit16u pg_phase_out; + Bit8u slot_num; }; struct _opl3_channel { - opl3_slot *slots[2]; + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ opl3_channel *pair; opl3_chip *chip; Bit16s *out[4]; @@ -103,6 +98,7 @@ struct _opl3_channel { Bit8u alg; Bit8u ksv; Bit16u cha, chb; + Bit8u ch_num; }; typedef struct _opl3_writebuf { @@ -113,8 +109,12 @@ typedef struct _opl3_writebuf { struct _opl3_chip { opl3_channel channel[18]; - opl3_slot chipslot[36]; + opl3_slot slot[36]; Bit16u timer; + Bit64u eg_timer; + Bit8u eg_timerrem; + Bit8u eg_state; + Bit8u eg_add; Bit8u newm; Bit8u nts; Bit8u rhy; @@ -126,6 +126,12 @@ struct _opl3_chip { Bit32u noise; Bit16s zeromod; Bit32s mixbuff[2]; + Bit8u rm_hh_bit2; + Bit8u rm_hh_bit3; + Bit8u rm_hh_bit7; + Bit8u rm_hh_bit8; + Bit8u rm_tc_bit3; + Bit8u rm_tc_bit5; /* OPL3L */ Bit32s rateratio; Bit32s samplecnt; diff --git a/src/sound/adlmidi/nukedopl3.c b/src/sound/adlmidi/chips/nuked/nukedopl3_174.c similarity index 83% rename from src/sound/adlmidi/nukedopl3.c rename to src/sound/adlmidi/chips/nuked/nukedopl3_174.c index 6ae1eb76e..99eab16dc 100644 --- a/src/sound/adlmidi/nukedopl3.c +++ b/src/sound/adlmidi/chips/nuked/nukedopl3_174.c @@ -30,7 +30,7 @@ #include #include #include -#include "nukedopl3.h" +#include "nukedopl3_174.h" #define RSM_FRAC 10 @@ -638,18 +638,18 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) channel6 = &chip->channel[6]; channel7 = &chip->channel[7]; channel8 = &chip->channel[8]; - channel6->out[0] = &channel6->slots[1]->out; - channel6->out[1] = &channel6->slots[1]->out; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; channel6->out[2] = &chip->zeromod; channel6->out[3] = &chip->zeromod; - channel7->out[0] = &channel7->slots[0]->out; - channel7->out[1] = &channel7->slots[0]->out; - channel7->out[2] = &channel7->slots[1]->out; - channel7->out[3] = &channel7->slots[1]->out; - channel8->out[0] = &channel8->slots[0]->out; - channel8->out[1] = &channel8->slots[0]->out; - channel8->out[2] = &channel8->slots[1]->out; - channel8->out[3] = &channel8->slots[1]->out; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; for (chnum = 6; chnum < 9; chnum++) { chip->channel[chnum].chtype = ch_drum; @@ -658,49 +658,49 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) /*hh*/ if (chip->rhy & 0x01) { - OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); } /*tc*/ if (chip->rhy & 0x02) { - OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); } /*tom*/ if (chip->rhy & 0x04) { - OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); } /*sd*/ if (chip->rhy & 0x08) { - OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); } /*bd*/ if (chip->rhy & 0x10) { - OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum); - OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum); - OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); } } else @@ -709,8 +709,8 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) { chip->channel[chnum].chtype = ch_2op; OPL3_ChannelSetupAlg(&chip->channel[chnum]); - OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum); - OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); } } } @@ -724,18 +724,18 @@ static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) channel->f_num = (channel->f_num & 0x300) | data; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); - OPL3_EnvelopeUpdateKSL(channel->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->slots[1]); - OPL3_EnvelopeUpdateRate(channel->slots[0]); - OPL3_EnvelopeUpdateRate(channel->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->ksv = channel->ksv; - OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[0]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); } } @@ -749,19 +749,19 @@ static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) channel->block = (data >> 2) & 0x07; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); - OPL3_EnvelopeUpdateKSL(channel->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->slots[1]); - OPL3_EnvelopeUpdateRate(channel->slots[0]); - OPL3_EnvelopeUpdateRate(channel->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->block = channel->block; channel->pair->ksv = channel->ksv; - OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[0]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); } } @@ -772,12 +772,12 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) switch (channel->alg & 0x01) { case 0x00: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->slots[0]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; break; case 0x01: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; break; } return; @@ -795,43 +795,43 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) switch (channel->alg & 0x03) { case 0x00: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; - channel->slots[0]->mod = &channel->chip->zeromod; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->pair->slots[1]->out; - channel->out[1] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x02: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->chip->zeromod; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->pair->slots[0]->out; - channel->out[1] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x03: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->chip->zeromod; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->chip->zeromod; - channel->out[0] = &channel->pair->slots[0]->out; - channel->out[1] = &channel->slots[0]->out; - channel->out[2] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; channel->out[3] = &channel->chip->zeromod; break; } @@ -841,18 +841,18 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) switch (channel->alg & 0x01) { case 0x00: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->slots[1]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->chip->zeromod; - channel->out[0] = &channel->slots[0]->out; - channel->out[1] = &channel->slots[1]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; @@ -905,21 +905,21 @@ static void OPL3_ChannelKeyOn(opl3_channel *channel) { if (channel->chtype == ch_4op) { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); - OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); } } else { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); } } @@ -929,21 +929,21 @@ static void OPL3_ChannelKeyOff(opl3_channel *channel) { if (channel->chtype == ch_4op) { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); - OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); } } else { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); } } @@ -997,9 +997,9 @@ static void OPL3_GenerateRhythm1(opl3_chip *chip) channel6 = &chip->channel[6]; channel7 = &chip->channel[7]; channel8 = &chip->channel[8]; - OPL3_SlotGenerate(channel6->slots[0]); - phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; - phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + OPL3_SlotGenerate(channel6->slotz[0]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; phase = 0x00; /*hh tc phase bit*/ phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) @@ -1007,9 +1007,9 @@ static void OPL3_GenerateRhythm1(opl3_chip *chip) /*hh*/ phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1)); - OPL3_SlotGeneratePhase(channel7->slots[0], phase); + OPL3_SlotGeneratePhase(channel7->slotz[0], phase); /*tt*/ - OPL3_SlotGenerateZM(channel8->slots[0]); + OPL3_SlotGenerateZM(channel8->slotz[0]); } static void OPL3_GenerateRhythm2(opl3_chip *chip) @@ -1025,22 +1025,22 @@ static void OPL3_GenerateRhythm2(opl3_chip *chip) channel6 = &chip->channel[6]; channel7 = &chip->channel[7]; channel8 = &chip->channel[8]; - OPL3_SlotGenerate(channel6->slots[1]); - phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; - phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + OPL3_SlotGenerate(channel6->slotz[1]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; phase = 0x00; /*hh tc phase bit*/ phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; /*sd*/ phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); - OPL3_SlotGeneratePhase(channel7->slots[1], phase); + OPL3_SlotGeneratePhase(channel7->slotz[1], phase); /*tc*/ phase = 0x100 | (phasebit << 9); - OPL3_SlotGeneratePhase(channel8->slots[1], phase); + OPL3_SlotGeneratePhase(channel8->slotz[1], phase); } -void OPL3_Generate(opl3_chip *chip, Bit16s *buf) +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf) { Bit8u ii; Bit8u jj; @@ -1161,20 +1161,20 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf) break; } chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; - OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } -void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf) { while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; - OPL3_Generate(chip, chip->samples); + OPL3v17_Generate(chip, chip->samples); chip->samplecnt -= chip->rateratio; } buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) @@ -1184,7 +1184,7 @@ void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) chip->samplecnt += 1 << RSM_FRAC; } -void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate) { Bit8u slotnum; Bit8u channum; @@ -1202,8 +1202,8 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) } for (channum = 0; channum < 18; channum++) { - chip->channel[channum].slots[0] = &chip->chipslot[ch_slot[channum]]; - chip->channel[channum].slots[1] = &chip->chipslot[ch_slot[channum] + 3]; + chip->channel[channum].slotz[0] = &chip->chipslot[ch_slot[channum]]; + chip->channel[channum].slotz[1] = &chip->chipslot[ch_slot[channum] + 3]; chip->chipslot[ch_slot[channum]].channel = &chip->channel[channum]; chip->chipslot[ch_slot[channum] + 3].channel = &chip->channel[channum]; if ((channum % 9) < 3) @@ -1230,7 +1230,7 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) chip->vibshift = 1; } -void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit8u high = (reg >> 8) & 0x01; Bit8u regm = reg & 0xff; @@ -1329,13 +1329,13 @@ void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) } } -void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit64u time1, time2; if (chip->writebuf[chip->writebuf_last].reg & 0x200) { - OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, chip->writebuf[chip->writebuf_last].data); chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; @@ -1357,13 +1357,13 @@ void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; } -void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) { Bit32u i; for(i = 0; i < numsamples; i++) { - OPL3_GenerateResampled(chip, sndptr); + OPL3v17_GenerateResampled(chip, sndptr); sndptr += 2; } } @@ -1372,7 +1372,7 @@ void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) #define OPL3_MAX(A, B) (((A) < (B)) ? (B) : (A)) #define OPL3_CLAMP(V, MIN, MAX) OPL3_MAX(OPL3_MIN(V, MAX), MIN) -void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) { Bit32u i; Bit16s sample[2]; @@ -1380,7 +1380,7 @@ void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) for(i = 0; i < numsamples; i++) { - OPL3_GenerateResampled(chip, sample); + OPL3v17_GenerateResampled(chip, sample); mix[0] = sndptr[0] + sample[0]; mix[1] = sndptr[1] + sample[1]; sndptr[0] = OPL3_CLAMP(mix[0], INT16_MIN, INT16_MAX); diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3_174.h b/src/sound/adlmidi/chips/nuked/nukedopl3_174.h new file mode 100644 index 000000000..240802f4f --- /dev/null +++ b/src/sound/adlmidi/chips/nuked/nukedopl3_174.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.7.4 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_phase; + Bit32u timer; + + Bit16u maskzero; + Bit8u signpos; + Bit8u phaseshift; +}; + +struct _opl3_channel { + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ + opl3_channel *pair; + opl3_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; +}; + +typedef struct _opl3_writebuf { + Bit64u time; + Bit16u reg; + Bit8u data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot chipslot[36]; + Bit16u timer; + Bit8u newm; + Bit8u nts; + Bit8u rhy; + Bit8u vibpos; + Bit8u vibshift; + Bit8u tremolo; + Bit8u tremolopos; + Bit8u tremoloshift; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + /* OPL3L */ + Bit32s rateratio; + Bit32s samplecnt; + Bit16s oldsamples[2]; + Bit16s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf); +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf); +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sound/adlmidi/chips/nuked_opl3.cpp b/src/sound/adlmidi/chips/nuked_opl3.cpp new file mode 100644 index 000000000..48e5c1736 --- /dev/null +++ b/src/sound/adlmidi/chips/nuked_opl3.cpp @@ -0,0 +1,49 @@ +#include "nuked_opl3.h" +#include "nuked/nukedopl3.h" +#include + +NukedOPL3::NukedOPL3() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + setRate(m_rate); +} + +NukedOPL3::~NukedOPL3() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, rate); +} + +void NukedOPL3::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, m_rate); +} + +void NukedOPL3::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_WriteRegBuffered(chip_r, addr, data); +} + +void NukedOPL3::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_Generate(chip_r, frame); +} + +const char *NukedOPL3::emulatorName() +{ + return "Nuked OPL3 (v 1.8)"; +} diff --git a/src/sound/adlmidi/chips/nuked_opl3.h b/src/sound/adlmidi/chips/nuked_opl3.h new file mode 100644 index 000000000..1b34e9ab5 --- /dev/null +++ b/src/sound/adlmidi/chips/nuked_opl3.h @@ -0,0 +1,23 @@ +#ifndef NUKED_OPL3_H +#define NUKED_OPL3_H + +#include "opl_chip_base.h" + +class NukedOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3(); + ~NukedOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // NUKED_OPL3_H diff --git a/src/sound/adlmidi/chips/nuked_opl3_v174.cpp b/src/sound/adlmidi/chips/nuked_opl3_v174.cpp new file mode 100644 index 000000000..e24b2e706 --- /dev/null +++ b/src/sound/adlmidi/chips/nuked_opl3_v174.cpp @@ -0,0 +1,49 @@ +#include "nuked_opl3_v174.h" +#include "nuked/nukedopl3_174.h" +#include + +NukedOPL3v174::NukedOPL3v174() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + setRate(m_rate); +} + +NukedOPL3v174::~NukedOPL3v174() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3v174::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, rate); +} + +void NukedOPL3v174::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, m_rate); +} + +void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_WriteReg(chip_r, addr, data); +} + +void NukedOPL3v174::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_Generate(chip_r, frame); +} + +const char *NukedOPL3v174::emulatorName() +{ + return "Nuked OPL3 (v 1.7.4)"; +} diff --git a/src/sound/adlmidi/chips/nuked_opl3_v174.h b/src/sound/adlmidi/chips/nuked_opl3_v174.h new file mode 100644 index 000000000..f14221fe0 --- /dev/null +++ b/src/sound/adlmidi/chips/nuked_opl3_v174.h @@ -0,0 +1,23 @@ +#ifndef NUKED_OPL3174_H +#define NUKED_OPL3174_H + +#include "opl_chip_base.h" + +class NukedOPL3v174 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3v174(); + ~NukedOPL3v174() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // NUKED_OPL3174_H diff --git a/src/sound/adlmidi/chips/opl_chip_base.h b/src/sound/adlmidi/chips/opl_chip_base.h new file mode 100644 index 000000000..879d6dad8 --- /dev/null +++ b/src/sound/adlmidi/chips/opl_chip_base.h @@ -0,0 +1,129 @@ +#ifndef ONP_CHIP_BASE_H +#define ONP_CHIP_BASE_H + +#include +#include + +#if !defined(_MSC_VER) && (__cplusplus <= 199711L) +#define final +#define override +#endif + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +class VResampler; +#endif + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +class OPLChipBase +{ +public: + enum { nativeRate = 49716 }; +protected: + uint32_t m_id; + uint32_t m_rate; +public: + OPLChipBase(); + virtual ~OPLChipBase(); + + uint32_t chipId() const { return m_id; } + void setChipId(uint32_t id) { m_id = id; } + + virtual bool canRunAtPcmRate() const = 0; + virtual bool isRunningAtPcmRate() const = 0; + virtual bool setRunningAtPcmRate(bool r) = 0; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + virtual void setAudioTickHandlerInstance(void *instance) = 0; +#endif + + virtual void setRate(uint32_t rate) = 0; + virtual uint32_t effectiveRate() const = 0; + virtual void reset() = 0; + virtual void writeReg(uint16_t addr, uint8_t data) = 0; + + virtual void nativePreGenerate() = 0; + virtual void nativePostGenerate() = 0; + virtual void nativeGenerate(int16_t *frame) = 0; + + virtual void generate(int16_t *output, size_t frames) = 0; + virtual void generateAndMix(int16_t *output, size_t frames) = 0; + virtual void generate32(int32_t *output, size_t frames) = 0; + virtual void generateAndMix32(int32_t *output, size_t frames) = 0; + + virtual const char* emulatorName() = 0; +private: + OPLChipBase(const OPLChipBase &c); + OPLChipBase &operator=(const OPLChipBase &c); +}; + +// A base class providing F-bounded generic and efficient implementations, +// supporting resampling of chip outputs +template +class OPLChipBaseT : public OPLChipBase +{ +public: + OPLChipBaseT(); + virtual ~OPLChipBaseT(); + + bool isRunningAtPcmRate() const override; + bool setRunningAtPcmRate(bool r) override; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void setAudioTickHandlerInstance(void *instance); +#endif + + virtual void setRate(uint32_t rate) override; + uint32_t effectiveRate() const override; + virtual void reset() override; + void generate(int16_t *output, size_t frames) override; + void generateAndMix(int16_t *output, size_t frames) override; + void generate32(int32_t *output, size_t frames) override; + void generateAndMix32(int32_t *output, size_t frames) override; +private: + bool m_runningAtPcmRate; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void *m_audioTickHandlerInstance; +#endif + void nativeTick(int16_t *frame); + void setupResampler(uint32_t rate); + void resetResampler(); + void resampledGenerate(int32_t *output); +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + VResampler *m_resampler; +#else + int32_t m_oldsamples[2]; + int32_t m_samples[2]; + int32_t m_samplecnt; + int32_t m_rateratio; + enum { rsm_frac = 10 }; +#endif + // amplitude scale factors in and out of resampler, varying for chips; + // values are OK to "redefine", the static polymorphism will accept it. + enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 }; +}; + +// A base class which provides frame-by-frame interfaces on emulations which +// don't have a routine for it. It produces outputs in fixed size buffers. +// Fast register updates will suffer some latency because of buffering. +template +class OPLChipBaseBufferedT : public OPLChipBaseT +{ +public: + OPLChipBaseBufferedT() + : OPLChipBaseT(), m_bufferIndex(0) {} + virtual ~OPLChipBaseBufferedT() + {} +public: + void reset() override; + void nativeGenerate(int16_t *frame) override; +protected: + virtual void nativeGenerateN(int16_t *output, size_t frames) = 0; +private: + unsigned m_bufferIndex; + int16_t m_buffer[2 * Buffer]; +}; + +#include "opl_chip_base.tcc" + +#endif // ONP_CHIP_BASE_H diff --git a/src/sound/adlmidi/chips/opl_chip_base.tcc b/src/sound/adlmidi/chips/opl_chip_base.tcc new file mode 100644 index 000000000..48ad103fa --- /dev/null +++ b/src/sound/adlmidi/chips/opl_chip_base.tcc @@ -0,0 +1,294 @@ +#include "opl_chip_base.h" +#include + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +#include +#endif + +#if !defined(LIKELY) && defined(__GNUC__) +#define LIKELY(x) __builtin_expect((x), 1) +#elif !defined(LIKELY) +#define LIKELY(x) (x) +#endif + +#if !defined(UNLIKELY) && defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect((x), 0) +#elif !defined(UNLIKELY) +#define UNLIKELY(x) (x) +#endif + +/* OPLChipBase */ + +inline OPLChipBase::OPLChipBase() : + m_id(0), + m_rate(44100) +{ +} + +inline OPLChipBase::~OPLChipBase() +{ +} + +/* OPLChipBaseT */ + +template +OPLChipBaseT::OPLChipBaseT() + : OPLChipBase(), + m_runningAtPcmRate(false) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , + m_audioTickHandlerInstance(NULL) +#endif +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler = new VResampler; +#endif + setupResampler(m_rate); +} + +template +OPLChipBaseT::~OPLChipBaseT() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + delete m_resampler; +#endif +} + +template +bool OPLChipBaseT::isRunningAtPcmRate() const +{ + return m_runningAtPcmRate; +} + +template +bool OPLChipBaseT::setRunningAtPcmRate(bool r) +{ + if(r != m_runningAtPcmRate) + { + if(r && !static_cast(this)->canRunAtPcmRate()) + return false; + m_runningAtPcmRate = r; + static_cast(this)->setRate(m_rate); + } + return true; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +template +void OPLChipBaseT::setAudioTickHandlerInstance(void *instance) +{ + m_audioTickHandlerInstance = instance; +} +#endif + +template +void OPLChipBaseT::setRate(uint32_t rate) +{ + uint32_t oldRate = m_rate; + m_rate = rate; + if(rate != oldRate) + setupResampler(rate); + else + resetResampler(); +} + +template +uint32_t OPLChipBaseT::effectiveRate() const +{ + return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate; +} + +template +void OPLChipBaseT::reset() +{ + resetResampler(); +} + +template +void OPLChipBaseT::generate(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = (int32_t)output[c] + frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generate32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + static_cast(this)->resampledGenerate(output); + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + output[0] += frame[0]; + output[1] += frame[1]; + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::nativeTick(int16_t *frame) +{ +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + adl_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate()); +#endif + static_cast(this)->nativeGenerate(frame); +} + +template +void OPLChipBaseT::setupResampler(uint32_t rate) +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->setup(rate * (1.0 / 49716), 2, 48); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; + m_rateratio = (int32_t)((rate << rsm_frac) / 49716); +#endif +} + +template +void OPLChipBaseT::resetResampler() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->reset(); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; +#endif +} + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + VResampler *rsm = m_resampler; + float scale = (float)T::resamplerPreAmplify / + (float)T::resamplerPostAttenuate; + float f_in[2]; + float f_out[2]; + rsm->inp_count = 0; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + while(rsm->process(), rsm->out_count != 0) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + f_in[0] = scale * (float)in[0]; + f_in[1] = scale * (float)in[1]; + rsm->inp_count = 1; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + } + output[0] = std::lround(f_out[0]); + output[1] = std::lround(f_out[1]); +} +#else +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + int32_t samplecnt = m_samplecnt; + const int32_t rateratio = m_rateratio; + while(samplecnt >= rateratio) + { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + int16_t buffer[2]; + static_cast(this)->nativeTick(buffer); + m_samples[0] = buffer[0] * T::resamplerPreAmplify; + m_samples[1] = buffer[1] * T::resamplerPreAmplify; + samplecnt -= rateratio; + } + output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt) + + m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt) + + m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + m_samplecnt = samplecnt + (1 << rsm_frac); +} +#endif + +/* OPLChipBaseBufferedT */ + +template +void OPLChipBaseBufferedT::reset() +{ + OPLChipBaseT::reset(); + m_bufferIndex = 0; +} + +template +void OPLChipBaseBufferedT::nativeGenerate(int16_t *frame) +{ + unsigned bufferIndex = m_bufferIndex; + if(bufferIndex == 0) + static_cast(this)->nativeGenerateN(m_buffer, Buffer); + frame[0] = m_buffer[2 * bufferIndex]; + frame[1] = m_buffer[2 * bufferIndex + 1]; + bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0; + m_bufferIndex = bufferIndex; +} diff --git a/src/sound/adlmidi/dbopl.cpp b/src/sound/adlmidi/dbopl.cpp deleted file mode 100644 index fb28e207d..000000000 --- a/src/sound/adlmidi/dbopl.cpp +++ /dev/null @@ -1,2045 +0,0 @@ -#ifdef ADLMIDI_USE_DOSBOX_OPL - -#ifdef __MINGW32__ -typedef struct vswprintf {} swprintf; -#endif -/* - * Copyright (C) 2002-2010 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. - Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 - Except for the table generation it's all integer math - Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms - The generation was based on the MAME implementation but tried to have it use less memory and be faster in general - MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times - - //TODO Don't delay first operator 1 sample in opl3 mode - //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter - //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? - //TODO Check if having the same accuracy in all frequency multipliers sounds better or not - - //DUNNO Keyon in 4op, switch to 2op without keyoff. -*/ - -/* $Id: dbopl.cpp,v 1.10 2009-06-10 19:54:51 harekiet Exp $ */ - - -#include -#include -#include -#include "dbopl.h" - -#define DB_MAX(x, y) ((x) > (y) ? (x) : (y)) -#define DB_MIN(x, y) ((x) < (y) ? (x) : (y)) - -#define DBOPL_CLAMP(V, MIN, MAX) DB_MAX(DB_MIN(V, (MAX)), (MIN)) - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -namespace DBOPL -{ - -#define OPLRATE ((double)(14318180.0 / 288.0)) -#define TREMOLO_TABLE 52 - - //Try to use most precision for frequencies - //Else try to keep different waves in synch - //#define WAVE_PRECISION 1 - #ifndef WAVE_PRECISION - //Wave bits available in the top of the 32bit range - //Original adlib uses 10.10, we use 10.22 -#define WAVE_BITS 10 - #else - //Need some extra bits at the top to have room for octaves and frequency multiplier - //We support to 8 times lower rate - //128 * 15 * 8 = 15350, 2^13.9, so need 14 bits -#define WAVE_BITS 14 - #endif -#define WAVE_SH ( 32 - WAVE_BITS ) -#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) - - //Use the same accuracy as the waves -#define LFO_SH ( WAVE_SH - 10 ) - //LFO is controlled by our tremolo 256 sample limit -#define LFO_MAX ( 256 << ( LFO_SH ) ) - - - //Maximum amount of attenuation bits - //Envelope goes to 511, 9 bits - #if (DBOPL_WAVE == WAVE_TABLEMUL ) - //Uses the value directly -#define ENV_BITS ( 9 ) - #else - //Add 3 bits here for more accuracy and would have to be shifted up either way -#define ENV_BITS ( 9 ) - #endif - //Limits of the envelope with those bits and when the envelope goes silent -#define ENV_MIN 0 -#define ENV_EXTRA ( ENV_BITS - 9 ) -#define ENV_MAX ( 511 << ENV_EXTRA ) -#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) -#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) - - //Attack/decay/release rate counter shift -#define RATE_SH 24 -#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) - //Has to fit within 16bit lookuptable -#define MUL_SH 16 - - //Check some ranges - #if ENV_EXTRA > 3 -#error Too many envelope bits - #endif - - - //How much to substract from the base value for the final attenuation - static const Bit8u KslCreateTable[16] = - { - //0 will always be be lower than 7 * 8 - 64, 32, 24, 19, - 16, 12, 11, 10, - 8, 6, 5, 4, - 3, 2, 1, 0, - }; - -#define M(_X_) ((Bit8u)( (_X_) * 2)) - static const Bit8u FreqCreateTable[16] = - { - M(0.5), M(1), M(2), M(3), M(4), M(5), M(6), M(7), - M(8), M(9), M(10), M(10), M(12), M(12), M(15), M(15) - }; -#undef M - - //We're not including the highest attack rate, that gets a special value - static const Bit8u AttackSamplesTable[13] = - { - 69, 55, 46, 40, - 35, 29, 23, 20, - 19, 15, 11, 10, - 9 - }; - //On a real opl these values take 8 samples to reach and are based upon larger tables - static const Bit8u EnvelopeIncreaseTable[13] = - { - 4, 5, 6, 7, - 8, 10, 12, 14, - 16, 20, 24, 28, - 32, - }; - - #if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) - static Bit16u ExpTable[ 256 ]; - #endif - - #if ( DBOPL_WAVE == WAVE_HANDLER ) - //PI table used by WAVEHANDLER - static Bit16u SinTable[ 512 ]; - #endif - - #if ( DBOPL_WAVE > WAVE_HANDLER ) - //Layout of the waveform table in 512 entry intervals - //With overlapping waves we reduce the table to half it's size - - // | |//\\|____|WAV7|//__|/\ |____|/\/\| - // |\\//| | |WAV7| | \/| | | - // |06 |0126|17 |7 |3 |4 |4 5 |5 | - - //6 is just 0 shifted and masked - - static Bit16s WaveTable[ 8 * 512 ]; - //Distance into WaveTable the wave starts - static const Bit16u WaveBaseTable[8] = - { - 0x000, 0x200, 0x200, 0x800, - 0xa00, 0xc00, 0x100, 0x400, - - }; - //Mask the counter with this - static const Bit16u WaveMaskTable[8] = - { - 1023, 1023, 511, 511, - 1023, 1023, 512, 1023, - }; - - //Where to start the counter on at keyon - static const Bit16u WaveStartTable[8] = - { - 512, 0, 0, 0, - 0, 512, 512, 256, - }; - #endif - - #if ( DBOPL_WAVE == WAVE_TABLEMUL ) - static Bit16u MulTable[ 384 ]; - #endif - - static Bit8u KslTable[ 8 * 16 ]; - static Bit8u TremoloTable[ TREMOLO_TABLE ]; - //Start of a channel behind the chip struct start - static Bit16u ChanOffsetTable[32]; - //Start of an operator behind the chip struct start - static Bit16u OpOffsetTable[64]; - - //The lower bits are the shift of the operator vibrato value - //The highest bit is right shifted to generate -1 or 0 for negation - //So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 - static const Bit8s VibratoTable[ 8 ] = - { - 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, - 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 - }; - - //Shift strength for the ksl value determined by ksl strength - static const Bit8u KslShiftTable[4] = - { - 31, 1, 2, 0 - }; - - //Generate a table index and table shift value using input value from a selected rate - static void EnvelopeSelect(Bit8u val, Bit8u &index, Bit8u &shift) - { - if(val < 13 * 4) //Rate 0 - 12 - { - shift = 12 - (val >> 2); - index = val & 3; - } - else if(val < 15 * 4) //rate 13 - 14 - { - shift = 0; - index = val - 12 * 4; - } - else //rate 15 and up - { - shift = 0; - index = 12; - } - } - - #if ( DBOPL_WAVE == WAVE_HANDLER ) - /* - Generate the different waveforms out of the sine/exponetial table using handlers - */ - static inline Bits MakeVolume(Bitu wave, Bitu volume) - { - Bitu total = wave + volume; - Bitu index = total & 0xff; - Bitu sig = ExpTable[ index ]; - Bitu exp = total >> 8; - #if 0 - - //Check if we overflow the 31 shift limit - if(exp >= 32) - LOG_MSG("WTF %d %d", total, exp); - - #endif - return (sig >> exp); - }; - - static Bits DB_FASTCALL WaveForm0(Bitu i, Bitu volume) - { - Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - return (MakeVolume(wave, volume) ^ neg) - neg; - } - static Bits DB_FASTCALL WaveForm1(Bitu i, Bitu volume) - { - Bit32u wave = SinTable[i & 511]; - wave |= (((i ^ 512) & 512) - 1) >> (32 - 12); - return MakeVolume(wave, volume); - } - static Bits DB_FASTCALL WaveForm2(Bitu i, Bitu volume) - { - Bitu wave = SinTable[i & 511]; - return MakeVolume(wave, volume); - } - static Bits DB_FASTCALL WaveForm3(Bitu i, Bitu volume) - { - Bitu wave = SinTable[i & 255]; - wave |= (((i ^ 256) & 256) - 1) >> (32 - 12); - return MakeVolume(wave, volume); - } - static Bits DB_FASTCALL WaveForm4(Bitu i, Bitu volume) - { - //Twice as fast - i <<= 1; - Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - wave |= (((i ^ 512) & 512) - 1) >> (32 - 12); - return (MakeVolume(wave, volume) ^ neg) - neg; - } - static Bits DB_FASTCALL WaveForm5(Bitu i, Bitu volume) - { - //Twice as fast - i <<= 1; - Bitu wave = SinTable[i & 511]; - wave |= (((i ^ 512) & 512) - 1) >> (32 - 12); - return MakeVolume(wave, volume); - } - static Bits DB_FASTCALL WaveForm6(Bitu i, Bitu volume) - { - Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0 - return (MakeVolume(0, volume) ^ neg) - neg; - } - static Bits DB_FASTCALL WaveForm7(Bitu i, Bitu volume) - { - //Negative is reversed here - Bits neg = ((i >> 9) & 1) - 1; - Bitu wave = (i << 3); - //When negative the volume also runs backwards - wave = ((wave ^ neg) - neg) & 4095; - return (MakeVolume(wave, volume) ^ neg) - neg; - } - - static const WaveHandler WaveHandlerTable[8] = - { - WaveForm0, WaveForm1, WaveForm2, WaveForm3, - WaveForm4, WaveForm5, WaveForm6, WaveForm7 - }; - - #endif - - /* - Operator - */ - - //We zero out when rate == 0 - inline void Operator::UpdateAttack(const Chip *chip) - { - Bit8u rate = reg60 >> 4; - - if(rate) - { - Bit8u val = (rate << 2) + ksr; - attackAdd = chip->attackRates[ val ]; - rateZero &= ~(1 << ATTACK); - } - else - { - attackAdd = 0; - rateZero |= (1 << ATTACK); - } - } - inline void Operator::UpdateDecay(const Chip *chip) - { - Bit8u rate = reg60 & 0xf; - - if(rate) - { - Bit8u val = (rate << 2) + ksr; - decayAdd = chip->linearRates[ val ]; - rateZero &= ~(1 << DECAY); - } - else - { - decayAdd = 0; - rateZero |= (1 << DECAY); - } - } - inline void Operator::UpdateRelease(const Chip *chip) - { - Bit8u rate = reg80 & 0xf; - - if(rate) - { - Bit8u val = (rate << 2) + ksr; - releaseAdd = chip->linearRates[ val ]; - rateZero &= ~(1 << RELEASE); - - if(!(reg20 & MASK_SUSTAIN)) - rateZero &= ~(1 << SUSTAIN); - } - else - { - rateZero |= (1 << RELEASE); - releaseAdd = 0; - - if(!(reg20 & MASK_SUSTAIN)) - rateZero |= (1 << SUSTAIN); - } - } - - inline void Operator::UpdateAttenuation() - { - Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); - Bit32u tl = reg40 & 0x3f; - Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; - //Make sure the attenuation goes to the right bits - totalLevel = tl << (ENV_BITS - 7); //Total level goes 2 bits below max - totalLevel += (kslBase << ENV_EXTRA) >> kslShift; - } - - void Operator::UpdateFrequency() - { - Bit32u freq = chanData & ((1 << 10) - 1); - Bit32u block = (chanData >> 10) & 0xff; - #ifdef WAVE_PRECISION - block = 7 - block; - waveAdd = (freq * freqMul) >> block; - #else - waveAdd = (freq << block) * freqMul; - #endif - - if(reg20 & MASK_VIBRATO) - { - vibStrength = (Bit8u)(freq >> 7); - #ifdef WAVE_PRECISION - vibrato = (vibStrength * freqMul) >> block; - #else - vibrato = (vibStrength << block) * freqMul; - #endif - } - else - { - vibStrength = 0; - vibrato = 0; - } - } - - void Operator::UpdateRates(const Chip *chip) - { - //Mame seems to reverse this where enabling ksr actually lowers - //the rate, but pdf manuals says otherwise? - Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); - - if(!(reg20 & MASK_KSR)) - newKsr >>= 2; - - if(ksr == newKsr) - return; - - ksr = newKsr; - UpdateAttack(chip); - UpdateDecay(chip); - UpdateRelease(chip); - } - - INLINE Bit32s Operator::RateForward(Bit32u add) - { - rateIndex += add; - Bit32s ret = rateIndex >> RATE_SH; - rateIndex = rateIndex & RATE_MASK; - return ret; - } - - template< Operator::State yes> - Bits Operator::TemplateVolume() - { - Bit32s vol = volume; - Bit32s change; - - switch(yes) - { - case OFF: - return ENV_MAX; - - case ATTACK: - change = RateForward(attackAdd); - - if(!change) - return vol; - - vol += ((~vol) * change) >> 3; - - if(vol < ENV_MIN) - { - volume = ENV_MIN; - rateIndex = 0; - SetState(DECAY); - return ENV_MIN; - } - - break; - - case DECAY: - vol += RateForward(decayAdd); - - if(GCC_UNLIKELY(vol >= sustainLevel)) - { - //Check if we didn't overshoot max attenuation, then just go off - if(GCC_UNLIKELY(vol >= ENV_MAX)) - { - volume = ENV_MAX; - SetState(OFF); - return ENV_MAX; - } - - //Continue as sustain - rateIndex = 0; - SetState(SUSTAIN); - } - - break; - - case SUSTAIN: - if(reg20 & MASK_SUSTAIN) - return vol; - - //In sustain phase, but not sustaining, do regular release - case RELEASE: - vol += RateForward(releaseAdd);; - - if(GCC_UNLIKELY(vol >= ENV_MAX)) - { - volume = ENV_MAX; - SetState(OFF); - return ENV_MAX; - } - - break; - } - - volume = vol; - return vol; - } - - static const VolumeHandler VolumeHandlerTable[5] = - { - &Operator::TemplateVolume< Operator::OFF >, - &Operator::TemplateVolume< Operator::RELEASE >, - &Operator::TemplateVolume< Operator::SUSTAIN >, - &Operator::TemplateVolume< Operator::DECAY >, - &Operator::TemplateVolume< Operator::ATTACK > - }; - - INLINE Bitu Operator::ForwardVolume() - { - return currentLevel + (this->*volHandler)(); - } - - - INLINE Bitu Operator::ForwardWave() - { - waveIndex += waveCurrent; - return waveIndex >> WAVE_SH; - } - - void Operator::Write20(const Chip *chip, Bit8u val) - { - Bit8u change = (reg20 ^ val); - - if(!change) - return; - - reg20 = val; - //Shift the tremolo bit over the entire register, saved a branch, YES! - tremoloMask = (Bit8s)(val) >> 7; - tremoloMask &= ~((1 << ENV_EXTRA) - 1); - - //Update specific features based on changes - if(change & MASK_KSR) - UpdateRates(chip); - - //With sustain enable the volume doesn't change - if(reg20 & MASK_SUSTAIN || (!releaseAdd)) - rateZero |= (1 << SUSTAIN); - else - rateZero &= ~(1 << SUSTAIN); - - //Frequency multiplier or vibrato changed - if(change & (0xf | MASK_VIBRATO)) - { - freqMul = chip->freqMul[ val & 0xf ]; - UpdateFrequency(); - } - } - - void Operator::Write40(const Chip * /*chip*/, Bit8u val) - { - if(!(reg40 ^ val)) - return; - - reg40 = val; - UpdateAttenuation(); - } - - void Operator::Write60(const Chip *chip, Bit8u val) - { - Bit8u change = reg60 ^ val; - reg60 = val; - - if(change & 0x0f) - UpdateDecay(chip); - - if(change & 0xf0) - UpdateAttack(chip); - } - - void Operator::Write80(const Chip *chip, Bit8u val) - { - Bit8u change = (reg80 ^ val); - - if(!change) - return; - - reg80 = val; - Bit8u sustain = val >> 4; - //Turn 0xf into 0x1f - sustain |= (sustain + 1) & 0x10; - sustainLevel = sustain << (ENV_BITS - 5); - - if(change & 0x0f) - UpdateRelease(chip); - } - - void Operator::WriteE0(const Chip *chip, Bit8u val) - { - if(!(regE0 ^ val)) - return; - - //in opl3 mode you can always selet 7 waveforms regardless of waveformselect - Bit8u waveForm = val & ((0x3 & chip->waveFormMask) | (0x7 & chip->opl3Active)); - regE0 = val; - #if ( DBOPL_WAVE == WAVE_HANDLER ) - waveHandler = WaveHandlerTable[ waveForm ]; - #else - waveBase = WaveTable + WaveBaseTable[ waveForm ]; - waveStart = WaveStartTable[ waveForm ] << WAVE_SH; - waveMask = WaveMaskTable[ waveForm ]; - #endif - } - - INLINE void Operator::SetState(Bit8u s) - { - state = s; - volHandler = VolumeHandlerTable[ s ]; - } - - INLINE bool Operator::Silent() const - { - if(!ENV_SILENT(totalLevel + volume)) - return false; - - if(!(rateZero & (1 << state))) - return false; - - return true; - } - - INLINE void Operator::Prepare(const Chip *chip) - { - currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); - waveCurrent = waveAdd; - - if(vibStrength >> chip->vibratoShift) - { - Bit32s add = vibrato >> chip->vibratoShift; - //Sign extend over the shift value - Bit32s neg = chip->vibratoSign; - //Negate the add with -1 or 0 - add = (add ^ neg) - neg; - waveCurrent += add; - } - } - - void Operator::KeyOn(Bit8u mask) - { - if(!keyOn) - { - //Restart the frequency generator - #if ( DBOPL_WAVE > WAVE_HANDLER ) - waveIndex = waveStart; - #else - waveIndex = 0; - #endif - rateIndex = 0; - SetState(ATTACK); - } - - keyOn |= mask; - } - - void Operator::KeyOff(Bit8u mask) - { - keyOn &= ~mask; - - if(!keyOn) - { - if(state != OFF) - SetState(RELEASE); - } - } - - INLINE Bits Operator::GetWave(Bitu index, Bitu vol) - { - #if ( DBOPL_WAVE == WAVE_HANDLER ) - return waveHandler(index, vol << (3 - ENV_EXTRA)); - #elif ( DBOPL_WAVE == WAVE_TABLEMUL ) - return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; - #elif ( DBOPL_WAVE == WAVE_TABLELOG ) - Bit32s wave = waveBase[ index & waveMask ]; - Bit32u total = (wave & 0x7fff) + vol << (3 - ENV_EXTRA); - Bit32s sig = ExpTable[ total & 0xff ]; - Bit32u exp = total >> 8; - Bit32s neg = wave >> 16; - return ((sig ^ neg) - neg) >> exp; - #else -#error "No valid wave routine" - #endif - } - - Bits INLINE Operator::GetSample(Bits modulation) - { - Bitu vol = ForwardVolume(); - - if(ENV_SILENT(vol)) - { - //Simply forward the wave - waveIndex += waveCurrent; - return 0; - } - else - { - Bitu index = ForwardWave(); - index += modulation; - return GetWave(index, vol); - } - } - - Operator::Operator() - { - chanData = 0; - freqMul = 0; - waveIndex = 0; - waveAdd = 0; - waveCurrent = 0; - keyOn = 0; - ksr = 0; - reg20 = 0; - reg40 = 0; - reg60 = 0; - reg80 = 0; - regE0 = 0; - SetState(OFF); - rateZero = (1 << OFF); - sustainLevel = ENV_MAX; - currentLevel = ENV_MAX; - totalLevel = ENV_MAX; - volume = ENV_MAX; - releaseAdd = 0; - } - - /* - Channel - */ - - Channel::Channel() - { - old[0] = old[1] = 0; - chanData = 0; - regB0 = 0; - regC0 = 0; - maskLeft = -1; - maskRight = -1; - feedback = 31; - fourMask = 0; - synthHandler = &Channel::BlockTemplate< sm2FM >; - } - - void Channel::SetChanData(const Chip *chip, Bit32u data) - { - Bit32u change = chanData ^ data; - chanData = data; - Op(0)->chanData = data; - Op(1)->chanData = data; - //Since a frequency update triggered this, always update frequency - Op(0)->UpdateFrequency(); - Op(1)->UpdateFrequency(); - - if(change & (0xff << SHIFT_KSLBASE)) - { - Op(0)->UpdateAttenuation(); - Op(1)->UpdateAttenuation(); - } - - if(change & (0xff << SHIFT_KEYCODE)) - { - Op(0)->UpdateRates(chip); - Op(1)->UpdateRates(chip); - } - } - - void Channel::UpdateFrequency(const Chip *chip, Bit8u fourOp) - { - //Extrace the frequency bits - Bit32u data = chanData & 0xffff; - Bit32u kslBase = KslTable[ data >> 6 ]; - Bit32u keyCode = (data & 0x1c00) >> 9; - - if(chip->reg08 & 0x40) - { - keyCode |= (data & 0x100) >> 8; /* notesel == 1 */ - } - else - { - keyCode |= (data & 0x200) >> 9; /* notesel == 0 */ - } - - //Add the keycode and ksl into the highest bits of chanData - data |= (keyCode << SHIFT_KEYCODE) | (kslBase << SHIFT_KSLBASE); - (this + 0)->SetChanData(chip, data); - - if(fourOp & 0x3f) - (this + 1)->SetChanData(chip, data); - } - - void Channel::WriteA0(const Chip *chip, Bit8u val) - { - Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; - - //Don't handle writes to silent fourop channels - if(fourOp > 0x80) - return; - - Bit32u change = (chanData ^ val) & 0xff; - - if(change) - { - chanData ^= change; - UpdateFrequency(chip, fourOp); - } - } - - void Channel::WriteB0(const Chip *chip, Bit8u val) - { - Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; - - //Don't handle writes to silent fourop channels - if(fourOp > 0x80) - return; - - Bitu change = (chanData ^ (val << 8)) & 0x1f00; - - if(change) - { - chanData ^= change; - UpdateFrequency(chip, fourOp); - } - - //Check for a change in the keyon/off state - if(!((val ^ regB0) & 0x20)) - return; - - regB0 = val; - - if(val & 0x20) - { - Op(0)->KeyOn(0x1); - Op(1)->KeyOn(0x1); - - if(fourOp & 0x3f) - { - (this + 1)->Op(0)->KeyOn(1); - (this + 1)->Op(1)->KeyOn(1); - } - } - else - { - Op(0)->KeyOff(0x1); - Op(1)->KeyOff(0x1); - - if(fourOp & 0x3f) - { - (this + 1)->Op(0)->KeyOff(1); - (this + 1)->Op(1)->KeyOff(1); - } - } - } - - void Channel::WriteC0(const Chip *chip, Bit8u val) - { - Bit8u change = val ^ regC0; - - if(!change) - return; - - regC0 = val; - feedback = (val >> 1) & 7; - - if(feedback) - { - //We shift the input to the right 10 bit wave index value - feedback = 9 - feedback; - } - else - feedback = 31; - - //Select the new synth mode - if(chip->opl3Active) - { - //4-op mode enabled for this channel - if((chip->reg104 & fourMask) & 0x3f) - { - Channel *chan0, *chan1; - - //Check if it's the 2nd channel in a 4-op - if(!(fourMask & 0x80)) - { - chan0 = this; - chan1 = this + 1; - } - else - { - chan0 = this - 1; - chan1 = this; - } - - Bit8u synth = ((chan0->regC0 & 1) << 0) | ((chan1->regC0 & 1) << 1); - - switch(synth) - { - case 0: - chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; - break; - - case 1: - chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; - break; - - case 2: - chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; - break; - - case 3: - chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; - break; - } - - //Disable updating percussion channels - } - else if((fourMask & 0x40) && (chip->regBD & 0x20)) - { - //Regular dual op, am or fm - } - else if(val & 1) - synthHandler = &Channel::BlockTemplate< sm3AM >; - else - synthHandler = &Channel::BlockTemplate< sm3FM >; - - maskLeft = (val & 0x10) ? -1 : 0; - maskRight = (val & 0x20) ? -1 : 0; - //opl2 active - } - else - { - //Disable updating percussion channels - if((fourMask & 0x40) && (chip->regBD & 0x20)) - { - //Regular dual op, am or fm - } - else if(val & 1) - synthHandler = &Channel::BlockTemplate< sm2AM >; - else - synthHandler = &Channel::BlockTemplate< sm2FM >; - } - } - - void Channel::ResetC0(const Chip *chip) - { - Bit8u val = regC0; - regC0 ^= 0xff; - WriteC0(chip, val); - } - - template< bool opl3Mode> - INLINE void Channel::GeneratePercussion(Chip *chip, Bit32s *output) - { - Channel *chan = this; - //BassDrum - Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; - old[0] = old[1]; - old[1] = Op(0)->GetSample(mod); - - //When bassdrum is in AM mode first operator is ignoed - if(chan->regC0 & 1) - mod = 0; - else - mod = old[0]; - - Bit32s sample = Op(1)->GetSample(mod); - //Precalculate stuff used by other outputs - Bit32u noiseBit = chip->ForwardNoise() & 0x1; - Bit32u c2 = Op(2)->ForwardWave(); - Bit32u c5 = Op(5)->ForwardWave(); - Bit32u phaseBit = (((c2 & 0x88) ^ ((c2 << 5) & 0x80)) | ((c5 ^ (c5 << 2)) & 0x20)) ? 0x02 : 0x00; - //Hi-Hat - Bit32u hhVol = Op(2)->ForwardVolume(); - - if(!ENV_SILENT(hhVol)) - { - Bit32u hhIndex = (phaseBit << 8) | (0x34 << (phaseBit ^ (noiseBit << 1))); - sample += Op(2)->GetWave(hhIndex, hhVol); - } - - //Snare Drum - Bit32u sdVol = Op(3)->ForwardVolume(); - - if(!ENV_SILENT(sdVol)) - { - Bit32u sdIndex = (0x100 + (c2 & 0x100)) ^ (noiseBit << 8); - sample += Op(3)->GetWave(sdIndex, sdVol); - } - - //Tom-tom - sample += Op(4)->GetSample(0); - //Top-Cymbal - Bit32u tcVol = Op(5)->ForwardVolume(); - - if(!ENV_SILENT(tcVol)) - { - Bit32u tcIndex = (1 + phaseBit) << 8; - sample += Op(5)->GetWave(tcIndex, tcVol); - } - - sample <<= 1; - - if(opl3Mode) - { - output[0] += sample; - output[1] += sample; - } - else - output[0] += sample; - } - - template - Channel *Channel::BlockTemplate(Chip *chip, Bit32u samples, Bit32s *output) - { - switch(mode) - { - case sm2AM: - case sm3AM: - if(Op(0)->Silent() && Op(1)->Silent()) - { - old[0] = old[1] = 0; - return (this + 1); - } - - break; - - case sm2FM: - case sm3FM: - if(Op(1)->Silent()) - { - old[0] = old[1] = 0; - return (this + 1); - } - - break; - - case sm3FMFM: - if(Op(3)->Silent()) - { - old[0] = old[1] = 0; - return (this + 2); - } - - break; - - case sm3AMFM: - if(Op(0)->Silent() && Op(3)->Silent()) - { - old[0] = old[1] = 0; - return (this + 2); - } - - break; - - case sm3FMAM: - if(Op(1)->Silent() && Op(3)->Silent()) - { - old[0] = old[1] = 0; - return (this + 2); - } - - break; - - case sm3AMAM: - if(Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent()) - { - old[0] = old[1] = 0; - return (this + 2); - } - - break; - - default: - break; - } - - //Init the operators with the the current vibrato and tremolo values - Op(0)->Prepare(chip); - Op(1)->Prepare(chip); - - if(mode > sm4Start) - { - Op(2)->Prepare(chip); - Op(3)->Prepare(chip); - } - - if(mode > sm6Start) - { - Op(4)->Prepare(chip); - Op(5)->Prepare(chip); - } - - for(Bitu i = 0; i < samples; i++) - { - //Early out for percussion handlers - if(mode == sm2Percussion) - { - GeneratePercussion(chip, output + i); - continue; //Prevent some unitialized value bitching - } - else if(mode == sm3Percussion) - { - GeneratePercussion(chip, output + i * 2); - continue; //Prevent some unitialized value bitching - } - - //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise - Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; - old[0] = old[1]; - old[1] = Op(0)->GetSample(mod); - Bit32s sample; - Bit32s out0 = old[0]; - - if(mode == sm2AM || mode == sm3AM) - sample = out0 + Op(1)->GetSample(0); - else if(mode == sm2FM || mode == sm3FM) - sample = Op(1)->GetSample(out0); - else if(mode == sm3FMFM) - { - Bits next = Op(1)->GetSample(out0); - next = Op(2)->GetSample(next); - sample = Op(3)->GetSample(next); - } - else if(mode == sm3AMFM) - { - sample = out0; - Bits next = Op(1)->GetSample(0); - next = Op(2)->GetSample(next); - sample += Op(3)->GetSample(next); - } - else if(mode == sm3FMAM) - { - sample = Op(1)->GetSample(out0); - Bits next = Op(2)->GetSample(0); - sample += Op(3)->GetSample(next); - } - else if(mode == sm3AMAM) - { - sample = out0; - Bits next = Op(1)->GetSample(0); - sample += Op(2)->GetSample(next); - sample += Op(3)->GetSample(0); - } - - switch(mode) - { - case sm2AM: - case sm2FM: - output[ i ] += sample; - break; - - case sm3AM: - case sm3FM: - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - output[ i * 2 + 0 ] += sample & maskLeft; - output[ i * 2 + 1 ] += sample & maskRight; - break; - - default: - break; - } - } - - switch(mode) - { - case sm2AM: - case sm2FM: - case sm3AM: - case sm3FM: - return (this + 1); - - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - return(this + 2); - - case sm2Percussion: - case sm3Percussion: - return(this + 3); - } - - return 0; - } - - /* - Chip - */ - - Chip::Chip() - { - reg08 = 0; - reg04 = 0; - regBD = 0; - reg104 = 0; - opl3Active = 0; - //Extra zeros! - vibratoIndex = 0; - tremoloIndex = 0; - vibratoSign = 0; - vibratoShift = 0; - tremoloValue = 0; - vibratoStrength = 0; - tremoloStrength = 0; - waveFormMask = 0; - lfoCounter = 0; - lfoAdd = 0; - noiseCounter = 0; - noiseAdd = 0; - noiseValue = 0; - memset(freqMul, 0, sizeof(Bit32u) * 16); - memset(linearRates, 0, sizeof(Bit32u) * 76); - memset(attackRates, 0, sizeof(Bit32u) * 76); - } - - INLINE Bit32u Chip::ForwardNoise() - { - noiseCounter += noiseAdd; - Bitu count = noiseCounter >> LFO_SH; - noiseCounter &= WAVE_MASK; - - for(; count > 0; --count) - { - //Noise calculation from mame - noiseValue ^= (0x800302) & (0 - (noiseValue & 1)); - noiseValue >>= 1; - } - - return noiseValue; - } - - INLINE Bit32u Chip::ForwardLFO(Bit32u samples) - { - //Current vibrato value, runs 4x slower than tremolo - vibratoSign = (VibratoTable[ vibratoIndex >> 2]) >> 7; - vibratoShift = (VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; - tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; - //Check hom many samples there can be done before the value changes - Bit32u todo = LFO_MAX - lfoCounter; - Bit32u count = (todo + lfoAdd - 1) / lfoAdd; - - if(count > samples) - { - count = samples; - lfoCounter += count * lfoAdd; - } - else - { - lfoCounter += count * lfoAdd; - lfoCounter &= (LFO_MAX - 1); - //Maximum of 7 vibrato value * 4 - vibratoIndex = (vibratoIndex + 1) & 31; - - //Clip tremolo to the the table size - if(tremoloIndex + 1 < TREMOLO_TABLE) - ++tremoloIndex; - else - tremoloIndex = 0; - } - - return count; - } - - - void Chip::WriteBD(Bit8u val) - { - Bit8u change = regBD ^ val; - - if(!change) - return; - - regBD = val; - //TODO could do this with shift and xor? - vibratoStrength = (val & 0x40) ? 0x00 : 0x01; - tremoloStrength = (val & 0x80) ? 0x00 : 0x02; - - if(val & 0x20) - { - //Drum was just enabled, make sure channel 6 has the right synth - if(change & 0x20) - { - if(opl3Active) - chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; - else - chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; - } - - //Bass Drum - if(val & 0x10) - { - chan[6].op[0].KeyOn(0x2); - chan[6].op[1].KeyOn(0x2); - } - else - { - chan[6].op[0].KeyOff(0x2); - chan[6].op[1].KeyOff(0x2); - } - - //Hi-Hat - if(val & 0x1) - chan[7].op[0].KeyOn(0x2); - else - chan[7].op[0].KeyOff(0x2); - - //Snare - if(val & 0x8) - chan[7].op[1].KeyOn(0x2); - else - chan[7].op[1].KeyOff(0x2); - - //Tom-Tom - if(val & 0x4) - chan[8].op[0].KeyOn(0x2); - else - chan[8].op[0].KeyOff(0x2); - - //Top Cymbal - if(val & 0x2) - chan[8].op[1].KeyOn(0x2); - else - chan[8].op[1].KeyOff(0x2); - - //Toggle keyoffs when we turn off the percussion - } - else if(change & 0x20) - { - //Trigger a reset to setup the original synth handler - chan[6].ResetC0(this); - chan[6].op[0].KeyOff(0x2); - chan[6].op[1].KeyOff(0x2); - chan[7].op[0].KeyOff(0x2); - chan[7].op[1].KeyOff(0x2); - chan[8].op[0].KeyOff(0x2); - chan[8].op[1].KeyOff(0x2); - } - } - - -#define REGOP( _FUNC_ ) \ - index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ - if ( OpOffsetTable[ index ] ) { \ - Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ - regOp->_FUNC_( this, val ); \ - } - -#define REGCHAN( _FUNC_ ) \ - index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ - if ( ChanOffsetTable[ index ] ) { \ - Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ - regChan->_FUNC_( this, val ); \ - } - - void Chip::WriteReg(Bit32u reg, Bit8u val) - { - Bitu index = 0; - - switch((reg & 0xf0) >> 4) - { - case 0x00 >> 4: - if(reg == 0x01) - waveFormMask = (val & 0x20) ? 0x7 : 0x0; - else if(reg == 0x104) - { - //Only detect changes in lowest 6 bits - if(!((reg104 ^ val) & 0x3f)) - return; - - //Always keep the highest bit enabled, for checking > 0x80 - reg104 = 0x80 | (val & 0x3f); - } - else if(reg == 0x105) - { - //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register - if(!((opl3Active ^ val) & 1)) - return; - - opl3Active = (val & 1) ? 0xff : 0; - - //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers - for(int i = 0; i < 18; i++) - chan[i].ResetC0(this); - } - else if(reg == 0x08) - reg08 = val; - - case 0x10 >> 4: - break; - - case 0x20 >> 4: - case 0x30 >> 4: - REGOP(Write20); - break; - - case 0x40 >> 4: - case 0x50 >> 4: - REGOP(Write40); - break; - - case 0x60 >> 4: - case 0x70 >> 4: - REGOP(Write60); - break; - - case 0x80 >> 4: - case 0x90 >> 4: - REGOP(Write80); - break; - - case 0xa0 >> 4: - REGCHAN(WriteA0); - break; - - case 0xb0 >> 4: - if(reg == 0xbd) - WriteBD(val); - else - REGCHAN(WriteB0); - - break; - - case 0xc0 >> 4: - REGCHAN(WriteC0); - - case 0xd0 >> 4: - break; - - case 0xe0 >> 4: - case 0xf0 >> 4: - REGOP(WriteE0); - break; - } - } - - - Bit32u Chip::WriteAddr(Bit32u port, Bit8u val) - { - switch(port & 3) - { - case 0: - return val; - - case 2: - if(opl3Active || (val == 0x05)) - return 0x100 | val; - else - return val; - } - - return 0; - } - - void Chip::GenerateBlock2(Bitu total, Bit32s *output) - { - while(total > 0) - { - Bit32u samples = ForwardLFO(total); - memset(output, 0, sizeof(Bit32s) * samples); - int count = 0; - - for(Channel *ch = chan; ch < chan + 9;) - { - count++; - ch = (ch->*(ch->synthHandler))(this, samples, output); - } - - total -= samples; - output += samples; - } - } - - void Chip::GenerateBlock3(Bitu total, Bit32s *output) - { - while(total > 0) - { - Bit32u samples = ForwardLFO((Bit32u)total); - memset(output, 0, sizeof(Bit32s) * samples * 2); - int count = 0; - - for(Channel *ch = chan; ch < chan + 18;) - { - count++; - ch = (ch->*(ch->synthHandler))(this, samples, output); - } - - total -= samples; - output += samples * 2; - } - } - - void Chip::GenerateBlock2_Mix(Bitu total, Bit32s *output) - { - while(total > 0) - { - Bit32u samples = ForwardLFO((Bit32u)total); - int count = 0; - for(Channel *ch = chan; ch < chan + 9;) - { - count++; - ch = (ch->*(ch->synthHandler))(this, samples, output); - } - - total -= samples; - output += samples; - } - } - - void Chip::GenerateBlock3_Mix(Bitu total, Bit32s *output) - { - while(total > 0) - { - Bit32u samples = ForwardLFO(total); - int count = 0; - for(Channel *ch = chan; ch < chan + 18;) - { - count++; - ch = (ch->*(ch->synthHandler))(this, samples, output); - } - total -= samples; - output += samples * 2; - } - } - - void Chip::Setup(Bit32u rate) - { - double original = OPLRATE; - // double original = rate; - double scale = original / (double)rate; - //Noise counter is run at the same precision as general waves - noiseAdd = (Bit32u)(0.5 + scale * (1 << LFO_SH)); - noiseCounter = 0; - noiseValue = 1; //Make sure it triggers the noise xor the first time - //The low frequency oscillation counter - //Every time his overflows vibrato and tremoloindex are increased - lfoAdd = (Bit32u)(0.5 + scale * (1 << LFO_SH)); - lfoCounter = 0; - vibratoIndex = 0; - tremoloIndex = 0; - //With higher octave this gets shifted up - //-1 since the freqCreateTable = *2 - #ifdef WAVE_PRECISION - double freqScale = (1 << 7) * scale * (1 << (WAVE_SH - 1 - 10)); - - for(int i = 0; i < 16; i++) - freqMul[i] = (Bit32u)(0.5 + freqScale * FreqCreateTable[ i ]); - - #else - Bit32u freqScale = (Bit32u)(0.5 + scale * (1 << (WAVE_SH - 1 - 10))); - - for(int i = 0; i < 16; i++) - freqMul[i] = freqScale * FreqCreateTable[ i ]; - - #endif - - //-3 since the real envelope takes 8 steps to reach the single value we supply - for(Bit8u i = 0; i < 76; i++) - { - Bit8u index, shift; - EnvelopeSelect(i, index, shift); - linearRates[i] = (Bit32u)(scale * (EnvelopeIncreaseTable[ index ] << (RATE_SH + ENV_EXTRA - shift - 3))); - } - - if(rate == 48000) - { - /* BISQWIT ADD: Use precalculated table for this common sample-rate. - * Because the actual generation code, below, is MOLASSES SLOW on DOS. - */ - static const Bit32u precalculated_table[62] = - { - 2152, 2700, 3228, 3712, 4304, 5399, 6456, 7424, 8608, 10799, 12912, 14849, 17216, 21598, - 25824, 29698, 34432, 43196, 51650, 59398, 68864, 86392, 103310, 118795, 137746, 172847, - 206619, 237693, 275559, 345774, 413238, 475500, 543030, 678787, 814545, 950302, 1086060, - 1357575, 1629090, 1900605, 2172120, 2715151, 3258181, 3801211, 4344241, 5430302, - 6516362, 7602423, 8688483, 10860604, 13032725, 15204846, 17376967, 21721209, 26065451, - 30409693, 34753934, 43442418, 52130902, 60819386, 69507869, 69507869 - }; - - for(Bit8u i = 0; i < 62; i++) - attackRates[i] = precalculated_table[i]; - } - else if(rate == 44100) - { - static const Bit32u precalculated_table[62] = - { - 2342, 2939, 3513, 4040, 4685, 5877, 7027, 8081, 9369, 11754, 14054, 16162, 18738, 23508, - 28108, 32325, 37478, 47018, 56219, 64649, 74965, 94044, 112448, 129292, 149929, 188132, - 224945, 258713, 300002, 376263, 449999, 517550, 591053, 738816, 886579, 1034343, 1182106, - 1477633, 1773159, 2068686, 2364213, 2955266, 3546319, 4137373, 4728426, 5910533, - 7092639, 8274746, 9456853, 11821066, 14185279, 16549492, 18913706, 23642132, 28370559, - 33098985, 37827412, 47284265, 56741118, 66197971, 75654824, 75654824 - }; - - for(Bit8u i = 0; i < 62; i++) - attackRates[i] = precalculated_table[i]; - } - else if(rate == 22050) - { - static const Bit32u precalculated_table[62] = - { - 4685, 5877, 7027, 8081, 9369, 11754, 14054, 16162, 18738, 23508, 28108, 32325, 37478, - 47018, 56219, 64649, 74965, 94044, 112448, 129292, 149929, 188132, 224945, 258713, 300002, - 376263, 449999, 517550, 591053, 738816, 886579, 1034343, 1182106, 1477633, 1773159, - 2068686, 2364213, 2955266, 3546319, 4137373, 4728426, 5910533, 7092639, 8274746, - 9456853, 11821066, 14185279, 16549492, 18913706, 23642132, 28370559, 33098985, - 37827412, 47284265, 56741118, 66197971, 75654824, 94568530, 113482236, 132395942, - 151309648, 151309648 - }; - - for(Bit8u i = 0; i < 62; i++) - attackRates[i] = precalculated_table[i]; - } - //Generate the best matching attack rate - else for(Bit8u i = 0; i < 62; i++) - { - Bit8u index, shift; - EnvelopeSelect(i, index, shift); - //Original amount of samples the attack would take - Bit32s original = (Bit32u)((AttackSamplesTable[ index ] << shift) / scale); - Bit32s guessAdd = (Bit32u)(scale * (EnvelopeIncreaseTable[ index ] << (RATE_SH - shift - 3))); - Bit32s bestAdd = guessAdd; - Bit32u bestDiff = 1 << 30; - - for(Bit32u passes = 0; passes < 16; passes ++) - { - Bit32s volume = ENV_MAX; - Bit32s samples = 0; - Bit32u count = 0; - - while(volume > 0 && samples < original * 2) - { - count += guessAdd; - Bit32s change = count >> RATE_SH; - count &= RATE_MASK; - - if(GCC_UNLIKELY(change)) // less than 1 % - volume += (~volume * change) >> 3; - - samples++; - } - - Bit32s diff = original - samples; - Bit32u lDiff = labs(diff); - - //Init last on first pass - if(lDiff < bestDiff) - { - bestDiff = lDiff; - bestAdd = guessAdd; - - if(!bestDiff) - break; - } - - //Below our target - if(diff < 0) - { - //Better than the last time - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = ((guessAdd * mul) >> 12); - guessAdd++; - } - else if(diff > 0) - { - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = (guessAdd * mul) >> 12; - guessAdd--; - } - } - - attackRates[i] = bestAdd; - } - - /*fprintf(stderr, "attack rate table: "); - for ( Bit8u i = 0; i < 62; i++ ) - fprintf(stderr, ",%u", attackRates[i]); - fprintf(stderr, "\n");*/ - - for(Bit8u i = 62; i < 76; i++) - { - //This should provide instant volume maximizing - attackRates[i] = 8 << RATE_SH; - } - - //Setup the channels with the correct four op flags - //Channels are accessed through a table so they appear linear here - chan[ 0].fourMask = 0x00 | (1 << 0); - chan[ 1].fourMask = 0x80 | (1 << 0); - chan[ 2].fourMask = 0x00 | (1 << 1); - chan[ 3].fourMask = 0x80 | (1 << 1); - chan[ 4].fourMask = 0x00 | (1 << 2); - chan[ 5].fourMask = 0x80 | (1 << 2); - chan[ 9].fourMask = 0x00 | (1 << 3); - chan[10].fourMask = 0x80 | (1 << 3); - chan[11].fourMask = 0x00 | (1 << 4); - chan[12].fourMask = 0x80 | (1 << 4); - chan[13].fourMask = 0x00 | (1 << 5); - chan[14].fourMask = 0x80 | (1 << 5); - //mark the percussion channels - chan[ 6].fourMask = 0x40; - chan[ 7].fourMask = 0x40; - chan[ 8].fourMask = 0x40; - //Clear Everything in opl3 mode - WriteReg(0x105, 0x1); - - for(int i = 0; i < 512; i++) - { - if(i == 0x105) - continue; - - WriteReg(i, 0xff); - WriteReg(i, 0x0); - } - - WriteReg(0x105, 0x0); - - //Clear everything in opl2 mode - for(int i = 0; i < 255; i++) - { - WriteReg(i, 0xff); - WriteReg(i, 0x0); - } - } - - static bool doneTables = false; - void InitTables(void) - { - if(doneTables) - return; - - doneTables = true; - #if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) - - //Exponential volume table, same as the real adlib - for(int i = 0; i < 256; i++) - { - //Save them in reverse - ExpTable[i] = (int)(0.5 + (pow(2.0, (255 - i) * (1.0 / 256)) - 1) * 1024); - ExpTable[i] += 1024; //or remove the -1 oh well :) - //Preshift to the left once so the final volume can shift to the right - ExpTable[i] *= 2; - } - - #endif - #if ( DBOPL_WAVE == WAVE_HANDLER ) - - //Add 0.5 for the trunc rounding of the integer cast - //Do a PI sinetable instead of the original 0.5 PI - for(int i = 0; i < 512; i++) - SinTable[i] = (Bit16s)(0.5 - log10(sin((i + 0.5) * (PI / 512.0))) / log10(2.0) * 256); - - #endif - #if ( DBOPL_WAVE == WAVE_TABLEMUL ) - - //Multiplication based tables - for(int i = 0; i < 384; i++) - { - int s = i * 8; - //TODO maybe keep some of the precision errors of the original table? - double val = (0.5 + (pow(2.0, -1.0 + (255 - s) * (1.0 / 256))) * (1 << MUL_SH)); - MulTable[i] = (Bit16u)(val); - } - - //Sine Wave Base - for(int i = 0; i < 512; i++) - { - WaveTable[ 0x0200 + i ] = (Bit16s)(sin((i + 0.5) * (PI / 512.0)) * 4084); - WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; - } - - //Exponential wave - for(int i = 0; i < 256; i++) - { - WaveTable[ 0x700 + i ] = (Bit16s)(0.5 + (pow(2.0, -1.0 + (255 - i * 8) * (1.0 / 256))) * 4085); - WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; - } - - #endif - #if ( DBOPL_WAVE == WAVE_TABLELOG ) - - //Sine Wave Base - for(int i = 0; i < 512; i++) - { - WaveTable[ 0x0200 + i ] = (Bit16s)(0.5 - log10(sin((i + 0.5) * (PI / 512.0))) / log10(2.0) * 256); - WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; - } - - //Exponential wave - for(int i = 0; i < 256; i++) - { - WaveTable[ 0x700 + i ] = i * 8; - WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; - } - - #endif - // | |//\\|____|WAV7|//__|/\ |____|/\/\| - // |\\//| | |WAV7| | \/| | | - // |06 |0126|27 |7 |3 |4 |4 5 |5 | - #if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) - - for(int i = 0; i < 256; i++) - { - //Fill silence gaps - WaveTable[ 0x400 + i ] = WaveTable[0]; - WaveTable[ 0x500 + i ] = WaveTable[0]; - WaveTable[ 0x900 + i ] = WaveTable[0]; - WaveTable[ 0xc00 + i ] = WaveTable[0]; - WaveTable[ 0xd00 + i ] = WaveTable[0]; - //Replicate sines in other pieces - WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; - //double speed sines - WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; - WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; - } - - #endif - - //Create the ksl table - for(int oct = 0; oct < 8; oct++) - { - int base = oct * 8; - - for(int i = 0; i < 16; i++) - { - int val = base - KslCreateTable[i]; - - if(val < 0) - val = 0; - - //*4 for the final range to match attenuation range - KslTable[ oct * 16 + i ] = val * 4; - } - } - - //Create the Tremolo table, just increase and decrease a triangle wave - for(Bit8u i = 0; i < TREMOLO_TABLE / 2; i++) - { - Bit8u val = i << ENV_EXTRA; - TremoloTable[i] = val; - TremoloTable[TREMOLO_TABLE - 1 - i] = val; - } - - //Create a table with offsets of the channels from the start of the chip - DBOPL::Chip *chip = 0; - - for(Bitu i = 0; i < 32; i++) - { - Bitu index = i & 0xf; - - if(index >= 9) - { - ChanOffsetTable[i] = 0; - continue; - } - - //Make sure the four op channels follow eachother - if(index < 6) - index = (index % 3) * 2 + (index / 3); - - //Add back the bits for highest ones - if(i >= 16) - index += 9; - - Bitu blah = reinterpret_cast(&(chip->chan[ index ])); - ChanOffsetTable[i] = blah; - } - - //Same for operators - for(Bitu i = 0; i < 64; i++) - { - if(i % 8 >= 6 || ((i / 8) % 4 == 3)) - { - OpOffsetTable[i] = 0; - continue; - } - - Bitu chNum = (i / 8) * 3 + (i % 8) % 3; - - //Make sure we use 16 and up for the 2nd range to match the chanoffset gap - if(chNum >= 12) - chNum += 16 - 12; - - Bitu opNum = (i % 8) / 3; - DBOPL::Channel *chan = 0; - Bitu blah = reinterpret_cast(&(chan->op[opNum])); - OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah; - } - - #if 0 - - //Stupid checks if table's are correct - for(Bitu i = 0; i < 18; i++) - { - Bit32u find = (Bit16u)(&(chip->chan[ i ])); - - for(Bitu c = 0; c < 32; c++) - { - if(ChanOffsetTable[c] == find) - { - find = 0; - break; - } - } - - if(find) - find = find; - } - - for(Bitu i = 0; i < 36; i++) - { - Bit32u find = (Bit16u)(&(chip->chan[ i / 2 ].op[i % 2])); - - for(Bitu c = 0; c < 64; c++) - { - if(OpOffsetTable[c] == find) - { - find = 0; - break; - } - } - - if(find) - find = find; - } - - #endif - } - - Bit32u Handler::WriteAddr(Bit32u port, Bit8u val) - { - return chip.WriteAddr(port, val); - } - void Handler::WriteReg(Bit32u addr, Bit8u val) - { - chip.WriteReg(addr, val); - } - - void Handler::Generate(void(*AddSamples_m32)(Bitu, Bit32s *), - void(*AddSamples_s32)(Bitu, Bit32s *), - Bitu samples) - { - Bit32s buffer[ 512 * 2 ]; - - if(GCC_UNLIKELY(samples > 512)) - samples = 512; - - if(!chip.opl3Active) - { - chip.GenerateBlock2(samples, buffer); - AddSamples_m32(samples, buffer); - } - else - { - chip.GenerateBlock3(samples, buffer); - AddSamples_s32(samples, buffer); - } - } - - void Handler::GenerateArr(Bit32s *out, Bitu *samples) - { - if(GCC_UNLIKELY(*samples > 512)) - *samples = 512; - - if(!chip.opl3Active) - chip.GenerateBlock2(*samples, out); - else - chip.GenerateBlock3(*samples, out); - } - - void Handler::GenerateArr(Bit32s *out, ssize_t *samples) - { - if(GCC_UNLIKELY(*samples > 512)) - *samples = 512; - - if(!chip.opl3Active) - chip.GenerateBlock2(static_cast(*samples), out); - else - chip.GenerateBlock3(static_cast(*samples), out); - } - - void Handler::GenerateArr(Bit16s *out, ssize_t *samples) - { - Bit32s out32[1024]; - if(GCC_UNLIKELY(*samples > 512)) - *samples = 512; - memset(out32, 0, sizeof(Bit32s) * 1024); - if(!chip.opl3Active) - chip.GenerateBlock2(static_cast(*samples), out32); - else - chip.GenerateBlock3(static_cast(*samples), out32); - ssize_t sz = *samples * 2; - for(ssize_t i = 0; i < sz; i++) - out[i] = static_cast(DBOPL_CLAMP(out32[i], static_cast(INT16_MIN), static_cast(INT16_MAX))); - } - - void Handler::GenerateArrMix(Bit32s *out, ssize_t *samples) - { - if(GCC_UNLIKELY(*samples > 512)) - *samples = 512; - if(!chip.opl3Active) - chip.GenerateBlock2_Mix(static_cast(*samples), out); - else - chip.GenerateBlock3_Mix(static_cast(*samples), out); - } - - void Handler::GenerateArrMix(Bit16s *out, ssize_t *samples) - { - Bit32s out32[1024]; - if(GCC_UNLIKELY(*samples > 512)) - *samples = 512; - memset(out32, 0, sizeof(Bit32s) * 1024); - if(!chip.opl3Active) - chip.GenerateBlock2(static_cast(*samples), out32); - else - chip.GenerateBlock3(static_cast(*samples), out32); - ssize_t sz = *samples * 2; - for(ssize_t i = 0; i < sz; i++) - out[i] += static_cast(DBOPL_CLAMP(out32[i], static_cast(INT16_MIN), static_cast(INT16_MAX))); - } - - - void Handler::Init(Bitu rate) - { - InitTables(); - chip.Setup((Bit32u)rate); - } - - -} //Namespace DBOPL - -#endif //ADLMIDI_USE_DOSBOX_OPL diff --git a/src/sound/adlmidi/dbopl.h b/src/sound/adlmidi/dbopl.h deleted file mode 100644 index b5b2ba10d..000000000 --- a/src/sound/adlmidi/dbopl.h +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2002-2010 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -/* BEGIN MIDIPLAY GLUE */ -#include -#include -typedef unsigned long Bitu; -typedef signed long Bits; -typedef unsigned Bit32u; -typedef int Bit32s; -typedef unsigned short Bit16u; -typedef signed short Bit16s; -typedef unsigned char Bit8u; -typedef signed char Bit8s; -#define INLINE inline -#ifdef _MSC_VER -#define GCC_UNLIKELY(x) (!!(x) == 0) -#define GCC_LIKELY(x) (!!(x) == 1) -#else -#define GCC_UNLIKELY(x) __builtin_expect((x),0) -#define GCC_LIKELY(x) __builtin_expect((x),1) -#endif -/* END MIDIPLAY GLUE */ - -//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume -#define WAVE_HANDLER 10 -//Use a logarithmic wavetable with an exponential table for volume -#define WAVE_TABLELOG 11 -//Use a linear wavetable with a multiply table for volume -#define WAVE_TABLEMUL 12 - -//Select the type of wave generator routine -#define DBOPL_WAVE WAVE_TABLEMUL - -#ifdef _WIN32 -# ifdef _MSC_VER -# ifdef _WIN64 -typedef __int64 ssize_t; -# else -typedef __int32 ssize_t; -# endif -# else -# ifdef _WIN64 -typedef int64_t ssize_t; -# else -typedef int32_t ssize_t; -# endif -# endif -#endif - -namespace DBOPL -{ - - struct Chip; - struct Operator; - struct Channel; - -#if (DBOPL_WAVE == WAVE_HANDLER) - typedef Bits(DB_FASTCALL *WaveHandler)(Bitu i, Bitu volume); -#endif - - typedef Bits(DBOPL::Operator::*VolumeHandler)(); - typedef Channel *(DBOPL::Channel::*SynthHandler)(Chip *chip, Bit32u samples, Bit32s *output); - - //Different synth modes that can generate blocks of data - typedef enum - { - sm2AM, - sm2FM, - sm3AM, - sm3FM, - sm4Start, - sm3FMFM, - sm3AMFM, - sm3FMAM, - sm3AMAM, - sm6Start, - sm2Percussion, - sm3Percussion - } SynthMode; - - //Shifts for the values contained in chandata variable - enum - { - SHIFT_KSLBASE = 16, - SHIFT_KEYCODE = 24 - }; - - struct Operator - { - public: - //Masks for operator 20 values - enum - { - MASK_KSR = 0x10, - MASK_SUSTAIN = 0x20, - MASK_VIBRATO = 0x40, - MASK_TREMOLO = 0x80 - }; - - typedef enum - { - OFF, - RELEASE, - SUSTAIN, - DECAY, - ATTACK - } State; - - VolumeHandler volHandler; - -#if (DBOPL_WAVE == WAVE_HANDLER) - WaveHandler waveHandler; //Routine that generate a wave -#else - Bit16s *waveBase; - Bit32u waveMask; - Bit32u waveStart; -#endif - Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index - Bit32u waveAdd; //The base frequency without vibrato - Bit32u waveCurrent; //waveAdd + vibratao - - Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this - Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? - Bit32u vibrato; //Scaled up vibrato strength - Bit32s sustainLevel; //When stopping at sustain level stop here - Bit32s totalLevel; //totalLevel is added to every generated volume - Bit32u currentLevel; //totalLevel + tremolo - Bit32s volume; //The currently active volume - - Bit32u attackAdd; //Timers for the different states of the envelope - Bit32u decayAdd; - Bit32u releaseAdd; - Bit32u rateIndex; //Current position of the evenlope - - Bit8u rateZero; //Bits for the different states of the envelope having no changes - Bit8u keyOn; //Bitmask of different values that can generate keyon - //Registers, also used to check for changes - Bit8u reg20, reg40, reg60, reg80, regE0; - //Active part of the envelope we're in - Bit8u state; - //0xff when tremolo is enabled - Bit8u tremoloMask; - //Strength of the vibrato - Bit8u vibStrength; - //Keep track of the calculated KSR so we can check for changes - Bit8u ksr; - private: - void SetState(Bit8u s); - void UpdateAttack(const Chip *chip); - void UpdateRelease(const Chip *chip); - void UpdateDecay(const Chip *chip); - public: - void UpdateAttenuation(); - void UpdateRates(const Chip *chip); - void UpdateFrequency(); - - void Write20(const Chip *chip, Bit8u val); - void Write40(const Chip *chip, Bit8u val); - void Write60(const Chip *chip, Bit8u val); - void Write80(const Chip *chip, Bit8u val); - void WriteE0(const Chip *chip, Bit8u val); - - bool Silent() const; - void Prepare(const Chip *chip); - - void KeyOn(Bit8u mask); - void KeyOff(Bit8u mask); - - template< State state> - Bits TemplateVolume(); - - Bit32s RateForward(Bit32u add); - Bitu ForwardWave(); - Bitu ForwardVolume(); - - Bits GetSample(Bits modulation); - Bits GetWave(Bitu index, Bitu vol); - public: - Operator(); - char ____padding[5]; - }; - - struct Channel - { - Operator op[2]; - inline Operator *Op(Bitu index) - { - return &((this + (index >> 1))->op[ index & 1 ]); - } - SynthHandler synthHandler; - Bit32u chanData; //Frequency/octave and derived values - Bit32s old[2]; //Old data for feedback - - Bit8u feedback; //Feedback shift - Bit8u regB0; //Register values to check for changes - Bit8u regC0; - //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel - Bit8u fourMask; - Bit8s maskLeft; //Sign extended values for both channel's panning - Bit8s maskRight; - - //Forward the channel data to the operators of the channel - void SetChanData(const Chip *chip, Bit32u data); - //Change in the chandata, check for new values and if we have to forward to operators - void UpdateFrequency(const Chip *chip, Bit8u fourOp); - void WriteA0(const Chip *chip, Bit8u val); - void WriteB0(const Chip *chip, Bit8u val); - void WriteC0(const Chip *chip, Bit8u val); - void ResetC0(const Chip *chip); - - //call this for the first channel - template< bool opl3Mode > - void GeneratePercussion(Chip *chip, Bit32s *output); - - //Generate blocks of data in specific modes - template - Channel *BlockTemplate(Chip *chip, Bit32u samples, Bit32s *output); - Channel(); - char ____padding[6]; - }; - - struct Chip - { - //This is used as the base counter for vibrato and tremolo - Bit32u lfoCounter; - Bit32u lfoAdd; - - - Bit32u noiseCounter; - Bit32u noiseAdd; - Bit32u noiseValue; - - //Frequency scales for the different multiplications - Bit32u freqMul[16]; - //Rates for decay and release for rate of this chip - Bit32u linearRates[76]; - //Best match attack rates for the rate of this chip - Bit32u attackRates[76]; - - //18 channels with 2 operators each - Channel chan[18]; - - Bit8u reg104; - Bit8u reg08; - Bit8u reg04; - Bit8u regBD; - Bit8u vibratoIndex; - Bit8u tremoloIndex; - Bit8s vibratoSign; - Bit8u vibratoShift; - Bit8u tremoloValue; - Bit8u vibratoStrength; - Bit8u tremoloStrength; - //Mask for allowed wave forms - Bit8u waveFormMask; - //0 or -1 when enabled - Bit8s opl3Active; - - //Return the maximum amount of samples before and LFO change - Bit32u ForwardLFO(Bit32u samples); - Bit32u ForwardNoise(); - - void WriteBD(Bit8u val); - void WriteReg(Bit32u reg, Bit8u val); - - Bit32u WriteAddr(Bit32u port, Bit8u val); - - void GenerateBlock2(Bitu samples, Bit32s *output); - void GenerateBlock3(Bitu samples, Bit32s *output); - - void GenerateBlock2_Mix(Bitu samples, Bit32s *output); - void GenerateBlock3_Mix(Bitu samples, Bit32s *output); - - void Generate(Bit32u samples); - void Setup(Bit32u r); - - Chip(); - }; - - struct Handler - { - DBOPL::Chip chip; - Bit32u WriteAddr(Bit32u port, Bit8u val); - void WriteReg(Bit32u addr, Bit8u val); - void Generate(void(*AddSamples_m32)(Bitu, Bit32s *), - void(*AddSamples_s32)(Bitu, Bit32s *), - Bitu samples); - void GenerateArr(Bit32s *out, Bitu *samples); - void GenerateArr(Bit32s *out, ssize_t *samples); - void GenerateArr(Bit16s *out, ssize_t *samples); - void GenerateArrMix(Bit32s *out, ssize_t *samples); - void GenerateArrMix(Bit16s *out, ssize_t *samples); - void Init(Bitu rate); - }; - - -} //Namespace diff --git a/src/sound/adlmidi/fraction.hpp b/src/sound/adlmidi/fraction.hpp index 1ec10ab57..1c0a38dde 100644 --- a/src/sound/adlmidi/fraction.hpp +++ b/src/sound/adlmidi/fraction.hpp @@ -4,6 +4,7 @@ #include #include + /* Fraction number handling. * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) */ @@ -82,6 +83,10 @@ public: self &operator= (long double orig); }; +#ifdef _MSC_VER +#pragma warning(disable:4146) +#endif + template void fraction::Optim() { @@ -110,6 +115,10 @@ void fraction::Optim() //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); } +#ifdef _MSC_VER +#pragma warning(default:4146) +#endif + template inline const fraction abs(const fraction &f) { diff --git a/src/sound/adlmidi/wopl/wopl_file.c b/src/sound/adlmidi/wopl/wopl_file.c new file mode 100644 index 000000000..25b75be53 --- /dev/null +++ b/src/sound/adlmidi/wopl/wopl_file.c @@ -0,0 +1,584 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "wopl_file.h" +#include +#include + +static const char *wopl3_magic = "WOPL3-BANK\0"; +static const char *wopli_magic = "WOPL3-INST\0"; + +static const uint16_t wopl_latest_version = 3; + +#define WOPL_INST_SIZE_V2 62 +#define WOPL_INST_SIZE_V3 66 + +static uint16_t toUint16LE(const uint8_t *arr) +{ + uint16_t num = arr[0]; + num |= ((arr[1] << 8) & 0xFF00); + return num; +} + +static uint16_t toUint16BE(const uint8_t *arr) +{ + uint16_t num = arr[1]; + num |= ((arr[0] << 8) & 0xFF00); + return num; +} + +static int16_t toSint16BE(const uint8_t *arr) +{ + int16_t num = *(const int8_t *)(&arr[0]); + num *= 1 << 8; + num |= arr[1]; + return num; +} + +static void fromUint16LE(uint16_t in, uint8_t *arr) +{ + arr[0] = in & 0x00FF; + arr[1] = (in >> 8) & 0x00FF; +} + +static void fromUint16BE(uint16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = (in >> 8) & 0x00FF; +} + +static void fromSint16BE(int16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = ((uint16_t)in >> 8) & 0x00FF; +} + + +WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks) +{ + WOPLFile *file = NULL; + if(melodic_banks == 0) + return NULL; + if(percussive_banks == 0) + return NULL; + file = (WOPLFile*)calloc(1, sizeof(WOPLFile)); + if(!file) + return NULL; + file->banks_count_melodic = melodic_banks; + file->banks_count_percussion = percussive_banks; + file->banks_melodic = (WOPLBank*)calloc(1, sizeof(WOPLBank) * melodic_banks ); + file->banks_percussive = (WOPLBank*)calloc(1, sizeof(WOPLBank) * percussive_banks ); + return file; +} + +void WOPL_Free(WOPLFile *file) +{ + if(file) + { + if(file->banks_melodic) + free(file->banks_melodic); + if(file->banks_percussive) + free(file->banks_percussive); + free(file); + } +} + +int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2) +{ + int res = 1; + + res &= (bank1->version == bank2->version); + res &= (bank1->opl_flags == bank2->opl_flags); + res &= (bank1->volume_model == bank2->volume_model); + res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); + res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); + + if(res) + { + int i; + for(i = 0; i < bank1->banks_count_melodic; i++) + res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPLBank)) == 0); + if(res) + { + for(i = 0; i < bank1->banks_count_percussion; i++) + res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPLBank)) == 0); + } + } + + return res; +} + +static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy(ins->inst_name, (const char*)cursor, 32); + ins->inst_name[32] = '\0'; + ins->note_offset1 = toSint16BE(cursor + 32); + ins->note_offset2 = toSint16BE(cursor + 34); + ins->midi_velocity_offset = (int8_t)cursor[36]; + ins->second_voice_detune = (int8_t)cursor[37]; + ins->percussion_key_number = cursor[38]; + ins->inst_flags = cursor[39]; + ins->fb_conn1_C0 = cursor[40]; + ins->fb_conn2_C0 = cursor[41]; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + ins->operators[l].avekf_20 = cursor[off + 0]; + ins->operators[l].ksl_l_40 = cursor[off + 1]; + ins->operators[l].atdec_60 = cursor[off + 2]; + ins->operators[l].susrel_80 = cursor[off + 3]; + ins->operators[l].waveform_E0 = cursor[off + 4]; + } + if((version >= 3) && has_sounding_delays) + { + ins->delay_on_ms = toUint16BE(cursor + 62); + ins->delay_off_ms = toUint16BE(cursor + 64); + } +} + +static void WOPL_writeInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy((char*)cursor, ins->inst_name, 32); + fromSint16BE(ins->note_offset1, cursor + 32); + fromSint16BE(ins->note_offset2, cursor + 34); + cursor[36] = (uint8_t)ins->midi_velocity_offset; + cursor[37] = (uint8_t)ins->second_voice_detune; + cursor[38] = ins->percussion_key_number; + cursor[39] = ins->inst_flags; + cursor[40] = ins->fb_conn1_C0; + cursor[41] = ins->fb_conn2_C0; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + cursor[off + 0] = ins->operators[l].avekf_20; + cursor[off + 1] = ins->operators[l].ksl_l_40; + cursor[off + 2] = ins->operators[l].atdec_60; + cursor[off + 3] = ins->operators[l].susrel_80; + cursor[off + 4] = ins->operators[l].waveform_E0; + } + if((version >= 3) && has_sounding_delays) + { + fromUint16BE(ins->delay_on_ms, cursor + 62); + fromUint16BE(ins->delay_off_ms, cursor + 64); + } +} + +WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error) +{ + WOPLFile *outFile = NULL; + uint16_t i = 0, j = 0, k = 0; + uint16_t version = 0; + uint16_t count_melodic_banks = 1; + uint16_t count_percusive_banks = 1; + uint8_t *cursor = (uint8_t *)mem; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + +#define SET_ERROR(err) \ +{\ + WOPL_Free(outFile);\ + if(error)\ + {\ + *error = err;\ + }\ +} + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(!cursor) + { + SET_ERROR(WOPL_ERR_NULL_POINTER); + return NULL; + } + + {/* Magic number */ + if(length < 11) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + if(memcmp(cursor, wopl3_magic, 11) != 0) + { + SET_ERROR(WOPL_ERR_BAD_MAGIC); + return NULL; + } + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + version = toUint16LE(cursor); + if(version > wopl_latest_version) + { + SET_ERROR(WOPL_ERR_NEWER_VERSION); + return NULL; + } + GO_FORWARD(2); + } + + {/* Header of WOPL */ + uint8_t head[6]; + if(length < 6) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + memcpy(head, cursor, 6); + count_melodic_banks = toUint16BE(head); + count_percusive_banks = toUint16BE(head + 2); + GO_FORWARD(6); + + outFile = WOPL_Init(count_melodic_banks, count_percusive_banks); + if(!outFile) + { + SET_ERROR(WOPL_ERR_OUT_OF_MEMORY); + return NULL; + } + + outFile->version = version; + outFile->opl_flags = head[4]; + outFile->volume_model = head[5]; + } + + bankslots_sizes[0] = count_melodic_banks; + bankslots[0] = outFile->banks_melodic; + bankslots_sizes[1] = count_percusive_banks; + bankslots[1] = outFile->banks_percussive; + + if(version >= 2) /* Bank names and LSB/MSB titles */ + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); + bankslots[i][j].bank_name[32] = '\0'; + bankslots[i][j].bank_midi_lsb = cursor[32]; + bankslots[i][j].bank_midi_msb = cursor[33]; + GO_FORWARD(34); + } + } + } + + {/* Read instruments data */ + uint16_t insSize = 0; + if(version > 2) + insSize = WOPL_INST_SIZE_V3; + else + insSize = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_parseInstrument(ins, cursor, version, 1); + GO_FORWARD(insSize); + } + } + } + } + +#undef GO_FORWARD +#undef SET_ERROR + + return outFile; +} + +int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length) +{ + uint16_t version = 0; + uint8_t *cursor = (uint8_t *)mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + if(memcmp(cursor, wopli_magic, 11) != 0) + return WOPL_ERR_BAD_MAGIC; + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + version = toUint16LE(cursor); + if(version > wopl_latest_version) + return WOPL_ERR_NEWER_VERSION; + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + file->is_drum = *cursor; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_parseInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 2 + 2 + 1 + 1; + /* + * Magic number, + * Version, + * Count of melodic banks, + * Count of percussive banks, + * Chip specific flags + * Volume Model + */ + + if(version >= 2) + { + /* Melodic banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_melodic; + /* Percussive banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_percussion; + } + + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + /* Melodic instruments */ + final_size += (ins_size * 128) * file->banks_count_melodic; + /* Percusive instruments */ + final_size += (ins_size * 128) * file->banks_count_percussion; + + return final_size; +} + +size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 1; + /* + * Magic number, + * version, + * is percussive instrument + */ + + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + final_size += ins_size * 128; + + return final_size; +} + +int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size = 0; + uint16_t i, j, k; + uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; + uint16_t banks_percusive = force_gm ? 1 : file->banks_count_percussion; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopl3_magic, 11); + GO_FORWARD(11); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_melodic, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_percusive, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + cursor[0] = file->opl_flags; + cursor[1] = file->volume_model; + GO_FORWARD(2); + + bankslots[0] = file->banks_melodic; + bankslots_sizes[0] = banks_melodic; + bankslots[1] = file->banks_percussive; + bankslots_sizes[1] = banks_percusive; + + if(version >= 2) + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + return WOPL_ERR_UNEXPECTED_ENDING; + strncpy((char*)cursor, bankslots[i][j].bank_name, 32); + cursor[32] = bankslots[i][j].bank_midi_lsb; + cursor[33] = bankslots[i][j].bank_midi_msb; + GO_FORWARD(34); + } + } + } + + {/* Write instruments data */ + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) + return WOPL_ERR_UNEXPECTED_ENDING; + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_writeInstrument(ins, cursor, version, 1); + GO_FORWARD(ins_size); + } + } + } + } + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopli_magic, 11); + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + *cursor = file->is_drum; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_writeInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} diff --git a/src/sound/adlmidi/wopl/wopl_file.h b/src/sound/adlmidi/wopl/wopl_file.h new file mode 100644 index 000000000..0e3884243 --- /dev/null +++ b/src/sound/adlmidi/wopl/wopl_file.h @@ -0,0 +1,290 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef WOPL_FILE_H +#define WOPL_FILE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \ + || defined(__STRICT_ANSI__) || !defined(__cplusplus) +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int int16_t; +typedef unsigned short int uint16_t; +#endif + +/* Global OPL flags */ +typedef enum WOPLFileFlags +{ + /* Enable Deep-Tremolo flag */ + WOPL_FLAG_DEEP_TREMOLO = 0x01, + /* Enable Deep-Vibrato flag */ + WOPL_FLAG_DEEP_VIBRATO = 0x02 +} WOPLFileFlags; + +/* Volume scaling model implemented in the libADLMIDI */ +typedef enum WOPL_VolumeModel +{ + WOPL_VM_Generic = 0, + WOPL_VM_Native, + WOPL_VM_DMX, + WOPL_VM_Apogee, + WOPL_VM_Win9x +} WOPL_VolumeModel; + +typedef enum WOPL_InstrumentFlags +{ + /* Is two-operator single-voice instrument (no flags) */ + WOPL_Ins_2op = 0x00, + /* Is true four-operator instrument */ + WOPL_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + WOPL_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + WOPL_Ins_IsBlank = 0x04, + + /* RythmMode flags mask */ + WOPL_RythmModeMask = 0x38, + + /* Mask of the flags range */ + WOPL_Ins_ALL_MASK = 0x07 +} WOPL_InstrumentFlags; + +typedef enum WOPL_RythmMode +{ + /* RythmMode: BassDrum */ + WOPL_RM_BassDrum = 0x08, + /* RythmMode: Snare */ + WOPL_RM_Snare = 0x10, + /* RythmMode: TomTom */ + WOPL_RM_TomTom = 0x18, + /* RythmMode: Cymbell */ + WOPL_RM_Cymball = 0x20, + /* RythmMode: HiHat */ + WOPL_RM_HiHat = 0x28 +} WOPL_RythmMode; + +/* Error codes */ +typedef enum WOPL_ErrorCodes +{ + WOPL_ERR_OK = 0, + /* Magic number is not maching */ + WOPL_ERR_BAD_MAGIC, + /* Too short file */ + WOPL_ERR_UNEXPECTED_ENDING, + /* Zero banks count */ + WOPL_ERR_INVALID_BANKS_COUNT, + /* Version of file is newer than supported by current version of library */ + WOPL_ERR_NEWER_VERSION, + /* Out of memory */ + WOPL_ERR_OUT_OF_MEMORY, + /* Given null pointer memory data */ + WOPL_ERR_NULL_POINTER +} WOPL_ErrorCodes; + +/* Operator indeces inside of Instrument Entry */ +#define WOPL_OP_CARRIER1 0 +#define WOPL_OP_MODULATOR1 1 +#define WOPL_OP_CARRIER2 2 +#define WOPL_OP_MODULATOR2 3 + +/* OPL3 Oerators data */ +typedef struct WOPLOperator +{ + /* AM/Vib/Env/Ksr/FMult characteristics */ + uint8_t avekf_20; + /* Key Scale Level / Total level register data */ + uint8_t ksl_l_40; + /* Attack / Decay */ + uint8_t atdec_60; + /* Systain and Release register data */ + uint8_t susrel_80; + /* Wave form */ + uint8_t waveform_E0; +} WOPLOperator; + +/* Instrument entry */ +typedef struct WOPLInstrument +{ + /* Title of the instrument */ + char inst_name[34]; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + int16_t note_offset1; + /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + int16_t note_offset2; + /* MIDI note velocity offset (taken from Apogee TMB format) */ + int8_t midi_velocity_offset; + /* Second voice detune level (taken from DMX OP2) */ + int8_t second_voice_detune; + /* Percussion MIDI base tone number at which this drum will be played */ + uint8_t percussion_key_number; + /* Enum WOPL_InstrumentFlags */ + uint8_t inst_flags; + /* Feedback&Connection register for first and second operators */ + uint8_t fb_conn1_C0; + /* Feedback&Connection register for third and fourth operators */ + uint8_t fb_conn2_C0; + /* Operators register data */ + WOPLOperator operators[4]; + /* Millisecond delay of sounding while key is on */ + uint16_t delay_on_ms; + /* Millisecond delay of sounding after key off */ + uint16_t delay_off_ms; +} WOPLInstrument; + +/* Bank entry */ +typedef struct WOPLBank +{ + /* Name of bank */ + char bank_name[33]; + /* MIDI Bank LSB code */ + uint8_t bank_midi_lsb; + /* MIDI Bank MSB code */ + uint8_t bank_midi_msb; + /* Instruments data of this bank */ + WOPLInstrument ins[128]; +} WOPLBank; + +/* Instrument data file */ +typedef struct WOPIFile +{ + /* Version of instrument file */ + uint16_t version; + /* Is this a percussion instrument */ + uint8_t is_drum; + /* Instrument data */ + WOPLInstrument inst; +} WOPIFile; + +/* Bank data file */ +typedef struct WOPLFile +{ + /* Version of bank file */ + uint16_t version; + /* Count of melodic banks in this file */ + uint16_t banks_count_melodic; + /* Count of percussion banks in this file */ + uint16_t banks_count_percussion; + /* Enum WOPLFileFlags */ + uint8_t opl_flags; + /* Enum WOPL_VolumeModel */ + uint8_t volume_model; + /* dynamically allocated data Melodic banks array */ + WOPLBank *banks_melodic; + /* dynamically allocated data Percussive banks array */ + WOPLBank *banks_percussive; +} WOPLFile; + + +/** + * @brief Initialize blank WOPL data structure with allocated bank data + * @param melodic_banks Count of melodic banks + * @param percussive_banks Count of percussive banks + * @return pointer to heap-allocated WOPL data structure or NULL when out of memory or incorrectly given banks counts + */ +extern WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks); + +/** + * @brief Clean up WOPL data file (all allocated bank arrays will be fried too) + * @param file pointer to heap-allocated WOPL data structure + */ +extern void WOPL_Free(WOPLFile *file); + +/** + * @brief Compare two bank entries + * @param bank1 First bank + * @param bank2 Second bank + * @return 1 if banks are equal or 0 if there are different + */ +extern int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2); + + +/** + * @brief Load WOPL bank file from the memory. + * WOPL data structure will be allocated. (don't forget to clear it with WOPL_Free() after use!) + * @param mem Pointer to memory block contains raw WOPL bank file data + * @param length Length of given memory block + * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. + * @return Heap-allocated WOPL file data structure or NULL if any error has occouped + */ +extern WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error); + +/** + * @brief Load WOPI instrument file from the memory. + * You must allocate WOPIFile structure by yourself and give the pointer to it. + * @param file Pointer to destinition WOPIFile structure to fill it with parsed data. + * @param mem Pointer to memory block contains raw WOPI instrument file data + * @param length Length of given memory block + * @return 0 if no errors occouped, or an error code of WOPL_ErrorCodes enumeration + */ +extern int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length); + +/** + * @brief Calculate the size of the output memory block + * @param file Heap-allocated WOPL file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPL file data + */ +extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version); + +/** + * @brief Calculate the size of the output memory block + * @param file Pointer to WOPI file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPI file data + */ +extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version); + +/** + * @brief Write raw WOPL into given memory block + * @param file Heap-allocated WOPL file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPL version + * @param force_gm Force GM set in saved bank file + * @return Error code or 0 on success + */ +extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); + +/** + * @brief Write raw WOPI into given memory block + * @param file Pointer to WOPI file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPI version + * @return Error code or 0 on success + */ +extern int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version); + +#ifdef __cplusplus +} +#endif + +#endif /* WOPL_FILE_H */ diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 525ade7ce..cccc12241 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -319,7 +319,6 @@ protected: class ADLMIDIDevice : public SoftSynthMIDIDevice { struct ADL_MIDIPlayer *Renderer; - TArray shortbuffer; public: ADLMIDIDevice(const char *args); ~ADLMIDIDevice(); @@ -338,7 +337,6 @@ protected: class OPNMIDIDevice : public SoftSynthMIDIDevice { struct OPN2_MIDIPlayer *Renderer; - TArray shortbuffer; public: OPNMIDIDevice(const char *args); ~OPNMIDIDevice(); diff --git a/src/sound/mididevices/music_adlmidi_mididevice.cpp b/src/sound/mididevices/music_adlmidi_mididevice.cpp index 9025c4745..7cece2ec6 100644 --- a/src/sound/mididevices/music_adlmidi_mididevice.cpp +++ b/src/sound/mididevices/music_adlmidi_mididevice.cpp @@ -56,6 +56,22 @@ CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Int, adl_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, adl_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) + { + MIDIDeviceChanged(-1, true); + } +} + CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) @@ -64,6 +80,22 @@ CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Bool, adl_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (adl_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) + { + MIDIDeviceChanged(-1, true); + } +} + CUSTOM_CVAR(Int, adl_volume_model, ADLMIDI_VolumeModel_DMX, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) @@ -84,7 +116,18 @@ ADLMIDIDevice::ADLMIDIDevice(const char *args) Renderer = adl_init(44100); // todo: make it configurable if (Renderer != nullptr) { - adl_setBank(Renderer, (int)adl_bank); + adl_switchEmulator(Renderer, (int)adl_emulator_id); + adl_setRunAtPcmRate(Renderer, (int)adl_run_at_pcm_rate); +// todo: Implement handling of external or in-resources WOPL bank files and load +/* + if(adl_use_custom_bank) + { + adl_openBankFile(Renderer, (char*)adl_bank_file); + adl_openBankData(Renderer, (char*)adl_bank, (unsigned long)size); + } + else +*/ + adl_setBank(Renderer, (int)adl_bank); adl_setNumChips(Renderer, (int)adl_chips_count); adl_setVolumeRangeModel(Renderer, (int)adl_volume_model); } @@ -176,6 +219,13 @@ void ADLMIDIDevice::HandleLongEvent(const uint8_t *data, int len) { } +static const ADLMIDI_AudioFormat audio_output_format = +{ + ADLMIDI_SampleType_F32, + sizeof(float), + 2 * sizeof(float) +}; + //========================================================================== // // ADLMIDIDevice :: ComputeOutput @@ -184,11 +234,12 @@ void ADLMIDIDevice::HandleLongEvent(const uint8_t *data, int len) void ADLMIDIDevice::ComputeOutput(float *buffer, int len) { - if ((int)shortbuffer.Size() < len*2) shortbuffer.Resize(len*2); - auto result = adl_generate(Renderer, len*2, &shortbuffer[0]); - for(int i=0; i(buffer); + ADL_UInt8* right = reinterpret_cast(buffer + 1); + auto result = adl_generateFormat(Renderer, len * 2, left, right, &audio_output_format); + for(int i=0; i < result; i++) { - buffer[i] = shortbuffer[i] * (3.5f/32768.f); + buffer[i] *= 3.5f; } } diff --git a/src/sound/mididevices/music_opnmidi_mididevice.cpp b/src/sound/mididevices/music_opnmidi_mididevice.cpp index 8c61ab9e7..de27644c3 100644 --- a/src/sound/mididevices/music_opnmidi_mididevice.cpp +++ b/src/sound/mididevices/music_opnmidi_mididevice.cpp @@ -58,6 +58,38 @@ CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + +CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (opn_use_custom_bank && currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + //========================================================================== // // OPNMIDIDevice Constructor @@ -70,6 +102,17 @@ OPNMIDIDevice::OPNMIDIDevice(const char *args) Renderer = opn2_init(44100); // todo: make it configurable if (Renderer != nullptr) { + opn2_switchEmulator(Renderer, (int)opn_emulator_id); + opn2_setRunAtPcmRate(Renderer, (int)opn_run_at_pcm_rate); +// todo: Implement handling of external or in-resources WOPN bank files and load +/* + if(opn_use_custom_bank) + { + opn2_openBankFile(Renderer, (char*)opn_bank_file); + opn2_openBankData(Renderer, (char*)opn_bank, (long)size); + } + else + */ int lump = Wads.CheckNumForFullName("xg.wopn"); if (lump < 0) { @@ -167,6 +210,13 @@ void OPNMIDIDevice::HandleLongEvent(const uint8_t *data, int len) { } +static const OPNMIDI_AudioFormat audio_output_format = +{ + OPNMIDI_SampleType_F32, + sizeof(float), + 2 * sizeof(float) +}; + //========================================================================== // // OPNMIDIDevice :: ComputeOutput @@ -175,11 +225,7 @@ void OPNMIDIDevice::HandleLongEvent(const uint8_t *data, int len) void OPNMIDIDevice::ComputeOutput(float *buffer, int len) { - if ((int)shortbuffer.Size() < len*2) shortbuffer.Resize(len*2); - auto result = opn2_generate(Renderer, len*2, &shortbuffer[0]); - for(int i=0; i(buffer); + OPN2_UInt8* right = reinterpret_cast(buffer + 1); + opn2_generateFormat(Renderer, len * 2, left, right, &audio_output_format); } - diff --git a/src/sound/opnmidi/Ym2612_ChipEmu.cpp b/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp similarity index 95% rename from src/sound/opnmidi/Ym2612_ChipEmu.cpp rename to src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp index 66219ebdc..18285bf84 100644 --- a/src/sound/opnmidi/Ym2612_ChipEmu.cpp +++ b/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp @@ -2,7 +2,7 @@ // Based on Gens 2.10 ym2612.c -#include "Ym2612_ChipEmu.h" +#include "Ym2612_Emu.h" #include #include @@ -110,7 +110,7 @@ struct state_t int TimerBcnt; // timerB counter = valeur courante du Timer B int Mode; // Mode actuel des voie 3 et 6 (normal / special) int DAC; // DAC enabled flag - channel_t CHANNEL[OPNMIDI_Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 + channel_t CHANNEL[Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif // cela nous rend le debuggage plus facile }; @@ -255,9 +255,9 @@ static const unsigned char LFO_FMS_TAB [8] = inline void YM2612_Special_Update() { } -struct OPNMIDI_Ym2612_Impl +struct Ym2612_Impl { - enum { channel_count = OPNMIDI_Ym2612_Emu::channel_count }; + enum { channel_count = Ym2612_Emu::channel_count }; state_t YM2612; int mute_mask; @@ -274,10 +274,10 @@ struct OPNMIDI_Ym2612_Impl void write0( int addr, int data ); void write1( int addr, int data ); void run_timer( int ); - void run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* ); + void run( int pair_count, Ym2612_Emu::sample_t* ); }; -void OPNMIDI_Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) +void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) { slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot @@ -300,7 +300,7 @@ void OPNMIDI_Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) } -void OPNMIDI_Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) +void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) { slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot @@ -318,7 +318,7 @@ void OPNMIDI_Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) } -int OPNMIDI_Ym2612_Impl::SLOT_SET( int Adr, int data ) +int Ym2612_Impl::SLOT_SET( int Adr, int data ) { int nch = Adr & 3; if ( nch == 3 ) @@ -420,7 +420,7 @@ int OPNMIDI_Ym2612_Impl::SLOT_SET( int Adr, int data ) } -int OPNMIDI_Ym2612_Impl::CHANNEL_SET( int Adr, int data ) +int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) { int num = Adr & 3; if ( num == 3 ) @@ -522,7 +522,7 @@ int OPNMIDI_Ym2612_Impl::CHANNEL_SET( int Adr, int data ) } -int OPNMIDI_Ym2612_Impl::YM_SET(int Adr, int data) +int Ym2612_Impl::YM_SET(int Adr, int data) { switch ( Adr ) { @@ -628,7 +628,7 @@ int OPNMIDI_Ym2612_Impl::YM_SET(int Adr, int data) return 0; } -void OPNMIDI_Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) +void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) { assert( sample_rate ); assert( clock_rate > sample_rate ); @@ -824,11 +824,11 @@ void OPNMIDI_Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) reset(); } -const char* OPNMIDI_Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) +const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) { if ( !impl ) { - impl = (OPNMIDI_Ym2612_Impl*) malloc( sizeof *impl ); + impl = (Ym2612_Impl*) malloc( sizeof *impl ); if ( !impl ) return "Out of memory"; impl->mute_mask = 0; @@ -840,12 +840,12 @@ const char* OPNMIDI_Ym2612_Emu::set_rate( double sample_rate, double clock_rate return 0; } -OPNMIDI_Ym2612_Emu::~OPNMIDI_Ym2612_Emu() +Ym2612_Emu::~Ym2612_Emu() { free( impl ); } -inline void OPNMIDI_Ym2612_Impl::write0( int opn_addr, int data ) +inline void Ym2612_Impl::write0( int opn_addr, int data ) { assert( (unsigned) data <= 0xFF ); @@ -865,7 +865,7 @@ inline void OPNMIDI_Ym2612_Impl::write0( int opn_addr, int data ) } } -inline void OPNMIDI_Ym2612_Impl::write1( int opn_addr, int data ) +inline void Ym2612_Impl::write1( int opn_addr, int data ) { assert( (unsigned) data <= 0xFF ); @@ -880,12 +880,12 @@ inline void OPNMIDI_Ym2612_Impl::write1( int opn_addr, int data ) } } -void OPNMIDI_Ym2612_Emu::reset() +void Ym2612_Emu::reset() { impl->reset(); } -void OPNMIDI_Ym2612_Impl::reset() +void Ym2612_Impl::reset() { g.LFOcnt = 0; YM2612.TimerA = 0; @@ -949,17 +949,17 @@ void OPNMIDI_Ym2612_Impl::reset() write0( 0x2A, 0x80 ); } -void OPNMIDI_Ym2612_Emu::write0( int addr, int data ) +void Ym2612_Emu::write0( int addr, int data ) { impl->write0( addr, data ); } -void OPNMIDI_Ym2612_Emu::write1( int addr, int data ) +void Ym2612_Emu::write1( int addr, int data ) { impl->write1( addr, data ); } -void OPNMIDI_Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } +void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } static void update_envelope_( slot_t* sl ) { @@ -1033,14 +1033,14 @@ inline void update_envelope( slot_t& sl ) template struct ym2612_update_chan { - static void func( tables_t&, channel_t&, OPNMIDI_Ym2612_Emu::sample_t*, int ); + static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); }; -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, OPNMIDI_Ym2612_Emu::sample_t*, int ); +typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); template void ym2612_update_chan::func( tables_t& g, channel_t& ch, - OPNMIDI_Ym2612_Emu::sample_t* buf, int length ) + Ym2612_Emu::sample_t* buf, int length ) { int not_end = ch.SLOT [S3].Ecnt - ENV_END; @@ -1201,7 +1201,7 @@ static const ym2612_update_chan_t UPDATE_CHAN [8] = { &ym2612_update_chan<7>::func }; -void OPNMIDI_Ym2612_Impl::run_timer( int length ) +void Ym2612_Impl::run_timer( int length ) { int const step = 6; int remain = length; @@ -1247,7 +1247,7 @@ void OPNMIDI_Ym2612_Impl::run_timer( int length ) while ( remain > 0 ); } -void OPNMIDI_Ym2612_Impl::run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* out ) +void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out ) { if ( pair_count <= 0 ) return; @@ -1316,4 +1316,4 @@ void OPNMIDI_Ym2612_Impl::run( int pair_count, OPNMIDI_Ym2612_Emu::sample_t* out g.LFOcnt += g.LFOinc * pair_count; } -void OPNMIDI_Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } +void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff --git a/src/sound/opnmidi/Ym2612_ChipEmu.h b/src/sound/opnmidi/chips/gens/Ym2612_Emu.h similarity index 82% rename from src/sound/opnmidi/Ym2612_ChipEmu.h rename to src/sound/opnmidi/chips/gens/Ym2612_Emu.h index 2628954d5..efce1e2d1 100644 --- a/src/sound/opnmidi/Ym2612_ChipEmu.h +++ b/src/sound/opnmidi/chips/gens/Ym2612_Emu.h @@ -4,31 +4,31 @@ #ifndef YM2612_EMU_H #define YM2612_EMU_H -struct OPNMIDI_Ym2612_Impl; +struct Ym2612_Impl; -class OPNMIDI_Ym2612_Emu { - OPNMIDI_Ym2612_Impl* impl; +class Ym2612_Emu { + Ym2612_Impl* impl; public: - OPNMIDI_Ym2612_Emu() { impl = 0; } - ~OPNMIDI_Ym2612_Emu(); - + Ym2612_Emu() { impl = 0; } + ~Ym2612_Emu(); + // Set output sample rate and chip clock rates, in Hz. Returns non-zero // if error. const char* set_rate( double sample_rate, double clock_rate ); - + // Reset to power-up state void reset(); - + // Mute voice n if bit n (1 << n) of mask is set enum { channel_count = 6 }; void mute_voices( int mask ); - + // Write addr to register 0 then data to register 1 void write0( int addr, int data ); - + // Write addr to register 2 then data to register 3 void write1( int addr, int data ); - + // Run and add pair_count samples into current output buffer contents typedef short sample_t; enum { out_chan_count = 2 }; // stereo diff --git a/src/sound/opnmidi/chips/gens_opn2.cpp b/src/sound/opnmidi/chips/gens_opn2.cpp new file mode 100644 index 000000000..3e8977c77 --- /dev/null +++ b/src/sound/opnmidi/chips/gens_opn2.cpp @@ -0,0 +1,52 @@ +#include "gens_opn2.h" +#include + +#include "gens/Ym2612_Emu.h" + +GensOPN2::GensOPN2() + : chip(new Ym2612_Emu()) +{ + setRate(m_rate, m_clock); +} + +GensOPN2::~GensOPN2() +{ + delete chip; +} + +void GensOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseBufferedT::setRate(rate, clock); + uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast(nativeRate); + chip->set_rate(chipRate, clock); // implies reset() +} + +void GensOPN2::reset() +{ + OPNChipBaseBufferedT::reset(); + chip->reset(); +} + +void GensOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + switch (port) + { + case 0: + chip->write0(addr, data); + break; + case 1: + chip->write1(addr, data); + break; + } +} + +void GensOPN2::nativeGenerateN(int16_t *output, size_t frames) +{ + std::memset(output, 0, frames * sizeof(int16_t) * 2); + chip->run((int)frames, output); +} + +const char *GensOPN2::emulatorName() +{ + return "GENS 2.10 OPN2"; +} diff --git a/src/sound/opnmidi/chips/gens_opn2.h b/src/sound/opnmidi/chips/gens_opn2.h new file mode 100644 index 000000000..158ffc69b --- /dev/null +++ b/src/sound/opnmidi/chips/gens_opn2.h @@ -0,0 +1,24 @@ +#ifndef GENS_OPN2_H +#define GENS_OPN2_H + +#include "opn_chip_base.h" + +class Ym2612_Emu; +class GensOPN2 final : public OPNChipBaseBufferedT +{ + Ym2612_Emu *chip; +public: + GensOPN2(); + ~GensOPN2() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif // GENS_OPN2_H diff --git a/src/sound/opnmidi/chips/mame/mame_ym2612fm.c b/src/sound/opnmidi/chips/mame/mame_ym2612fm.c new file mode 100644 index 000000000..0628a878a --- /dev/null +++ b/src/sound/opnmidi/chips/mame/mame_ym2612fm.c @@ -0,0 +1,2847 @@ +/* +** +** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator +** Split from fm.c to keep 2612 fixes from infecting other OPN chips +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368) +** +*/ + +/* +** History: +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware +** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +** TODO: +** +** - core documentation +** - BUSY flag support +** +** CHANGELOG: +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** xx-xx-xxxx +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** +** 06-23-2007 Zsolt Vasvari: +** - changed the timing not to require the use of floating point calculations +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + + + + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include +#include /* for memset */ +#include /* for NULL */ +#include +#include "mamedef.h" +#include "mame_ym2612fm.h" + +static stream_sample_t *DUMMYBUF = NULL; + +/* shared function building option */ +#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) +#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) + +#define RSM_ENABLE 0 +#define RSM_FRAC 10 + +/* globals */ +#define TYPE_SSG 0x01 /* SSG support */ +#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ +#define TYPE_6CH 0x04 /* FM 6CH / 3CH */ +#define TYPE_DAC 0x08 /* YM2612's DAC device */ +#define TYPE_ADPCM 0x10 /* two ADPCM units */ +#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ + + +#define TYPE_YM2203 (TYPE_SSG) +#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) +#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) +#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH) + + +/* globals */ +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +*/ +O(18),O(18),O( 2),O( 3), /* from Nemesis's tests on real YM2612 hardware */ +O( 0),O( 1),O( 2),O( 2), /* Nemesis's tests */ + +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is losing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* bit0 = Right enable , bit1 = Left enable */ +#define OUTD_RIGHT 1 +#define OUTD_LEFT 2 +#define OUTD_CENTER 3 + + +/* save output as raw 16-bit sample */ +/* #define SAVE_SAMPLE */ + +#ifdef SAVE_SAMPLE +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect;/* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk:adjusted to sample rate */ + UINT8 kcode; /* key code: */ + UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 Muted; +} FM_CH; + + +typedef struct +{ + /* running_device *device; */ + void * param; /* this chip parameter */ + double freqbase; /* frequency base */ + int timer_prescaler; /* timer prescaler */ + UINT8 irq; /* interrupt level */ + UINT8 irqmask; /* irq mask */ +#if FM_BUSY_FLAG_SUPPORT + TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ +#endif + UINT32 clock; /* master clock (Hz) */ + UINT32 rate; /* internal sampling rate (Hz) */ +#if RSM_ENABLE + INT32 rateratio; /* resampling ratio */ + INT32 framecnt; /* resampling frames count*/ + FMSAMPLE cur_sample[2]; /* previous sample */ + FMSAMPLE prev_sample[2]; /* previous sample */ +#endif + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + UINT8 prescaler_sel; /* prescaler selector */ + INT32 TA; /* timer a */ + INT32 TAC; /* timer a counter */ + UINT8 TB; /* timer b */ + INT32 TBC; /* timer b counter */ + /* local time tables */ + INT32 dt_tab[8][32]; /* DeTune table */ + /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; +} FM_ST; + + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + UINT8 type; /* chip type */ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + FM_CH *P_CH; /* pointer of CH */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ + + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + UINT32 fn_table[4096]; /* fnumber->increment counter */ + UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_add; /* step of lfo_timer */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + +} FM_OPN; + +/* here's the virtual YM2612 */ +typedef struct +{ + UINT8 REGS[512]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[6]; /* channel state */ + UINT8 addr_A1; /* address line A1 */ + + /* dac output (YM2612) */ + /* int dacen; */ + UINT8 dacen; + UINT8 dac_test; + INT32 dacout; + UINT8 MuteDAC; + + UINT8 WaveOutMode; + INT32 WaveL; + INT32 WaveR; +} YM2612; + +/* log output level */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WAR 2 /* WARNING */ +#define LOG_INF 1 /* INFORMATION */ +#define LOG_LEVEL LOG_INF + +#ifndef __RAINE__ +#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) +#endif + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +#if 0 +#define USE_VGM_INIT_SWITCH +static UINT8 IsVGMInit = 0; +#endif +static UINT8 PseudoSt = 0x00; +/*#include +static FILE* hFile; +static UINT32 FileSample;*/ + +/* status set and IRQ handling */ +INLINE void FM_STATUS_SET(FM_ST *ST,int flag) +{ + /* set status flag */ + ST->status |= flag; + if ( !(ST->irq) && (ST->status & ST->irqmask) ) + { + ST->irq = 1; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); + } +} + +/* status reset and IRQ handling */ +INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) +{ + /* reset status flag */ + ST->status &=~flag; + if ( (ST->irq) && !(ST->status & ST->irqmask) ) + { + ST->irq = 0; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); + } +} + +/* IRQ mask set */ +INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) +{ + ST->irqmask = flag; + /* IRQ handling check */ + FM_STATUS_SET(ST,0); + FM_STATUS_RESET(ST,0); +} + +INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + /* Note by Valley Bell: + I assume that the CSM mode shouldn't affect channels + other than FM3, so I added a check for it here.*/ + if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) /* workaround for VGMs trimmed with VGMTool */ + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !OPN->SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((OPN->ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + OPN->P_CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4); + OPN->SL3.key_csm = 0; + } + } + + /* reset Timer b flag */ + if( v & 0x20 ) + FM_STATUS_RESET(ST,0x02); + /* reset Timer a flag */ + if( v & 0x10 ) + FM_STATUS_RESET(ST,0x01); + /* load b */ + if ((v&2) && !(ST->mode&2)) + { + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); + } + /* load a */ + if ((v&1) && !(ST->mode&1)) + { + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; + } + + ST->mode = (UINT32)v; +} + + +/* Timer A Overflow */ +INLINE void TimerAOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); + /* clear or reload the counter */ + ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; +} +/* Timer B Overflow */ +INLINE void TimerBOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); + /* clear or reload the counter */ + ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); +} + + +#if FM_INTERNAL_TIMER +/* ----- internal timer mode , update timer */ +/* Valley Bell: defines fixed */ + +/* ---------- calculate timer A ---------- */ + #define INTERNAL_TIMER_A(ST,CSM_CH) \ + { \ + if( (ST)->TAC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ + { \ + TimerAOver( ST ); \ + /* CSM mode total level latch and auto key on */ \ + if( (ST)->mode & 0x80 ) \ + CSMKeyControll( OPN, CSM_CH ); \ + } \ + } +/* ---------- calculate timer B ---------- */ + #define INTERNAL_TIMER_B(ST,step) \ + { \ + if( (ST)->TBC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ + TimerBOver( ST ); \ + } +#else /* FM_INTERNAL_TIMER */ +/* external timer mode */ +#define INTERNAL_TIMER_A(ST,CSM_CH) +#define INTERNAL_TIMER_B(ST,step) +#endif /* FM_INTERNAL_TIMER */ + + + +#if FM_BUSY_FLAG_SUPPORT +#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) +INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) +{ + if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) + { + if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) + return ST->status | 0x80; /* with busy */ + /* expire */ + FM_BUSY_CLEAR(ST); + } + return ST->status; +} +INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) +{ + TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); + ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); +} +#else +#define FM_STATUS_FLAG(ST) ((ST)->status) +#define FM_BUSY_SET(ST,bclock) {} +#define FM_BUSY_CLEAR(ST) {} +#endif + + +/* set algorithm connection */ +INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) +{ + INT32 *carrier = &OPN->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &OPN->mem; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &OPN->c2; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = &OPN->c2; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ST->dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + (void)CH; + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + (void)type; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ + /* and could result in SLOT->kc remaining unchanged. */ + /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ + /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */ + } +} + +/* set decay rate */ +INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; +} + +/* set sustain rate */ +INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPN *OPN) +{ + if (OPN->lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer */ + OPN->lfo_timer += OPN->lfo_timer_add; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + while (OPN->lfo_timer >= OPN->lfo_timer_overflow) + { + OPN->lfo_timer -= OPN->lfo_timer_overflow; + + /* There are 128 LFO steps */ + OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; + + /* Valley Bell: Replaced old code (non-inverted triangle) with + the one from Genesis Plus GX 1.71. */ + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (OPN->lfo_cnt<64) + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt ^ 63) << 1; + else + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + OPN->LFO_PM = OPN->lfo_cnt >> 2; + } + } +} + +INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) +{ + /* unsigned int out; */ + unsigned int i = 4; /* four operators per channel */ + + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + + case EG_DEC: /* decay phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + + case EG_SUS: /* sustain phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + + case EG_REL: /* release phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + + /* Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file. + Disabling them fixes the SSG-EG. + Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines, + but less accurate. */ + #if 0 + out = ((UINT32)SLOT->volume); + + /* negate output (changes come from alternate bit, init comes from attack bit) */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) + out ^= MAX_ATT_INDEX; + + /* we need to store the result here because we are going to change ssgn + in next instruction */ + SLOT->vol_out = out + SLOT->tl; + #endif + + SLOT++; + i--; + } while (i); + +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) +{ + unsigned int i = 4; /* four operators per channel */ + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + i--; + } while (i); +} + + +INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) +{ + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* This really stupid bug caused a read outside of the + array [size 0x10] and returned invalid values. + This caused an annoying vibrato for some notes. + (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0) + Why are MAME cores always SOO buggy ?! */ + /* Oh, and before I forget: it's correct in fm.c */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* Thanks to Blargg - his patch that helped me to find this bug */ + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* update phase */ + SLOT->phase += (fc * SLOT->mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) +{ + UINT32 block_fnum = CH->block_fnum; + + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* the same stupid bug as above */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)); + + /* (frequency) phase overflow (credits to Nemesis) */ + int finc = fc + CH->SLOT[SLOT1].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; + + finc = fc + CH->SLOT[SLOT2].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; + + finc = fc + CH->SLOT[SLOT3].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; + + finc = fc + CH->SLOT[SLOT4].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) +{ + int ksr = kc >> SLOT->KSR; + + fc += SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH) +{ + UINT32 AM = OPN->LFO_AM >> CH->ams; + unsigned int eg_out; + + if (CH->Muted) + return; + + OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + eg_out = volume_calc(&CH->SLOT[SLOT1]); + { + INT32 out = CH->op1_out[0] + CH->op1_out[1]; + CH->op1_out[0] = CH->op1_out[1]; + + if( !CH->connect1 ) + { + /* algorithm 5 */ + OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; + } + else + { + /* other algorithms */ + *CH->connect1 += CH->op1_out[0]; + } + + + CH->op1_out[1] = 0; + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (!CH->FB) + out=0; + + CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); + } + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); + + + /* store current MEM */ + CH->mem_value = OPN->mem; + + /* update phase counters AFTER output calculations */ + if(CH->pms) + { + /* add support for 3 slot mode */ + if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2])) + { + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); + } + else update_phase_lfo_channel(OPN, CH); + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +static void FMCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + return; +} + + +/* CSM Key Controll */ +INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(OPN,CH,SLOT1); + FM_KEYON_CSM(OPN,CH,SLOT2); + FM_KEYON_CSM(OPN,CH,SLOT3); + FM_KEYON_CSM(OPN,CH,SLOT4); + OPN->SL3.key_csm = 1; +} + +#ifdef __STATE_H__ +/* FM channel save , internal state only */ +static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch) +{ + int slot , ch; + + for(ch=0;chop1_out); + state_save_register_device_item(device, ch, CH->fc); + /* slots */ + for(slot=0;slot<4;slot++) + { + FM_SLOT *SLOT = &CH->SLOT[slot]; + state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); + state_save_register_device_item(device, ch * 4 + slot, SLOT->state); + state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); + } + } +} + +static void FMsave_state_st(running_device *device,FM_ST *ST) +{ +#if FM_BUSY_FLAG_SUPPORT + state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); + state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); +#endif + state_save_register_device_item(device, 0, ST->address ); + state_save_register_device_item(device, 0, ST->irq ); + state_save_register_device_item(device, 0, ST->irqmask ); + state_save_register_device_item(device, 0, ST->status ); + state_save_register_device_item(device, 0, ST->mode ); + state_save_register_device_item(device, 0, ST->prescaler_sel ); + state_save_register_device_item(device, 0, ST->fn_h ); + state_save_register_device_item(device, 0, ST->TA ); + state_save_register_device_item(device, 0, ST->TAC ); + state_save_register_device_item(device, 0, ST->TB ); + state_save_register_device_item(device, 0, ST->TBC ); +} +#endif /* _STATE_H */ + +#if BUILD_OPN +/* write a OPN mode register 0x20-0x2f */ +static void OPNWriteMode(FM_OPN *OPN, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r) + { + case 0x21: /* Test */ + break; + case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ + if (v&8) /* LFO enabled ? */ + { + #if 0 + if (!OPN->lfo_timer_overflow) + { + /* restart LFO */ + OPN->lfo_cnt = 0; + OPN->lfo_timer = 0; + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + } + #endif + + OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; + } + else + { + /* Valley Bell: Ported from Genesis Plus GX 1.71 + hold LFO waveform in reset state */ + OPN->lfo_timer_overflow = 0; + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + + + OPN->LFO_PM = 0; + OPN->LFO_AM = 126; + /* OPN->lfo_timer_overflow = 0; */ + } + break; + case 0x24: /* timer A High 8*/ + OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); + break; + case 0x25: /* timer A Low 2*/ + OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); + break; + case 0x26: /* timer B */ + OPN->ST.TB = (UINT8)v; + break; + case 0x27: /* mode, timer control */ + set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; + CH = OPN->P_CH; + CH = &CH[c]; + if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1); + if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2); + if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3); + if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +static void OPNWriteReg(FM_OPN *OPN, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = OPN->P_CH; + CH = &CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(&OPN->ST,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(CH,SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(OPN->type,CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(OPN->type, SLOT,v); + + if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ + { + SLOT->AMmask = (v&0x80) ? ~0 : 0; + } + break; + + case 0x70: /* SR */ + set_sr(OPN->type,SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(OPN->type,SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + + + break; + + case 0xa0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xa0-0xa2 : FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->ST.fn_h = CH->block_fnum >> 8; +#endif + { + UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; + UINT8 blk = OPN->ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = OPN->fn_table[fn*2]>>(7-blk); + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + } + break; + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + OPN->ST.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) // workaround for stupid Kega Fusion init block + CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF); +#endif + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.fn_h = OPN->SL3.block_fnum[c] >> 8; +#endif + if(r < 0x100) + { + UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; + UINT8 blk = OPN->SL3.fn_h>>3; + /* keyscale code */ + OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); + OPN->SL3.block_fnum[c] = (blk<<11) | fn; + (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + { + OPN->SL3.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.block_fnum[c] = (OPN->SL3.fn_h << 8) | (OPN->SL3.block_fnum[c] & 0xFF); +#endif + } + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + unsigned char feedback = ((v>>3)&7); + CH->ALGO = v&7; + CH->FB = feedback ? feedback + 6 : 0; + setup_connection( OPN, CH, c ); + } + break; + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ + if( OPN->type & TYPE_LFOPAN) + { + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; + OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; + + } + break; + } + break; + } +} + +/* initialize time tables */ +static void init_timetables(FM_OPN *OPN, double freqbase) +{ + int i,d; + double rate; + + /* DeTune table */ + for (d = 0;d <= 3;d++) + { + for (i = 0;i <= 31;i++) + { + rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + OPN->ST.dt_tab[d][i] = (INT32) rate; + OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i]; + } + } + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + /* calculate fnumber -> increment counter table */ + for(i = 0; i < 4096; i++) + { + /* freq table for octave 7 */ + /* OPN phase increment counter = 20bit */ + /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ + /* where sample clock is M/144 */ + /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ + /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ + OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ + OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) ); +} + +/* prescaler set (and make time tables) */ +static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) +{ + /* frequency base */ + OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; + + /* EG is updated every 3 samples */ + OPN->eg_timer_add = (UINT32)((1<ST.freqbase); + OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase); + + /* Timer base time */ + OPN->ST.timer_prescaler = timer_prescaler; + + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); + + /* make time tables */ + init_timetables(OPN, OPN->ST.freqbase); +} + +static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) +{ + int c,s; + (void)ST; + + for( c = 0 ; c < num ; c++ ) + { + /* memset(&CH[c], 0x00, sizeof(FM_CH)); */ + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + CH[c].fc = 0; + for(s = 0 ; s < 4 ; s++ ) + { + /* memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); */ + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssg = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state= EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static void init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<CH; + FMSAMPLE *bufOut = buffer; + int i; +#if !RSM_ENABLE + FMSAMPLE bufTmp[2]; +#endif + + ym2612_pre_generate(chip); + + if (!frames) + { + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + } + + /* buffering */ + for(i=0 ; i < frames ; i++) + { +#if RSM_ENABLE + while(F2612->OPN.ST.framecnt >= F2612->OPN.ST.rateratio)/* Copy-Pasta from Nuked */ + { + /* Copy-Pasta from Nuked */ + F2612->OPN.ST.prev_sample[0] = F2612->OPN.ST.cur_sample[0]; + F2612->OPN.ST.prev_sample[1] = F2612->OPN.ST.cur_sample[1]; + ym2612_generate_one_native(chip, F2612->OPN.ST.cur_sample); + F2612->OPN.ST.framecnt -= F2612->OPN.ST.rateratio; + /* Copy-Pasta from Nuked */ + } + if (mix) + { + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } else { + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } + F2612->OPN.ST.framecnt += 1 << RSM_FRAC; +#else + if (mix) + { + ym2612_generate_one_native(chip, bufTmp); + bufOut[0] += bufTmp[0]; + bufOut[1] += bufTmp[1]; + } + else + { + ym2612_generate_one_native(chip, bufOut); + } + bufOut += 2; +#endif + } + /* ym2612_post_generate(chip, frames); */ +} + +void ym2612_pre_generate(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + FM_CH *cch = F2612->CH; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, &cch[0] ); + refresh_fc_eg_chan( OPN, &cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT4] , cch[2].fc , cch[2].kcode ); + } + } else + refresh_fc_eg_chan( OPN, &cch[2] ); + refresh_fc_eg_chan( OPN, &cch[3] ); + refresh_fc_eg_chan( OPN, &cch[4] ); + refresh_fc_eg_chan( OPN, &cch[5] ); +} + +void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[]) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + INT32 *out_fm = OPN->out_fm; + FM_CH *cch = F2612->CH; + INT32 dacout; + int lt,rt; + + if (! F2612->MuteDAC) + dacout = F2612->dacout; + else + dacout = 0; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + + /* calculate FM */ + if (! F2612->dac_test) + { + chan_calc(F2612, OPN, &cch[0]); + chan_calc(F2612, OPN, &cch[1]); + chan_calc(F2612, OPN, &cch[2]); + chan_calc(F2612, OPN, &cch[3]); + chan_calc(F2612, OPN, &cch[4]); + if( F2612->dacen ) + cch[5].connect4 += dacout; + else + chan_calc(F2612, OPN, &cch[5]); + } + else + { + out_fm[0] = out_fm[1] = dacout; + out_fm[2] = out_fm[3] = dacout; + out_fm[5] = dacout; + } + + /* advance LFO */ + advance_lfo(OPN); + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + /* reset EG timer */ + OPN->eg_timer -= OPN->eg_timer_overflow; + /* increment EG counter */ + OPN->eg_cnt++; + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (OPN->eg_cnt == 4096) + OPN->eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channel(OPN, &cch[0].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5].SLOT[SLOT1]); + } + + /*fprintf(hFile, "%u", FileSample, out_fm[0]); + for (lt = 0; lt < 6; lt ++) + fprintf(hFile, "\t%d", out_fm[lt]); + fprintf(hFile, "\n"); + FileSample ++;*/ + + if (out_fm[0] > 8192) out_fm[0] = 8192; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8192) out_fm[1] = 8192; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8192) out_fm[2] = 8192; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8192) out_fm[3] = 8192; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8192) out_fm[4] = 8192; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8192) out_fm[5] = 8192; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + + /* 6-channels mixing */ + lt = ((out_fm[0]>>0) & OPN->pan[0]); + rt = ((out_fm[0]>>0) & OPN->pan[1]); + lt += ((out_fm[1]>>0) & OPN->pan[2]); + rt += ((out_fm[1]>>0) & OPN->pan[3]); + lt += ((out_fm[2]>>0) & OPN->pan[4]); + rt += ((out_fm[2]>>0) & OPN->pan[5]); + lt += ((out_fm[3]>>0) & OPN->pan[6]); + rt += ((out_fm[3]>>0) & OPN->pan[7]); + if (! F2612->dac_test) + { + lt += ((out_fm[4]>>0) & OPN->pan[8]); + rt += ((out_fm[4]>>0) & OPN->pan[9]); + } + else + { + lt += dacout; + lt += dacout; + } + lt += ((out_fm[5]>>0) & OPN->pan[10]); + rt += ((out_fm[5]>>0) & OPN->pan[11]); + + /* Limit( lt, MAXOUT, MINOUT ); */ + /* Limit( rt, MAXOUT, MINOUT ); */ + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + if (F2612->WaveOutMode & 0x01) + F2612->WaveL = lt; + if (F2612->WaveOutMode & 0x02) + F2612->WaveR = rt; + if (F2612->WaveOutMode ^ 0x03) + F2612->WaveOutMode ^= 0x03; + + buffer[0] = (FMSAMPLE)(F2612->WaveL / 2); + buffer[1] = (FMSAMPLE)(F2612->WaveR / 2); + + /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + OPN->SL3.key_csm <<= 1; + + /* timer A control */ + /* INTERNAL_TIMER_A( &OPN->ST , cch[2] ) */ + { + if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) + if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) + { + TimerAOver( &OPN->ST ); + /* CSM mode total level latch and auto key on */ + if( OPN->ST.mode & 0x80 ) + CSMKeyControll( OPN, &cch[2] ); + } + } + + /* CSM Mode Key ON still disabled */ + if (OPN->SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&cch[2],SLOT1); + FM_KEYOFF_CSM(&cch[2],SLOT2); + FM_KEYOFF_CSM(&cch[2],SLOT3); + FM_KEYOFF_CSM(&cch[2],SLOT4); + OPN->SL3.key_csm = 0; + } +} + +#if 0 +void ym2612_post_generate(void *chip, int length) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* timer B control */ + INTERNAL_TIMER_B(&F2612->OPN.ST, length); +} +#endif + +#ifdef __STATE_H__ +void ym2612_postload(void *chip) +{ + if (chip) + { + YM2612 *F2612 = (YM2612 *)chip; + int r; + + /* DAC data & port */ + F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ + F2612->dacen = F2612->REGS[0x2d] & 0x80; + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* channels */ + /*FM_channel_postload(F2612->CH,6);*/ + } +} + +static void YM2612_save_state(YM2612 *F2612, running_device *device) +{ + state_save_register_device_item_array(device, 0, F2612->REGS); + FMsave_state_st(device,&F2612->OPN.ST); + FMsave_state_channel(device,F2612->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2612->addr_A1); +} +#endif /* _STATE_H */ + +/* initialize YM2612 emulator(s) */ +/* void * ym2612_init(void *param, running_device *device, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) */ +void * ym2612_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) +{ + YM2612 *F2612; + + if (clock <= 0 || rate <= 0) + return NULL; /* Forbid zero clock and sample rate */ + + /* allocate extend state space */ + /* F2612 = auto_alloc_clear(device->machine, YM2612); */ + F2612 = (YM2612 *)malloc(sizeof(YM2612)); + if (F2612 == NULL) + return NULL; + memset(F2612, 0x00, sizeof(YM2612)); + /* allocate total level table (128kb space) */ + init_tables(); + + F2612->OPN.ST.param = param; + F2612->OPN.type = TYPE_YM2612; + F2612->OPN.P_CH = F2612->CH; + /* F2612->OPN.ST.device = device; */ + F2612->OPN.ST.clock = clock; +#if RSM_ENABLE + F2612->OPN.ST.rate = 53267; + F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock); + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#else + F2612->OPN.ST.rate = rate; +#endif + /* F2612->OPN.ST.irq = 0; */ + /* F2612->OPN.ST.status = 0; */ + /* Extend handler */ + F2612->OPN.ST.timer_handler = timer_handler; + F2612->OPN.ST.IRQ_Handler = IRQHandler; + + if (PseudoSt) + F2612->WaveOutMode = 0x01; + else + F2612->WaveOutMode = 0x03; + /*hFile = fopen("YM2612.log", "wt"); + fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); + fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); + FileSample = 0;*/ + +#ifdef __STATE_H__ + YM2612_save_state(F2612, device); +#endif + return F2612; +} + +/* shut down emulator */ +void ym2612_shutdown(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* fclose(hFile); */ + + FMCloseTable(); + /* auto_free(F2612->OPN.ST.device->machine, F2612); */ + free(F2612); +} + +/* reset one of chip */ +void ym2612_reset_chip(void *chip) +{ + int i; + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + + OPNSetPres( OPN, 6*24, 6*24, 0); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + /* OPNWriteMode(OPN,0x27,0x30); */ /* mode 0 , timer reset */ + +#if RSM_ENABLE + /* Resampler's state */ + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#endif + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + OPN->LFO_AM = 126; + OPN->LFO_PM = 0; + + OPN->ST.TAC = 0; + OPN->ST.TBC = 0; + + OPN->SL3.key_csm = 0; + + OPN->ST.status = 0; + OPN->ST.mode = 0; + + memset(F2612->REGS, 0x00, sizeof(UINT8) * 512); + + OPNWriteMode(OPN,0x22,0x00); + + OPNWriteMode(OPN,0x27,0x30); + OPNWriteMode(OPN,0x26,0x00); + OPNWriteMode(OPN,0x25,0x00); + OPNWriteMode(OPN,0x24,0x00); + + reset_channels( &OPN->ST , &F2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + + /* DAC mode clear */ + F2612->dacen = 0; + F2612->dac_test = 0; + F2612->dacout = 0; + + if (F2612->WaveOutMode == 0x02) + F2612->WaveOutMode >>= 1; +} + +/* YM2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +int ym2612_write(void *chip, int a, UINT8 v) +{ + YM2612 *F2612 = (YM2612 *)chip; + int addr; + + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a&3) + { + case 0: /* address port 0 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 0; + break; + + case 1: /* data port 0 */ + if (F2612->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr] = v; + switch( addr & 0xf0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (YM2612) */ + ym2612_update_one(chip, DUMMYBUF, 0); + F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ + break; + case 0x2b: /* DAC Sel (YM2612) */ + /* b7 = dac enable */ + F2612->dacen = v & 0x80; + break; + case 0x2C: /* undocumented: DAC Test Reg */ + /* b5 = volume enable */ + F2612->dac_test = v & 0x20; + break; + default: /* OPN section */ + /* ym2612_update_req(F2612->OPN.ST.param); */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteMode(&(F2612->OPN),addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteReg(&(F2612->OPN),addr,v); + } + break; + + case 2: /* address port 1 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2612->addr_A1 != 1) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr | 0x100] = v; + ym2612_update_one(chip, DUMMYBUF, 0); + OPNWriteReg(&(F2612->OPN),addr | 0x100,v); + break; + } + return F2612->OPN.ST.irq; +} + +UINT8 ym2612_read(void *chip,int a) +{ + YM2612 *F2612 = (YM2612 *)chip; + + switch( a&3) + { + case 0: /* status 0 */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + case 1: + case 2: + case 3: + /* LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a)); */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + } + return 0; +} + +int ym2612_timer_over(void *chip,int c) +{ + YM2612 *F2612 = (YM2612 *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2612->OPN.ST) ); + } + else + { /* Timer A */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* timer update */ + TimerAOver( &(F2612->OPN.ST) ); + /* CSM mode key,TL controll */ + if ((F2612->OPN.ST.mode & 0xc0) == 0x80) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) ); + } + } + return F2612->OPN.ST.irq; +} + + +void ym2612_set_mutemask(void *chip, UINT32 MuteMask) +{ + YM2612 *F2612 = (YM2612 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + F2612->MuteDAC = (MuteMask >> 6) & 0x01; + + return; +} + +void ym2612_setoptions(UINT8 Flags) +{ + PseudoSt = (Flags >> 2) & 0x01; + + return; +} +#endif /* (BUILD_YM2612||BUILD_YM3238) */ diff --git a/src/sound/opnmidi/chips/mame/mame_ym2612fm.h b/src/sound/opnmidi/chips/mame/mame_ym2612fm.h new file mode 100644 index 000000000..de05236ef --- /dev/null +++ b/src/sound/opnmidi/chips/mame/mame_ym2612fm.h @@ -0,0 +1,164 @@ +/* + File: fm.h -- header file for software emulation for FM sound generator + +*/ + +#ifndef FM_HHHHH +#define FM_HHHHH + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mamedef.h" + +/* --- select emulation chips --- */ +/* +#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator +#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator +#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator +#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator +#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator +#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator +*/ +#define BUILD_YM2203 0 +#define BUILD_YM2608 0 +#define BUILD_YM2610 0 +#define BUILD_YM2610B 0 +#define BUILD_YM2612 1 +#define BUILD_YM3438 0 + +#define FM_BUSY_FLAG_SUPPORT 0 + +/* select bit size of output : 8 or 16 */ +#define FM_SAMPLE_BITS 16 + +/* select timer system internal or external */ +#define FM_INTERNAL_TIMER 1 + +/* --- speedup optimize --- */ +/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ +/* #define FM_BUSY_FLAG_SUPPORT 1 */ + +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +typedef struct _ssg_callbacks ssg_callbacks; +struct _ssg_callbacks +{ + void (*set_clock)(void *param, int clock); + void (*write)(void *param, int address, int data); + int (*read)(void *param); + void (*reset)(void *param); +}; + +/* --- external callback funstions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +#define TIME_TYPE attotime +#define UNDEFINED_TIME attotime_zero +#define FM_GET_TIME_NOW(machine) timer_get_time(machine) +#define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) +#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) +#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) +#endif + +/* compiler dependence */ +#if 0 +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif /* OSD_CPU_H */ +#endif + + + +typedef stream_sample_t FMSAMPLE; +/* +#if (FM_SAMPLE_BITS==16) +typedef INT16 FMSAMPLE; +#endif +#if (FM_SAMPLE_BITS==8) +typedef unsigned char FMSAMPLE; +#endif +*/ + +typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(void *param,int irq); +/* FM_TIMERHANDLER : Stop or Start timer */ +/* int n = chip number */ +/* int c = Channel 0=TimerA,1=TimerB */ +/* int count = timer count (0=stop) */ +/* doube stepTime = step time of one count (sec.)*/ + +/* FM_IRQHHANDLER : IRQ level changing sense */ +/* int n = chip number */ +/* int irq = IRQ level 0=OFF,1=ON */ + +#if (BUILD_YM2612||BUILD_YM3438) + +/** + * @brief Initialize chip and return the instance + * @param param Unused, keep NULL + * @param baseclock YM2612 clock + * @param rate Output sample rate + * @param TimerHandler Keep NULL + * @param IRQHandler Keep NULL + * @return Chip instance or NULL on any error + */ +void * ym2612_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); +/** + * @brief Free chip instance + * @param chip Chip instance + */ +void ym2612_shutdown(void *chip); +/** + * @brief Reset state of the chip + * @param chip Chip instance + */ +void ym2612_reset_chip(void *chip); +/** + * @brief Generate stereo output of specified length + * @param chip Chip instance + * @param buffer Output sound buffer + * @param frames Output buffer size in frames (one frame - two array entries of the buffer) + * @param mix 0 - override buffer data, 1 - mix output data with a content of the buffer + */ +void ym2612_generate(void *chip, FMSAMPLE *buffer, int frames, int mix); +#define ym2612_update_one(chip, buffer, length) ym2612_generate(chip, buffer, length, 0) + +/** + * @brief Single-Sample generation prepare + * @param chip Chip instance + */ +void ym2612_pre_generate(void *chip); +/** + * @brief Generate single stereo PCM frame. Will be used native sample rate of 53267 Hz + * @param chip Chip instance + * @param buffer One stereo PCM frame + */ +void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]); + +/* void ym2612_post_generate(void *chip, int length); */ + +int ym2612_write(void *chip, int a,unsigned char v); +unsigned char ym2612_read(void *chip,int a); +int ym2612_timer_over(void *chip, int c ); +void ym2612_postload(void *chip); + +void ym2612_set_mutemask(void *chip, UINT32 MuteMask); +void ym2612_setoptions(UINT8 Flags); +#endif /* (BUILD_YM2612||BUILD_YM3438) */ + +#ifdef __cplusplus +} +#endif + +#endif /* FM_HHHHH */ diff --git a/src/sound/opnmidi/chips/mame/mamedef.h b/src/sound/opnmidi/chips/mame/mamedef.h new file mode 100644 index 000000000..efed080b8 --- /dev/null +++ b/src/sound/opnmidi/chips/mame/mamedef.h @@ -0,0 +1,65 @@ +#ifndef MAMEDEF_H_ +#define MAMEDEF_H_ + +/* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */ +/* 8-bit values */ +typedef unsigned char UINT8; +typedef signed char INT8; + +/* 16-bit values */ +typedef unsigned short UINT16; +typedef signed short INT16; + +/* 32-bit values */ +#ifndef _WINDOWS_H +typedef unsigned int UINT32; +typedef signed int INT32; +#endif + +/* 64-bit values */ +#ifndef _WINDOWS_H +#ifdef _MSC_VER +typedef signed __int64 INT64; +typedef unsigned __int64 UINT64; +#else +__extension__ typedef unsigned long long UINT64; +__extension__ typedef signed long long INT64; +#endif +#endif + +/* offsets and addresses are 32-bit (for now...) */ +typedef UINT32 offs_t; + +/* stream_sample_t is used to represent a single sample in a sound stream */ +typedef INT16 stream_sample_t; + +#if defined(VGM_BIG_ENDIAN) +#define BYTE_XOR_BE(x) (x) +#elif defined(VGM_LITTLE_ENDIAN) +#define BYTE_XOR_BE(x) ((x) ^ 0x01) +#else +/* don't define BYTE_XOR_BE so that it throws an error when compiling */ +#endif + +#if defined(_MSC_VER) +//#define INLINE static __forceinline +#define INLINE static __inline +#elif defined(__GNUC__) +#define INLINE static __inline__ +#else +#define INLINE static inline +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef _DEBUG +#define logerror printf +#else +#define logerror +#endif + +typedef void (*SRATE_CALLBACK)(void*, UINT32); + +#endif /* __MAMEDEF_H__ */ diff --git a/src/sound/opnmidi/chips/mame_opn2.cpp b/src/sound/opnmidi/chips/mame_opn2.cpp new file mode 100644 index 000000000..e9533131f --- /dev/null +++ b/src/sound/opnmidi/chips/mame_opn2.cpp @@ -0,0 +1,54 @@ +#include "mame_opn2.h" +#include "mame/mame_ym2612fm.h" +#include +#include + +MameOPN2::MameOPN2() +{ + chip = NULL; + setRate(m_rate, m_clock); +} + +MameOPN2::~MameOPN2() +{ + ym2612_shutdown(chip); +} + +void MameOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + if(chip) + ym2612_shutdown(chip); + uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast(nativeRate); + chip = ym2612_init(NULL, (int)clock, (int)chipRate, NULL, NULL); + ym2612_reset_chip(chip); +} + +void MameOPN2::reset() +{ + OPNChipBaseT::reset(); + ym2612_reset_chip(chip); +} + +void MameOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + ym2612_write(chip, 0 + (int)(port) * 2, (uint8_t)addr); + ym2612_write(chip, 1 + (int)(port) * 2, data); +} + +void MameOPN2::nativePreGenerate() +{ + void *chip = this->chip; + ym2612_pre_generate(chip); +} + +void MameOPN2::nativeGenerate(int16_t *frame) +{ + void *chip = this->chip; + ym2612_generate_one_native(chip, frame); +} + +const char *MameOPN2::emulatorName() +{ + return "MAME YM2612"; +} diff --git a/src/sound/opnmidi/chips/mame_opn2.h b/src/sound/opnmidi/chips/mame_opn2.h new file mode 100644 index 000000000..832602476 --- /dev/null +++ b/src/sound/opnmidi/chips/mame_opn2.h @@ -0,0 +1,23 @@ +#ifndef MAME_OPN2_H +#define MAME_OPN2_H + +#include "opn_chip_base.h" + +class MameOPN2 final : public OPNChipBaseT +{ + void *chip; +public: + MameOPN2(); + ~MameOPN2() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void nativePreGenerate() override; + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // MAME_OPN2_H diff --git a/src/sound/opnmidi/chips/nuked/ym3438.c b/src/sound/opnmidi/chips/nuked/ym3438.c new file mode 100644 index 000000000..019ca4cde --- /dev/null +++ b/src/sound/opnmidi/chips/nuked/ym3438.c @@ -0,0 +1,1592 @@ +/* + * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.7 + */ + +#include +#include "ym3438.h" + +enum { + eg_num_attack = 0, + eg_num_decay = 1, + eg_num_sustain = 2, + eg_num_release = 3 +}; + +/* logsin table */ +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* exp table */ +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, + 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, + 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, + 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, + 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, + 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, + 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, + 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, + 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, + 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, + 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, + 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, + 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, + 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, + 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, + 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, + 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +/* Note table */ +static const Bit32u fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 +}; + +/* Envelope generator */ +static const Bit32u eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const Bit8u eg_am_shift[4] = { + 7, 3, 1, 0 +}; + +/* Phase generator */ +static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + +static const Bit32u pg_lfo_sh1[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 1, 1 }, + { 7, 7, 7, 7, 1, 1, 1, 1 }, + { 7, 7, 7, 1, 1, 1, 1, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 } +}; + +static const Bit32u pg_lfo_sh2[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 2, 2, 2, 2 }, + { 7, 7, 7, 2, 2, 2, 7, 7 }, + { 7, 7, 2, 2, 7, 7, 2, 2 }, + { 7, 7, 2, 7, 7, 7, 2, 7 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 } +}; + +/* Address decoder */ +static const Bit32u op_offset[12] = { + 0x000, /* Ch1 OP1/OP2 */ + 0x001, /* Ch2 OP1/OP2 */ + 0x002, /* Ch3 OP1/OP2 */ + 0x100, /* Ch4 OP1/OP2 */ + 0x101, /* Ch5 OP1/OP2 */ + 0x102, /* Ch6 OP1/OP2 */ + 0x004, /* Ch1 OP3/OP4 */ + 0x005, /* Ch2 OP3/OP4 */ + 0x006, /* Ch3 OP3/OP4 */ + 0x104, /* Ch4 OP3/OP4 */ + 0x105, /* Ch5 OP3/OP4 */ + 0x106 /* Ch6 OP3/OP4 */ +}; + +static const Bit32u ch_offset[6] = { + 0x000, /* Ch1 */ + 0x001, /* Ch2 */ + 0x002, /* Ch3 */ + 0x100, /* Ch4 */ + 0x101, /* Ch5 */ + 0x102 /* Ch6 */ +}; + +/* LFO */ +static const Bit32u lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 +}; + +/* FM algorithm */ +static const Bit32u fm_algorithm[4][6][8] = { + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ + }, + { + { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ + { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ + { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ + { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ + } +}; + +static Bit32u chip_type = ym3438_type_discrete; + +void OPN2_DoIO(ym3438_t *chip) +{ + /* Write signal check */ + chip->write_a_en = (chip->write_a & 0x03) == 0x01; + chip->write_d_en = (chip->write_d & 0x03) == 0x01; + chip->write_a <<= 1; + chip->write_d <<= 1; + /* Busy counter */ + chip->busy = chip->write_busy; + chip->write_busy_cnt += chip->write_busy; + chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; + chip->write_busy_cnt &= 0x1f; +} + +void OPN2_DoRegWrite(ym3438_t *chip) +{ + Bit32u i; + Bit32u slot = chip->cycles % 12; + Bit32u address; + Bit32u channel = chip->channel; + /* Update registers */ + if (chip->write_fm_data) + { + /* Slot */ + if (op_offset[slot] == (chip->address & 0x107)) + { + if (chip->address & 0x08) + { + /* OP2, OP4 */ + slot += 12; + } + address = chip->address & 0xf0; + switch (address) + { + case 0x30: /* DT, MULTI */ + chip->multi[slot] = chip->data & 0x0f; + if (!chip->multi[slot]) + { + chip->multi[slot] = 1; + } + else + { + chip->multi[slot] <<= 1; + } + chip->dt[slot] = (chip->data >> 4) & 0x07; + break; + case 0x40: /* TL */ + chip->tl[slot] = chip->data & 0x7f; + break; + case 0x50: /* KS, AR */ + chip->ar[slot] = chip->data & 0x1f; + chip->ks[slot] = (chip->data >> 6) & 0x03; + break; + case 0x60: /* AM, DR */ + chip->dr[slot] = chip->data & 0x1f; + chip->am[slot] = (chip->data >> 7) & 0x01; + break; + case 0x70: /* SR */ + chip->sr[slot] = chip->data & 0x1f; + break; + case 0x80: /* SL, RR */ + chip->rr[slot] = chip->data & 0x0f; + chip->sl[slot] = (chip->data >> 4) & 0x0f; + chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; + break; + case 0x90: /* SSG-EG */ + chip->ssg_eg[slot] = chip->data & 0x0f; + break; + default: + break; + } + } + + /* Channel */ + if (ch_offset[channel] == (chip->address & 0x103)) + { + address = chip->address & 0xfc; + switch (address) + { + case 0xa0: + chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); + chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; + chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; + break; + case 0xa4: + chip->reg_a4 = chip->data & 0xff; + break; + case 0xa8: + chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); + chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; + chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; + break; + case 0xac: + chip->reg_ac = chip->data & 0xff; + break; + case 0xb0: + chip->connect[channel] = chip->data & 0x07; + chip->fb[channel] = (chip->data >> 3) & 0x07; + break; + case 0xb4: + chip->pms[channel] = chip->data & 0x07; + chip->ams[channel] = (chip->data >> 4) & 0x03; + chip->pan_l[channel] = (chip->data >> 7) & 0x01; + chip->pan_r[channel] = (chip->data >> 6) & 0x01; + break; + default: + break; + } + } + } + + if (chip->write_a_en || chip->write_d_en) + { + /* Data */ + if (chip->write_a_en) + { + chip->write_fm_data = 0; + } + + if (chip->write_fm_address && chip->write_d_en) + { + chip->write_fm_data = 1; + } + + /* Address */ + if (chip->write_a_en) + { + if ((chip->write_data & 0xf0) != 0x00) + { + /* FM Write */ + chip->address = chip->write_data; + chip->write_fm_address = 1; + } + else + { + /* SSG write */ + chip->write_fm_address = 0; + } + } + + /* FM Mode */ + /* Data */ + if (chip->write_d_en && (chip->write_data & 0x100) == 0) + { + switch (chip->address) + { + case 0x21: /* LSI test 1 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; + } + break; + case 0x22: /* LFO control */ + if ((chip->write_data >> 3) & 0x01) + { + chip->lfo_en = 0x7f; + } + else + { + chip->lfo_en = 0; + } + chip->lfo_freq = chip->write_data & 0x07; + break; + case 0x24: /* Timer A */ + chip->timer_a_reg &= 0x03; + chip->timer_a_reg |= (chip->write_data & 0xff) << 2; + break; + case 0x25: + chip->timer_a_reg &= 0x3fc; + chip->timer_a_reg |= chip->write_data & 0x03; + break; + case 0x26: /* Timer B */ + chip->timer_b_reg = chip->write_data & 0xff; + break; + case 0x27: /* CSM, Timer control */ + chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; + chip->mode_csm = chip->mode_ch3 == 2; + chip->timer_a_load = chip->write_data & 0x01; + chip->timer_a_enable = (chip->write_data >> 2) & 0x01; + chip->timer_a_reset = (chip->write_data >> 4) & 0x01; + chip->timer_b_load = (chip->write_data >> 1) & 0x01; + chip->timer_b_enable = (chip->write_data >> 3) & 0x01; + chip->timer_b_reset = (chip->write_data >> 5) & 0x01; + break; + case 0x28: /* Key on/off */ + for (i = 0; i < 4; i++) + { + chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; + } + if ((chip->write_data & 0x03) == 0x03) + { + /* Invalid address */ + chip->mode_kon_channel = 0xff; + } + else + { + chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; + } + break; + case 0x2a: /* DAC data */ + chip->dacdata &= 0x01; + chip->dacdata |= (chip->write_data ^ 0x80) << 1; + break; + case 0x2b: /* DAC enable */ + chip->dacen = chip->write_data >> 7; + break; + case 0x2c: /* LSI test 2 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; + } + chip->dacdata &= 0x1fe; + chip->dacdata |= chip->mode_test_2c[3]; + chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; + break; + default: + break; + } + } + + /* Address */ + if (chip->write_a_en) + { + chip->write_fm_mode_a = chip->write_data & 0xff; + } + } + + if (chip->write_fm_data) + { + chip->data = chip->write_data & 0xff; + } +} + +void OPN2_PhaseCalcIncrement(ym3438_t *chip) +{ + Bit32u chan = chip->channel; + Bit32u slot = chip->cycles; + Bit32u fnum = chip->pg_fnum; + Bit32u fnum_h = fnum >> 4; + Bit32u fm; + Bit32u basefreq; + Bit8u lfo = chip->lfo_pm; + Bit8u lfo_l = lfo & 0x0f; + Bit8u pms = chip->pms[chan]; + Bit8u dt = chip->dt[slot]; + Bit8u dt_l = dt & 0x03; + Bit8u detune = 0; + Bit8u block, note; + Bit8u sum, sum_h, sum_l; + Bit8u kcode = chip->pg_kcode; + + fnum <<= 1; + /* Apply LFO */ + if (lfo_l & 0x08) + { + lfo_l ^= 0x0f; + } + fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); + if (pms > 5) + { + fm <<= pms - 5; + } + fm >>= 2; + if (lfo & 0x10) + { + fnum -= fm; + } + else + { + fnum += fm; + } + fnum &= 0xfff; + + basefreq = (fnum << chip->pg_block) >> 2; + + /* Apply detune */ + if (dt_l) + { + if (kcode > 0x1c) + { + kcode = 0x1c; + } + block = kcode >> 2; + note = kcode & 0x03; + sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); + sum_h = sum >> 1; + sum_l = sum & 0x01; + detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); + } + if (dt & 0x04) + { + basefreq -= detune; + } + else + { + basefreq += detune; + } + basefreq &= 0x1ffff; + chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; + chip->pg_inc[slot] &= 0xfffff; +} + +void OPN2_PhaseGenerate(ym3438_t *chip) +{ + Bit32u slot; + /* Mask increment */ + slot = (chip->cycles + 20) % 24; + if (chip->pg_reset[slot]) + { + chip->pg_inc[slot] = 0; + } + /* Phase step */ + slot = (chip->cycles + 19) % 24; + chip->pg_phase[slot] += chip->pg_inc[slot]; + chip->pg_phase[slot] &= 0xfffff; + if (chip->pg_reset[slot] || chip->mode_test_21[3]) + { + chip->pg_phase[slot] = 0; + } +} + +void OPN2_EnvelopeSSGEG(ym3438_t *chip) +{ + Bit32u slot = chip->cycles; + Bit8u direction = 0; + chip->eg_ssg_pgrst_latch[slot] = 0; + chip->eg_ssg_repeat_latch[slot] = 0; + chip->eg_ssg_hold_up_latch[slot] = 0; + chip->eg_ssg_inv[slot] = 0; + if (chip->ssg_eg[slot] & 0x08) + { + direction = chip->eg_ssg_dir[slot]; + if (chip->eg_level[slot] & 0x200) + { + /* Reset */ + if ((chip->ssg_eg[slot] & 0x03) == 0x00) + { + chip->eg_ssg_pgrst_latch[slot] = 1; + } + /* Repeat */ + if ((chip->ssg_eg[slot] & 0x01) == 0x00) + { + chip->eg_ssg_repeat_latch[slot] = 1; + } + /* Inverse */ + if ((chip->ssg_eg[slot] & 0x03) == 0x02) + { + direction ^= 1; + } + if ((chip->ssg_eg[slot] & 0x03) == 0x03) + { + direction = 1; + } + } + /* Hold up */ + if (chip->eg_kon_latch[slot] + && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) + { + chip->eg_ssg_hold_up_latch[slot] = 1; + } + direction &= chip->eg_kon[slot]; + chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) + & chip->eg_kon[slot]; + } + chip->eg_ssg_dir[slot] = direction; + chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; +} + +void OPN2_EnvelopeADSR(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 22) % 24; + + Bit8u nkon = chip->eg_kon_latch[slot]; + Bit8u okon = chip->eg_kon[slot]; + Bit8u kon_event; + Bit8u koff_event; + Bit8u eg_off; + Bit16s level; + Bit16s nextlevel = 0; + Bit16s ssg_level; + Bit8u nextstate = chip->eg_state[slot]; + Bit16s inc = 0; + chip->eg_read[0] = chip->eg_read_inc; + chip->eg_read_inc = chip->eg_inc > 0; + + /* Reset phase generator */ + chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; + + /* KeyOn/Off */ + kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); + koff_event = okon && !nkon; + + ssg_level = level = (Bit16s)chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + ssg_level = 512 - level; + ssg_level &= 0x3ff; + } + if (koff_event) + { + level = ssg_level; + } + if (chip->eg_ssg_enable[slot]) + { + eg_off = level >> 9; + } + else + { + eg_off = (level & 0x3f0) == 0x3f0; + } + nextlevel = level; + if (kon_event) + { + nextstate = eg_num_attack; + /* Instant attack */ + if (chip->eg_ratemax) + { + nextlevel = 0; + } + else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + } + else + { + switch (chip->eg_state[slot]) + { + case eg_num_attack: + if (level == 0) + { + nextstate = eg_num_decay; + } + else if(chip->eg_inc && !chip->eg_ratemax && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + break; + case eg_num_decay: + if ((level >> 5) == chip->eg_sl[1]) + { + nextstate = eg_num_sustain; + } + else if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + case eg_num_sustain: + case eg_num_release: + if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + default: + break; + } + if (!nkon) + { + nextstate = eg_num_release; + } + } + if (chip->eg_kon_csm[slot]) + { + nextlevel |= chip->eg_tl[1] << 3; + } + + /* Envelope off */ + if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) + { + nextstate = eg_num_release; + nextlevel = 0x3ff; + } + + nextlevel += inc; + + chip->eg_kon[slot] = chip->eg_kon_latch[slot]; + chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; + chip->eg_state[slot] = nextstate; +} + +void OPN2_EnvelopePrepare(ym3438_t *chip) +{ + Bit8u rate; + Bit8u sum; + Bit8u inc = 0; + Bit32u slot = chip->cycles; + Bit8u rate_sel; + + /* Prepare increment */ + rate = (chip->eg_rate << 1) + chip->eg_ksv; + + if (rate > 0x3f) + { + rate = 0x3f; + } + + sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; + if (chip->eg_rate != 0 && chip->eg_quotient == 2) + { + if (rate < 48) + { + switch (sum) + { + case 12: + inc = 1; + break; + case 13: + inc = (rate >> 1) & 0x01; + break; + case 14: + inc = rate & 0x01; + break; + default: + break; + } + } + else + { + inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; + if (inc > 4) + { + inc = 4; + } + } + } + chip->eg_inc = inc; + chip->eg_ratemax = (rate >> 1) == 0x1f; + + /* Prepare rate & ksv */ + rate_sel = chip->eg_state[slot]; + if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) + || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) + { + rate_sel = eg_num_attack; + } + switch (rate_sel) + { + case eg_num_attack: + chip->eg_rate = chip->ar[slot]; + break; + case eg_num_decay: + chip->eg_rate = chip->dr[slot]; + break; + case eg_num_sustain: + chip->eg_rate = chip->sr[slot]; + break; + case eg_num_release: + chip->eg_rate = (chip->rr[slot] << 1) | 0x01; + break; + default: + break; + } + chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); + if (chip->am[slot]) + { + chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; + } + else + { + chip->eg_lfo_am = 0; + } + /* Delay TL & SL value */ + chip->eg_tl[1] = chip->eg_tl[0]; + chip->eg_tl[0] = chip->tl[slot]; + chip->eg_sl[1] = chip->eg_sl[0]; + chip->eg_sl[0] = chip->sl[slot]; +} + +void OPN2_EnvelopeGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 23) % 24; + Bit16u level; + + level = chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + level = 512 - level; + } + if (chip->mode_test_21[5]) + { + level = 0; + } + level &= 0x3ff; + + /* Apply AM LFO */ + level += chip->eg_lfo_am; + + /* Apply TL */ + if (!(chip->mode_csm && chip->channel == 2 + 1)) + { + level += chip->eg_tl[0] << 3; + } + if (level > 0x3ff) + { + level = 0x3ff; + } + chip->eg_out[slot] = level; +} + +void OPN2_UpdateLFO(ym3438_t *chip) +{ + if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) + { + chip->lfo_quotient = 0; + chip->lfo_cnt++; + } + else + { + chip->lfo_quotient += chip->lfo_inc; + } + chip->lfo_cnt &= chip->lfo_en; +} + +void OPN2_FMPrepare(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 6) % 24; + Bit32u channel = chip->channel; + Bit16s mod, mod1, mod2; + Bit32u op = slot / 6; + Bit8u connect = chip->connect[channel]; + Bit32u prevslot = (chip->cycles + 18) % 24; + + /* Calculate modulation */ + mod1 = mod2 = 0; + + if (fm_algorithm[op][0][connect]) + { + mod2 |= chip->fm_op1[channel][0]; + } + if (fm_algorithm[op][1][connect]) + { + mod1 |= chip->fm_op1[channel][1]; + } + if (fm_algorithm[op][2][connect]) + { + mod1 |= chip->fm_op2[channel]; + } + if (fm_algorithm[op][3][connect]) + { + mod2 |= chip->fm_out[prevslot]; + } + if (fm_algorithm[op][4][connect]) + { + mod1 |= chip->fm_out[prevslot]; + } + mod = mod1 + mod2; + if (op == 0) + { + /* Feedback */ + mod = mod >> (10 - chip->fb[channel]); + if (!chip->fb[channel]) + { + mod = 0; + } + } + else + { + mod >>= 1; + } + chip->fm_mod[slot] = mod; + + slot = (chip->cycles + 18) % 24; + /* OP1 */ + if (slot / 6 == 0) + { + chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; + chip->fm_op1[channel][0] = chip->fm_out[slot]; + } + /* OP2 */ + if (slot / 6 == 2) + { + chip->fm_op2[channel] = chip->fm_out[slot]; + } +} + +void OPN2_ChGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 18) % 24; + Bit32u channel = chip->channel; + Bit32u op = slot / 6; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s acc = chip->ch_acc[channel]; + Bit16s add = test_dac; + Bit16s sum = 0; + if (op == 0 && !test_dac) + { + acc = 0; + } + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot] >> 5; + } + sum = acc + add; + /* Clamp */ + if (sum > 255) + { + sum = 255; + } + else if(sum < -256) + { + sum = -256; + } + + if (op == 0 || test_dac) + { + chip->ch_out[channel] = chip->ch_acc[channel]; + } + chip->ch_acc[channel] = sum; +} + +void OPN2_ChOutput(ym3438_t *chip) +{ + Bit32u cycles = chip->cycles; + Bit32u slot = chip->cycles; + Bit32u channel = chip->channel; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s out; + Bit16s sign; + Bit32u out_en; + chip->ch_read = chip->ch_lock; + if (slot < 12) + { + /* Ch 4,5,6 */ + channel++; + } + if ((cycles & 3) == 0) + { + if (!test_dac) + { + /* Lock value */ + chip->ch_lock = chip->ch_out[channel]; + } + chip->ch_lock_l = chip->pan_l[channel]; + chip->ch_lock_r = chip->pan_r[channel]; + } + /* Ch 6 */ + if (((cycles >> 2) == 1 && chip->dacen) || test_dac) + { + out = (Bit16s)chip->dacdata; + out <<= 7; + out >>= 7; + } + else + { + out = chip->ch_lock; + } + chip->mol = 0; + chip->mor = 0; + + if (chip_type == ym3438_type_ym2612) + { + out_en = ((cycles & 3) == 3) || test_dac; + /* YM2612 DAC emulation(not verified) */ + sign = out >> 8; + if (out >= 0) + { + out++; + sign++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + else + { + chip->mol = sign; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + else + { + chip->mor = sign; + } + /* Amplify signal */ + chip->mol *= 3; + chip->mor *= 3; + } + else + { + out_en = ((cycles & 3) != 0) || test_dac; + /* Discrete YM3438 seems has the ladder effect too */ + if (out >= 0 && chip_type == ym3438_type_discrete) + { + out++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + } +} + +void OPN2_FMGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 19) % 24; + /* Calculate phase */ + Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; + Bit16u quarter; + Bit16u level; + Bit16s output; + if (phase & 0x100) + { + quarter = (phase ^ 0xff) & 0xff; + } + else + { + quarter = phase & 0xff; + } + level = logsinrom[quarter]; + /* Apply envelope */ + level += chip->eg_out[slot] << 2; + /* Transform */ + if (level > 0x1fff) + { + level = 0x1fff; + } + output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); + if (phase & 0x200) + { + output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; + } + else + { + output = output ^ (chip->mode_test_21[4] << 13); + } + output <<= 2; + output >>= 2; + chip->fm_out[slot] = output; +} + +void OPN2_DoTimerA(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_a_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_a_load_lock && chip->timer_a_load); + chip->timer_a_load_lock = chip->timer_a_load; + if (chip->mode_csm) + { + /* CSM KeyOn */ + chip->mode_kon_csm = load; + } + else + { + chip->mode_kon_csm = 0; + } + } + /* Load counter */ + if (chip->timer_a_load_latch) + { + time = chip->timer_a_reg; + } + else + { + time = chip->timer_a_cnt; + } + chip->timer_a_load_latch = load; + /* Increase counter */ + if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) + { + time++; + } + /* Set overflow flag */ + if (chip->timer_a_reset) + { + chip->timer_a_reset = 0; + chip->timer_a_overflow_flag = 0; + } + else + { + chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; + } + chip->timer_a_overflow = (time >> 10); + chip->timer_a_cnt = time & 0x3ff; +} + +void OPN2_DoTimerB(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_b_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_b_load_lock && chip->timer_b_load); + chip->timer_b_load_lock = chip->timer_b_load; + } + /* Load counter */ + if (chip->timer_b_load_latch) + { + time = chip->timer_b_reg; + } + else + { + time = chip->timer_b_cnt; + } + chip->timer_b_load_latch = load; + /* Increase counter */ + if (chip->cycles == 1) + { + chip->timer_b_subcnt++; + } + if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) + { + time++; + } + chip->timer_b_subcnt &= 0x0f; + /* Set overflow flag */ + if (chip->timer_b_reset) + { + chip->timer_b_reset = 0; + chip->timer_b_overflow_flag = 0; + } + else + { + chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; + } + chip->timer_b_overflow = (time >> 8); + chip->timer_b_cnt = time & 0xff; +} + +void OPN2_KeyOn(ym3438_t*chip) +{ + Bit32u slot = chip->cycles; + Bit32u chan = chip->channel; + /* Key On */ + chip->eg_kon_latch[slot] = chip->mode_kon[slot]; + chip->eg_kon_csm[slot] = 0; + if (chip->channel == 2 && chip->mode_kon_csm) + { + /* CSM Key On */ + chip->eg_kon_latch[slot] = 1; + chip->eg_kon_csm[slot] = 1; + } + if (chip->cycles == chip->mode_kon_channel) + { + /* OP1 */ + chip->mode_kon[chan] = chip->mode_kon_operator[0]; + /* OP2 */ + chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; + /* OP3 */ + chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; + /* OP4 */ + chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; + } +} + +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) +{ + Bit32u i, rateratio; + rateratio = (Bit32u)chip->rateratio; + memset(chip, 0, sizeof(ym3438_t)); + for (i = 0; i < 24; i++) + { + chip->eg_out[i] = 0x3ff; + chip->eg_level[i] = 0x3ff; + chip->eg_state[i] = eg_num_release; + chip->multi[i] = 1; + } + for (i = 0; i < 6; i++) + { + chip->pan_l[i] = 1; + chip->pan_r[i] = 1; + } + + if (rate != 0) + { + chip->rateratio = (Bit32s)(Bit32u)((((Bit64u)144 * rate) << RSM_FRAC) / clock); + } + else + { + chip->rateratio = (Bit32s)rateratio; + } +} + +void OPN2_SetChipType(Bit32u type) +{ + chip_type = type; +} + +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +{ + Bit32u slot = chip->cycles; + chip->lfo_inc = chip->mode_test_21[1]; + chip->pg_read >>= 1; + chip->eg_read[1] >>= 1; + chip->eg_cycle++; + /* Lock envelope generator timer value */ + if (chip->cycles == 1 && chip->eg_quotient == 2) + { + if (chip->eg_cycle_stop) + { + chip->eg_shift_lock = 0; + } + else + { + chip->eg_shift_lock = chip->eg_shift + 1; + } + chip->eg_timer_low_lock = chip->eg_timer & 0x03; + } + /* Cycle specific functions */ + switch (chip->cycles) + { + case 0: + chip->lfo_pm = chip->lfo_cnt >> 2; + if (chip->lfo_cnt & 0x40) + { + chip->lfo_am = chip->lfo_cnt & 0x3f; + } + else + { + chip->lfo_am = chip->lfo_cnt ^ 0x3f; + } + chip->lfo_am <<= 1; + break; + case 1: + chip->eg_quotient++; + chip->eg_quotient %= 3; + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer_inc |= chip->eg_quotient >> 1; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 2: + chip->pg_read = chip->pg_phase[21] & 0x3ff; + chip->eg_read[1] = chip->eg_out[0]; + break; + case 13: + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 23: + chip->lfo_inc |= 1; + break; + } + chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); + if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) + { + chip->eg_shift = chip->eg_cycle; + chip->eg_cycle_stop = 0; + } + + OPN2_DoIO(chip); + + OPN2_DoTimerA(chip); + OPN2_DoTimerB(chip); + OPN2_KeyOn(chip); + + OPN2_ChOutput(chip); + OPN2_ChGenerate(chip); + + OPN2_FMPrepare(chip); + OPN2_FMGenerate(chip); + + OPN2_PhaseGenerate(chip); + OPN2_PhaseCalcIncrement(chip); + + OPN2_EnvelopeADSR(chip); + OPN2_EnvelopeGenerate(chip); + OPN2_EnvelopeSSGEG(chip); + OPN2_EnvelopePrepare(chip); + + /* Prepare fnum & block */ + if (chip->mode_ch3) + { + /* Channel 3 special mode */ + switch (slot) + { + case 1: /* OP1 */ + chip->pg_fnum = chip->fnum_3ch[1]; + chip->pg_block = chip->block_3ch[1]; + chip->pg_kcode = chip->kcode_3ch[1]; + break; + case 7: /* OP3 */ + chip->pg_fnum = chip->fnum_3ch[0]; + chip->pg_block = chip->block_3ch[0]; + chip->pg_kcode = chip->kcode_3ch[0]; + break; + case 13: /* OP2 */ + chip->pg_fnum = chip->fnum_3ch[2]; + chip->pg_block = chip->block_3ch[2]; + chip->pg_kcode = chip->kcode_3ch[2]; + break; + case 19: /* OP4 */ + default: + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + break; + } + } + else + { + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + } + + OPN2_UpdateLFO(chip); + OPN2_DoRegWrite(chip); + chip->cycles = (chip->cycles + 1) % 24; + chip->channel = chip->cycles % 6; + + buffer[0] = chip->mol; + buffer[1] = chip->mor; +} + +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) +{ + port &= 3; + chip->write_data = ((port << 7) & 0x100) | data; + if (port & 1) + { + /* Data */ + chip->write_d |= 1; + } + else + { + /* Address */ + chip->write_a |= 1; + } +} + +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) +{ + chip->pin_test_in = value & 1; +} + +Bit32u OPN2_ReadTestPin(ym3438_t *chip) +{ + if (!chip->mode_test_2c[7]) + { + return 0; + } + return chip->cycles == 23; +} + +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) +{ + return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; +} + +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) +{ + if ((port & 3) == 0 || chip_type == ym3438_type_asic) + { + if (chip->mode_test_21[6]) + { + /* Read test data */ + Bit32u slot = (chip->cycles + 18) % 24; + Bit16u testdata = ((chip->pg_read & 0x01) << 15) + | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); + if (chip->mode_test_2c[4]) + { + testdata |= chip->ch_read & 0x1ff; + } + else + { + testdata |= chip->fm_out[slot] & 0x3fff; + } + if (chip->mode_test_21[7]) + { + return testdata & 0xff; + } + else + { + return testdata >> 8; + } + } + else + { + return (Bit8u)(chip->busy << 7) | (Bit8u)(chip->timer_b_overflow_flag << 1) + | (Bit8u)chip->timer_a_overflow_flag; + } + } + return 0; +} + +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) +{ + Bit64u time1, time2; + Bit16s buffer[2]; + Bit64u skip; + + if (chip->writebuf[chip->writebuf_last].port & 0x04) + { + OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + OPN2_Clock(chip, buffer); + } + } + + chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; +} + +void OPN2_Generate(ym3438_t *chip, Bit16s *buf) +{ + Bit32u i; + Bit16s buffer[2]; + Bit32u mute; + + buf[0] = 0; + buf[1] = 0; + + for (i = 0; i < 24; i++) + { + switch (chip->cycles >> 2) + { + case 0: /* Ch 2 */ + mute = chip->mute[1]; + break; + case 1: /* Ch 6, DAC */ + mute = chip->mute[5 + chip->dacen]; + break; + case 2: /* Ch 4 */ + mute = chip->mute[3]; + break; + case 3: /* Ch 1 */ + mute = chip->mute[0]; + break; + case 4: /* Ch 5 */ + mute = chip->mute[4]; + break; + case 5: /* Ch 3 */ + mute = chip->mute[2]; + break; + default: + mute = 0; + break; + } + OPN2_Clock(chip, buffer); + if (!mute) + { + buf[0] += buffer[0]; + buf[1] += buffer[1]; + } + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) + { + break; + } + chip->writebuf[chip->writebuf_cur].port &= 0x03; + OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } +} + +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf) +{ + Bit16s buffer[2]; + + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPN2_Generate(chip, buffer); + chip->samples[0] = buffer[0] * 11; + chip->samples[1] = buffer[1] * 11; + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)(((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio)>>1); + buf[1] = (Bit16s)(((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio)>>1); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ = buffer[0]; + *output++ = buffer[1]; + } +} + +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ += buffer[0]; + *output++ += buffer[1]; + } +} + + +void OPN2_SetOptions(Bit8u flags) +{ + switch ((flags >> 3) & 0x03) + { + case 0x00: /* YM2612 */ + default: + OPN2_SetChipType(ym3438_type_ym2612); + break; + case 0x01: /* ASIC YM3438 */ + OPN2_SetChipType(ym3438_type_asic); + break; + case 0x02: /* Discrete YM3438 */ + OPN2_SetChipType(ym3438_type_discrete); + break; + } +} + +void OPN2_SetMute(ym3438_t *chip, Bit32u mute) +{ + Bit32u i; + for (i = 0; i < 7; i++) + { + chip->mute[i] = (mute >> i) & 0x01; + } +} diff --git a/src/sound/opnmidi/chips/nuked/ym3438.h b/src/sound/opnmidi/chips/nuked/ym3438.h new file mode 100644 index 000000000..c277f087b --- /dev/null +++ b/src/sound/opnmidi/chips/nuked/ym3438.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.7 + */ + +#ifndef YM3438_H +#define YM3438_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*EXTRA*/ +#define RSM_FRAC 10 +#define OPN_WRITEBUF_SIZE 2048 +#define OPN_WRITEBUF_DELAY 15 + +enum { + ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */ + ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */ + ym3438_type_ym2612 = 2 /* YM2612 (MD1, MD2 VA2) */ +}; + +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +/*EXTRA*/ +typedef struct _opn2_writebuf { + Bit64u time; + Bit8u port; + Bit8u data; + Bit8u reserved[6]; +} opn2_writebuf; + +typedef struct +{ + Bit32u cycles; + Bit32u channel; + Bit16s mol, mor; + /* IO */ + Bit16u write_data; + Bit8u write_a; + Bit8u write_d; + Bit8u write_a_en; + Bit8u write_d_en; + Bit8u write_busy; + Bit8u write_busy_cnt; + Bit8u write_fm_address; + Bit8u write_fm_data; + Bit8u write_fm_mode_a; + Bit16u address; + Bit8u data; + Bit8u pin_test_in; + Bit8u pin_irq; + Bit8u busy; + /* LFO */ + Bit8u lfo_en; + Bit8u lfo_freq; + Bit8u lfo_pm; + Bit8u lfo_am; + Bit8u lfo_cnt; + Bit8u lfo_inc; + Bit8u lfo_quotient; + /* Phase generator */ + Bit16u pg_fnum; + Bit8u pg_block; + Bit8u pg_kcode; + Bit32u pg_inc[24]; + Bit32u pg_phase[24]; + Bit8u pg_reset[24]; + Bit32u pg_read; + /* Envelope generator */ + Bit8u eg_cycle; + Bit8u eg_cycle_stop; + Bit8u eg_shift; + Bit8u eg_shift_lock; + Bit8u eg_timer_low_lock; + Bit16u eg_timer; + Bit8u eg_timer_inc; + Bit16u eg_quotient; + Bit8u eg_custom_timer; + Bit8u eg_rate; + Bit8u eg_ksv; + Bit8u eg_inc; + Bit8u eg_ratemax; + Bit8u eg_sl[2]; + Bit8u eg_lfo_am; + Bit8u eg_tl[2]; + Bit8u eg_state[24]; + Bit16u eg_level[24]; + Bit16u eg_out[24]; + Bit8u eg_kon[24]; + Bit8u eg_kon_csm[24]; + Bit8u eg_kon_latch[24]; + Bit8u eg_csm_mode[24]; + Bit8u eg_ssg_enable[24]; + Bit8u eg_ssg_pgrst_latch[24]; + Bit8u eg_ssg_repeat_latch[24]; + Bit8u eg_ssg_hold_up_latch[24]; + Bit8u eg_ssg_dir[24]; + Bit8u eg_ssg_inv[24]; + Bit32u eg_read[2]; + Bit8u eg_read_inc; + /* FM */ + Bit16s fm_op1[6][2]; + Bit16s fm_op2[6]; + Bit16s fm_out[24]; + Bit16u fm_mod[24]; + /* Channel */ + Bit16s ch_acc[6]; + Bit16s ch_out[6]; + Bit16s ch_lock; + Bit8u ch_lock_l; + Bit8u ch_lock_r; + Bit16s ch_read; + /* Timer */ + Bit16u timer_a_cnt; + Bit16u timer_a_reg; + Bit8u timer_a_load_lock; + Bit8u timer_a_load; + Bit8u timer_a_enable; + Bit8u timer_a_reset; + Bit8u timer_a_load_latch; + Bit8u timer_a_overflow_flag; + Bit8u timer_a_overflow; + + Bit16u timer_b_cnt; + Bit8u timer_b_subcnt; + Bit16u timer_b_reg; + Bit8u timer_b_load_lock; + Bit8u timer_b_load; + Bit8u timer_b_enable; + Bit8u timer_b_reset; + Bit8u timer_b_load_latch; + Bit8u timer_b_overflow_flag; + Bit8u timer_b_overflow; + + /* Register set */ + Bit8u mode_test_21[8]; + Bit8u mode_test_2c[8]; + Bit8u mode_ch3; + Bit8u mode_kon_channel; + Bit8u mode_kon_operator[4]; + Bit8u mode_kon[24]; + Bit8u mode_csm; + Bit8u mode_kon_csm; + Bit8u dacen; + Bit16s dacdata; + + Bit8u ks[24]; + Bit8u ar[24]; + Bit8u sr[24]; + Bit8u dt[24]; + Bit8u multi[24]; + Bit8u sl[24]; + Bit8u rr[24]; + Bit8u dr[24]; + Bit8u am[24]; + Bit8u tl[24]; + Bit8u ssg_eg[24]; + + Bit16u fnum[6]; + Bit8u block[6]; + Bit8u kcode[6]; + Bit16u fnum_3ch[6]; + Bit8u block_3ch[6]; + Bit8u kcode_3ch[6]; + Bit8u reg_a4; + Bit8u reg_ac; + Bit8u connect[6]; + Bit8u fb[6]; + Bit8u pan_l[6], pan_r[6]; + Bit8u ams[6]; + Bit8u pms[6]; + + /*EXTRA*/ + Bit32u mute[7]; + Bit32s rateratio; + Bit32s samplecnt; + Bit32s oldsamples[2]; + Bit32s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; +} ym3438_t; + +/* EXTRA, original was "void OPN2_Reset(ym3438_t *chip)" */ +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock); +void OPN2_SetChipType(Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); +Bit32u OPN2_ReadTestPin(ym3438_t *chip); +Bit32u OPN2_ReadIRQPin(ym3438_t *chip); +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); + +/*EXTRA*/ +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_Generate(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_SetOptions(Bit8u flags); +void OPN2_SetMute(ym3438_t *chip, Bit32u mute); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sound/opnmidi/chips/nuked_opn2.cpp b/src/sound/opnmidi/chips/nuked_opn2.cpp new file mode 100644 index 000000000..e452ccf0f --- /dev/null +++ b/src/sound/opnmidi/chips/nuked_opn2.cpp @@ -0,0 +1,49 @@ +#include "nuked_opn2.h" +#include "nuked/ym3438.h" +#include + +NukedOPN2::NukedOPN2() +{ + OPN2_SetChipType(ym3438_type_asic); + chip = new ym3438_t; + setRate(m_rate, m_clock); +} + +NukedOPN2::~NukedOPN2() +{ + ym3438_t *chip_r = reinterpret_cast(chip); + delete chip_r; +} + +void NukedOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Reset(chip_r, rate, clock); +} + +void NukedOPN2::reset() +{ + OPNChipBaseT::reset(); + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Reset(chip_r, m_rate, m_clock); +} + +void NukedOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_WriteBuffered(chip_r, 0 + (port) * 2, (uint8_t)addr); + OPN2_WriteBuffered(chip_r, 1 + (port) * 2, data); + //qDebug() << QString("%1: 0x%2 => 0x%3").arg(port).arg(addr, 2, 16, QChar('0')).arg(data, 2, 16, QChar('0')); +} + +void NukedOPN2::nativeGenerate(int16_t *frame) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Generate(chip_r, frame); +} + +const char *NukedOPN2::emulatorName() +{ + return "Nuked OPN2"; +} diff --git a/src/sound/opnmidi/chips/nuked_opn2.h b/src/sound/opnmidi/chips/nuked_opn2.h new file mode 100644 index 000000000..9eb239f48 --- /dev/null +++ b/src/sound/opnmidi/chips/nuked_opn2.h @@ -0,0 +1,25 @@ +#ifndef NUKED_OPN2_H +#define NUKED_OPN2_H + +#include "opn_chip_base.h" + +class NukedOPN2 final : public OPNChipBaseT +{ + void *chip; +public: + NukedOPN2(); + ~NukedOPN2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + // amplitude scale factors to use in resampling + enum { resamplerPreAmplify = 11, resamplerPostAttenuate = 2 }; +}; + +#endif // NUKED_OPN2_H diff --git a/src/sound/opnmidi/chips/opn_chip_base.h b/src/sound/opnmidi/chips/opn_chip_base.h new file mode 100644 index 000000000..5c532c920 --- /dev/null +++ b/src/sound/opnmidi/chips/opn_chip_base.h @@ -0,0 +1,110 @@ +#ifndef ONP_CHIP_BASE_H +#define ONP_CHIP_BASE_H + +#include +#include + +#if !defined(_MSC_VER) && (__cplusplus <= 199711L) +#define final +#define override +#endif + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +class VResampler; +#endif + +class OPNChipBase +{ +public: + enum { nativeRate = 53267 }; +protected: + uint32_t m_rate; + uint32_t m_clock; +public: + OPNChipBase(); + virtual ~OPNChipBase(); + + virtual bool canRunAtPcmRate() const = 0; + virtual bool isRunningAtPcmRate() const = 0; + virtual bool setRunningAtPcmRate(bool r) = 0; + + virtual void setRate(uint32_t rate, uint32_t clock) = 0; + virtual void reset() = 0; + virtual void writeReg(uint32_t port, uint16_t addr, uint8_t data) = 0; + + virtual void nativePreGenerate() = 0; + virtual void nativePostGenerate() = 0; + virtual void nativeGenerate(int16_t *frame) = 0; + + virtual void generate(int16_t *output, size_t frames) = 0; + virtual void generateAndMix(int16_t *output, size_t frames) = 0; + virtual void generate32(int32_t *output, size_t frames) = 0; + virtual void generateAndMix32(int32_t *output, size_t frames) = 0; + + virtual const char* emulatorName() = 0; +private: + OPNChipBase(const OPNChipBase &c); + OPNChipBase &operator=(const OPNChipBase &c); +}; + +// A base class providing F-bounded generic and efficient implementations, +// supporting resampling of chip outputs +template +class OPNChipBaseT : public OPNChipBase +{ +public: + OPNChipBaseT(); + virtual ~OPNChipBaseT(); + + bool isRunningAtPcmRate() const override; + bool setRunningAtPcmRate(bool r) override; + + virtual void setRate(uint32_t rate, uint32_t clock) override; + virtual void reset() override; + void generate(int16_t *output, size_t frames) override; + void generateAndMix(int16_t *output, size_t frames) override; + void generate32(int32_t *output, size_t frames) override; + void generateAndMix32(int32_t *output, size_t frames) override; +private: + bool m_runningAtPcmRate; + void setupResampler(uint32_t rate); + void resetResampler(); + void resampledGenerate(int32_t *output); +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + VResampler *m_resampler; +#else + int32_t m_oldsamples[2]; + int32_t m_samples[2]; + int32_t m_samplecnt; + int32_t m_rateratio; + enum { rsm_frac = 10 }; +#endif + // amplitude scale factors in and out of resampler, varying for chips; + // values are OK to "redefine", the static polymorphism will accept it. + enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 }; +}; + +// A base class which provides frame-by-frame interfaces on emulations which +// don't have a routine for it. It produces outputs in fixed size buffers. +// Fast register updates will suffer some latency because of buffering. +template +class OPNChipBaseBufferedT : public OPNChipBaseT +{ +public: + OPNChipBaseBufferedT() + : OPNChipBaseT(), m_bufferIndex(0) {} + virtual ~OPNChipBaseBufferedT() + {} +public: + void reset() override; + void nativeGenerate(int16_t *frame) override; +protected: + virtual void nativeGenerateN(int16_t *output, size_t frames) = 0; +private: + unsigned m_bufferIndex; + int16_t m_buffer[2 * Buffer]; +}; + +#include "opn_chip_base.tcc" + +#endif // ONP_CHIP_BASE_H diff --git a/src/sound/opnmidi/chips/opn_chip_base.tcc b/src/sound/opnmidi/chips/opn_chip_base.tcc new file mode 100644 index 000000000..4915e802d --- /dev/null +++ b/src/sound/opnmidi/chips/opn_chip_base.tcc @@ -0,0 +1,268 @@ +#include "opn_chip_base.h" +#include + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +#include +#endif + +#if !defined(LIKELY) && defined(__GNUC__) +#define LIKELY(x) __builtin_expect((x), 1) +#elif !defined(LIKELY) +#define LIKELY(x) (x) +#endif + +#if !defined(UNLIKELY) && defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect((x), 0) +#elif !defined(UNLIKELY) +#define UNLIKELY(x) (x) +#endif + +/* OPNChipBase */ + +inline OPNChipBase::OPNChipBase() : + m_rate(44100), + m_clock(7670454) +{ +} + +inline OPNChipBase::~OPNChipBase() +{ +} + +/* OPNChipBaseT */ + +template +OPNChipBaseT::OPNChipBaseT() + : OPNChipBase(), + m_runningAtPcmRate(false) +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + m_resampler = new VResampler; +#endif + setupResampler(m_rate); +} + +template +OPNChipBaseT::~OPNChipBaseT() +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + delete m_resampler; +#endif +} + +template +bool OPNChipBaseT::isRunningAtPcmRate() const +{ + return m_runningAtPcmRate; +} + +template +bool OPNChipBaseT::setRunningAtPcmRate(bool r) +{ + if(r != m_runningAtPcmRate) + { + if(r && !static_cast(this)->canRunAtPcmRate()) + return false; + m_runningAtPcmRate = r; + static_cast(this)->setRate(m_rate, m_clock); + } + return true; +} + +template +void OPNChipBaseT::setRate(uint32_t rate, uint32_t clock) +{ + uint32_t oldRate = m_rate; + m_rate = rate; + m_clock = clock; + if(rate != oldRate) + setupResampler(rate); + else + resetResampler(); +} + +template +void OPNChipBaseT::reset() +{ + resetResampler(); +} + +template +void OPNChipBaseT::generate(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generateAndMix(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = (int32_t)output[c] + frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generate32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + static_cast(this)->resampledGenerate(output); + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generateAndMix32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + output[0] += frame[0]; + output[1] += frame[1]; + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::setupResampler(uint32_t rate) +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->setup(rate * (1.0 / 53267), 2, 48); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; + m_rateratio = (int32_t)(uint32_t)((((uint64_t)144 * rate) << rsm_frac) / m_clock); +#endif +} + +template +void OPNChipBaseT::resetResampler() +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->reset(); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; +#endif +} + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +template +void OPNChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeGenerate(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + VResampler *rsm = m_resampler; + float scale = (float)T::resamplerPreAmplify / + (float)T::resamplerPostAttenuate; + float f_in[2]; + float f_out[2]; + rsm->inp_count = 0; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + while(rsm->process(), rsm->out_count != 0) + { + int16_t in[2]; + static_cast(this)->nativeGenerate(in); + f_in[0] = scale * (float)in[0]; + f_in[1] = scale * (float)in[1]; + rsm->inp_count = 1; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + } + output[0] = std::lround(f_out[0]); + output[1] = std::lround(f_out[1]); +} +#else +template +void OPNChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeGenerate(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + int32_t samplecnt = m_samplecnt; + const int32_t rateratio = m_rateratio; + while(samplecnt >= rateratio) + { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + int16_t buffer[2]; + static_cast(this)->nativeGenerate(buffer); + m_samples[0] = buffer[0] * T::resamplerPreAmplify; + m_samples[1] = buffer[1] * T::resamplerPreAmplify; + samplecnt -= rateratio; + } + output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt) + + m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt) + + m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + m_samplecnt = samplecnt + (1 << rsm_frac); +} +#endif + +/* OPNChipBaseBufferedT */ + +template +void OPNChipBaseBufferedT::reset() +{ + OPNChipBaseT::reset(); + m_bufferIndex = 0; +} + +template +void OPNChipBaseBufferedT::nativeGenerate(int16_t *frame) +{ + unsigned bufferIndex = m_bufferIndex; + if(bufferIndex == 0) + static_cast(this)->nativeGenerateN(m_buffer, Buffer); + frame[0] = m_buffer[2 * bufferIndex]; + frame[1] = m_buffer[2 * bufferIndex + 1]; + bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0; + m_bufferIndex = bufferIndex; +} diff --git a/src/sound/opnmidi/fraction.hpp b/src/sound/opnmidi/fraction.hpp index 1ec10ab57..7aab95bd2 100644 --- a/src/sound/opnmidi/fraction.hpp +++ b/src/sound/opnmidi/fraction.hpp @@ -82,6 +82,10 @@ public: self &operator= (long double orig); }; +#ifdef _MSC_VER +#pragma warning(disable:4146) +#endif + template void fraction::Optim() { @@ -110,6 +114,10 @@ void fraction::Optim() //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); } +#ifdef _MSC_VER +#pragma warning(default:4146) +#endif + template inline const fraction abs(const fraction &f) { diff --git a/src/sound/opnmidi/opnbank.h b/src/sound/opnmidi/opnbank.h index fcf3abb77..521616ae1 100644 --- a/src/sound/opnmidi/opnbank.h +++ b/src/sound/opnmidi/opnbank.h @@ -21,6 +21,10 @@ * along with this program. If not, see . */ +#ifndef OPNMIDI_OPNBANK_H +#define OPNMIDI_OPNBANK_H + +#include #include #ifdef ADLMIDI_buildAsApp @@ -36,13 +40,25 @@ public: }; #endif +enum { opnNoteOnMaxTime = 40000 }; + /* *********** FM Operator indexes *********** */ -#define OPERATOR1 0 -#define OPERATOR2 1 -#define OPERATOR3 2 -#define OPERATOR4 3 +enum +{ + OPERATOR1 = 0, + OPERATOR2 = 1, + OPERATOR3 = 2, + OPERATOR4 = 3, +}; /* *********** FM Operator indexes *end******* */ +#pragma pack(push, 1) +#define OPNDATA_BYTE_COMPARABLE(T) \ + inline bool operator==(const T &a, const T &b) \ + { return !memcmp(&a, &b, sizeof(T)); } \ + inline bool operator!=(const T &a, const T &b) \ + { return !operator==(a, b); } + struct OPN_Operator { //! Raw register data @@ -58,6 +74,7 @@ struct OPN_Operator 6 - SSG-EG byte */ }; +OPNDATA_BYTE_COMPARABLE(struct OPN_Operator) struct opnInstData { @@ -68,6 +85,7 @@ struct opnInstData //! Note offset int16_t finetune; }; +OPNDATA_BYTE_COMPARABLE(struct opnInstData) struct opnInstMeta { @@ -79,3 +97,41 @@ struct opnInstMeta uint16_t ms_sound_koff; double fine_tune; }; +OPNDATA_BYTE_COMPARABLE(struct opnInstMeta) + +/** + * @brief Instrument data with operators included + */ +struct opnInstMeta2 +{ + opnInstData opn[2]; + uint8_t tone; + uint8_t flags; + uint16_t ms_sound_kon; // Number of milliseconds it produces sound; + uint16_t ms_sound_koff; + double fine_tune; +#if 0 + opnInstMeta2() {} + explicit opnInstMeta2(const opnInstMeta &d); +#endif +}; +OPNDATA_BYTE_COMPARABLE(struct opnInstMeta2) + +#undef OPNDATA_BYTE_COMPARABLE +#pragma pack(pop) + +#if 0 +/** + * @brief Conversion of storage formats + */ +inline opnInstMeta2::opnInstMeta2(const opnInstMeta &d) + : tone(d.tone), flags(d.flags), + ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), + fine_tune(d.fine_tune) +{ + opn[0] = ::opn[d.opnno1]; + opn[1] = ::opn[d.opnno2]; +} +#endif + +#endif // OPNMIDI_OPNBANK_H diff --git a/src/sound/opnmidi/opnmidi.cpp b/src/sound/opnmidi/opnmidi.cpp index 6657ee2f5..eab98eee7 100644 --- a/src/sound/opnmidi/opnmidi.cpp +++ b/src/sound/opnmidi/opnmidi.cpp @@ -32,6 +32,13 @@ static OPN2_Version opn2_version = { OPNMIDI_VERSION_PATCHLEVEL }; +static const OPNMIDI_AudioFormat opn2_DefaultAudioFormat = +{ + OPNMIDI_SampleType_S16, + sizeof(int16_t), + 2 * sizeof(int16_t), +}; + /*---------------------------EXPORTS---------------------------*/ OPNMIDI_EXPORT struct OPN2_MIDIPlayer *opn2_init(long sample_rate) @@ -129,7 +136,14 @@ OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod) if(!device) return; OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); play->m_setup.ScaleModulators = smod; - play->opn.ScaleModulators = play->m_setup.ScaleModulators; + play->opn.ScaleModulators = (play->m_setup.ScaleModulators != 0); +} + +OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness) +{ + if(!device) return; + OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); } OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn) @@ -139,12 +153,16 @@ OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn) play->m_setup.loopingIsEnabled = (loopEn != 0); } +/* !!!DEPRECATED!!! */ OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol) { if(!device) return; OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); play->m_setup.LogarithmicVolumes = static_cast(logvol); - play->opn.LogarithmicVolumes = play->m_setup.LogarithmicVolumes; + if(play->m_setup.LogarithmicVolumes != 0) + play->opn.ChangeVolumeRangesModel(OPNMIDI_VolumeModel_CMF); + else + play->opn.ChangeVolumeRangesModel(static_cast(play->m_setup.VolumeModel)); } OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volumeModel) @@ -209,16 +227,60 @@ OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsig OPNMIDI_EXPORT const char *opn2_emulatorName() { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - return "GENS 2.10 YM2612"; - #else - return "Nuked OPN2 YM3438"; - #endif + return ""; } +OPNMIDI_EXPORT const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device) +{ + if(device) + { + OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(play && !play->opn.cardsOP2.empty()) + return play->opn.cardsOP2[0]->emulatorName(); + } + return "Unknown"; +} + +OPNMIDI_EXPORT int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator) +{ + if(device) + { + OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(play && (emulator >= 0) && (emulator < OPNMIDI_EMU_end)) + { + play->m_setup.emulator = emulator; + opn2_reset(device); + return 0; + } + play->setErrorString("OPN2 MIDI: Unknown emulation core!"); + } + return -1; +} + + +OPNMIDI_EXPORT int opn2_setRunAtPcmRate(OPN2_MIDIPlayer *device, int enabled) +{ + if(device) + { + OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(play) + { + play->m_setup.runAtPcmRate = (enabled != 0); + opn2_reset(device); + return 0; + } + } + return -1; +} + + OPNMIDI_EXPORT const char *opn2_linkedLibraryVersion() { +#if !defined(OPNMIDI_ENABLE_HQ_RESAMPLER) return OPNMIDI_VERSION; +#else + return OPNMIDI_VERSION " (HQ)"; +#endif } OPNMIDI_EXPORT const OPN2_Version *opn2_linkedVersion() @@ -270,7 +332,8 @@ OPNMIDI_EXPORT void opn2_reset(OPN2_MIDIPlayer *device) return; OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); play->m_setup.tick_skip_samples_delay = 0; - play->opn.Reset(play->m_setup.PCM_RATE); + play->opn.runAtPcmRate = play->m_setup.runAtPcmRate; + play->opn.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE); play->ch.clear(); play->ch.resize(play->opn.NumChannels); } @@ -491,24 +554,139 @@ OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN } +template +static void CopySamplesRaw(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i]; + *(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1]; + } +} -inline static void SendStereoAudio(int &samples_requested, - ssize_t &in_size, - short *_in, - ssize_t out_pos, - short *_out) +template +static void CopySamplesTransformed(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = transform(src[2 * i]); + *(Dst *)(dstRight + (i * sampleOffset)) = transform(src[(2 * i) + 1]); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + OPN2_UInt8 *left, + OPN2_UInt8 *right, + const OPNMIDI_AudioFormat *format) { if(!in_size) - return; - size_t offset = static_cast(out_pos); + return 0; + size_t outputOffset = static_cast(out_pos); size_t inSamples = static_cast(in_size * 2); - size_t maxSamples = static_cast(samples_requested) - offset; + size_t maxSamples = static_cast(samples_requested) - outputOffset; size_t toCopy = std::min(maxSamples, inSamples); - std::memcpy(_out + out_pos, _in, toCopy * sizeof(short)); + + OPNMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + typedef int32_t(&pfnConvert)(int32_t); + + switch(sampleType) { + case OPNMIDI_SampleType_S8: + case OPNMIDI_SampleType_U8: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S8) ? opn2_cvtS8 : opn2_cvtU8; + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S16: + case OPNMIDI_SampleType_U16: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S16) ? opn2_cvtS16 : opn2_cvtU16; + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesRaw(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S24: + case OPNMIDI_SampleType_U24: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S24) ? opn2_cvtS24 : opn2_cvtU24; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S32: + case OPNMIDI_SampleType_U32: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S32) ? opn2_cvtS32 : opn2_cvtU32; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_F32: + if(containerSize != sizeof(float)) + return -1; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal); + break; + case OPNMIDI_SampleType_F64: + if(containerSize != sizeof(double)) + return -1; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal); + break; + default: + return -1; + } + + return 0; } -OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *out) +OPNMIDI_EXPORT int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out) +{ + return opn2_playFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat); +} + +OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, + OPN2_UInt8 *out_left, OPN2_UInt8 *out_right, + const OPNMIDI_AudioFormat *format) { #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER sampleCount -= sampleCount % 2; //Avoid even sample requests @@ -562,31 +740,20 @@ OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *ou ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opn.NumCards; if(chips == 1) - { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - player->opn.cardsOP2[0]->run(int(in_generatedStereo), out_buf); - #else - OPN2_GenerateStream(player->opn.cardsOP2[0], out_buf, (Bit32u)in_generatedStereo); - #endif - } + player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); else/* if(n_periodCountStereo > 0)*/ { /* Generate data from every chip and mix result */ - for(unsigned card = 0; card < chips; ++card) - { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - player->opn.cardsOP2[card]->run(int(in_generatedStereo), out_buf); - #else - OPN2_GenerateStreamMix(player->opn.cardsOP2[card], out_buf, (Bit32u)in_generatedStereo); - #endif - } + for(size_t card = 0; card < chips; ++card) + player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; @@ -609,6 +776,13 @@ OPNMIDI_EXPORT int opn2_play(OPN2_MIDIPlayer *device, int sampleCount, short *ou OPNMIDI_EXPORT int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out) +{ + return opn2_generateFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat); +} + +OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, + OPN2_UInt8 *out_left, OPN2_UInt8 *out_right, + const OPNMIDI_AudioFormat *format) { sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) @@ -644,31 +818,20 @@ OPNMIDI_EXPORT int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opn.NumCards; if(chips == 1) - { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - player->opn.cardsOP2[0]->run(int(in_generatedStereo), out_buf); - #else - OPN2_GenerateStream(player->opn.cardsOP2[0], out_buf, (Bit32u)in_generatedStereo); - #endif - } + player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); else/* if(n_periodCountStereo > 0)*/ { /* Generate data from every chip and mix result */ - for(unsigned card = 0; card < chips; ++card) - { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - player->opn.cardsOP2[card]->run(int(in_generatedStereo), out_buf); - #else - OPN2_GenerateStreamMix(player->opn.cardsOP2[card], out_buf, (Bit32u)in_generatedStereo); - #endif - } + for(size_t card = 0; card < chips; ++card) + player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; diff --git a/src/sound/opnmidi/opnmidi.h b/src/sound/opnmidi/opnmidi.h index f9c61d648..29ac58f86 100644 --- a/src/sound/opnmidi/opnmidi.h +++ b/src/sound/opnmidi/opnmidi.h @@ -29,10 +29,11 @@ extern "C" { #endif #define OPNMIDI_VERSION_MAJOR 1 -#define OPNMIDI_VERSION_MINOR 1 -#define OPNMIDI_VERSION_PATCHLEVEL 1 +#define OPNMIDI_VERSION_MINOR 3 +#define OPNMIDI_VERSION_PATCHLEVEL 0 -#define OPNMIDI_TOSTR(s) #s +#define OPNMIDI_TOSTR_I(s) #s +#define OPNMIDI_TOSTR(s) OPNMIDI_TOSTR_I(s) #define OPNMIDI_VERSION \ OPNMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \ OPNMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \ @@ -63,6 +64,28 @@ enum OPNMIDI_VolumeModels OPNMIDI_VolumeModel_9X }; +enum OPNMIDI_SampleType +{ + OPNMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */ + OPNMIDI_SampleType_S8, /* signed PCM 8-bit */ + OPNMIDI_SampleType_F32, /* float 32-bit */ + OPNMIDI_SampleType_F64, /* float 64-bit */ + OPNMIDI_SampleType_S24, /* signed PCM 24-bit */ + OPNMIDI_SampleType_S32, /* signed PCM 32-bit */ + OPNMIDI_SampleType_U8, /* unsigned PCM 8-bit */ + OPNMIDI_SampleType_U16, /* unsigned PCM 16-bit */ + OPNMIDI_SampleType_U24, /* unsigned PCM 24-bit */ + OPNMIDI_SampleType_U32, /* unsigned PCM 32-bit */ + OPNMIDI_SampleType_Count, +}; + +struct OPNMIDI_AudioFormat +{ + enum OPNMIDI_SampleType type; /* type of sample */ + unsigned containerSize; /* size in bytes of the storage type */ + unsigned sampleOffset; /* distance in bytes between consecutive samples */ +}; + struct OPN2_MIDIPlayer { void *opn2_midiPlayer; @@ -80,10 +103,16 @@ extern int opn2_getNumChips(struct OPN2_MIDIPlayer *device); /*Enable or disable Enables scaling of modulator volumes*/ extern void opn2_setScaleModulators(struct OPN2_MIDIPlayer *device, int smod); +/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling. + By default, brightness affects sound between 0 and 64. + When this option is enabled, the range will use a full range from 0 up to 127. +*/ +extern void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness); + /*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/ extern void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn); -/*Enable or disable Logariphmic volume changer (-1 sets default per bank, 0 disable, 1 enable) */ +/* !!!DEPRECATED!!! */ extern void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol); /*Set different volume range model */ @@ -96,15 +125,33 @@ extern int opn2_openBankFile(struct OPN2_MIDIPlayer *device, const char *filePat extern int opn2_openBankData(struct OPN2_MIDIPlayer *device, const void *mem, long size); -/*Returns chip emulator name string*/ +/* DEPRECATED */ extern const char *opn2_emulatorName(); +/*Returns chip emulator name string*/ +extern const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device); + +enum Opn2_Emulator +{ + OPNMIDI_EMU_MAME = 0, + OPNMIDI_EMU_NUKED, + OPNMIDI_EMU_GENS, + OPNMIDI_EMU_GX, + OPNMIDI_EMU_end +}; + +/* Switch the emulation core */ +extern int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator); + typedef struct { OPN2_UInt16 major; OPN2_UInt16 minor; OPN2_UInt16 patch; } OPN2_Version; +/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/ +extern int opn2_setRunAtPcmRate(struct OPN2_MIDIPlayer *device, int enabled); + /*Returns string which contains a version number*/ extern const char *opn2_linkedLibraryVersion(); @@ -186,11 +233,17 @@ extern struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, s /*Take a sample buffer and iterate MIDI timers */ -extern int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short out[]); +extern int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); + +/*Take a sample buffer and iterate MIDI timers */ +extern int opn2_playFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); /*Generate audio output from chip emulators without iteration of MIDI timers.*/ extern int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); +/*Generate audio output from chip emulators without iteration of MIDI timers.*/ +extern int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); + /** * @brief Periodic tick handler. * @param device diff --git a/src/sound/opnmidi/opnmidi_bankmap.h b/src/sound/opnmidi/opnmidi_bankmap.h new file mode 100644 index 000000000..eb05566bc --- /dev/null +++ b/src/sound/opnmidi/opnmidi_bankmap.h @@ -0,0 +1,127 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_BANKMAP_H +#define OPNMIDI_BANKMAP_H + +#include +#include +#include +#include + +#include "opnmidi_ptr.hpp" + +/** + * A simple hash map which accepts bank numbers as keys, can be reserved to a + * fixed size, offers O(1) search and insertion, has a hash function to + * optimize for the worst case, and has some good cache locality properties. + */ +template +class BasicBankMap +{ +public: + typedef uint16_t key_type; /* the bank identifier */ + typedef T mapped_type; + typedef std::pair value_type; + + BasicBankMap(); + void reserve(size_t capacity); + + size_t size() const + { return m_size; } + size_t capacity() const + { return m_capacity; } + bool empty() const + { return m_size == 0; } + + class iterator; + iterator begin() const; + iterator end() const; + + struct do_not_expand_t {}; + + iterator find(key_type key); + void erase(iterator it); + std::pair insert(const value_type &value); + std::pair insert(const value_type &value, do_not_expand_t); + void clear(); + + T &operator[](key_type key); + +private: + struct Slot; + enum { minimum_allocation = 4 }; + enum + { + hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ + hash_buckets = 1 << hash_bits, + }; + +public: + class iterator + { + public: + iterator(); + value_type &operator*() const { return slot->value; } + value_type *operator->() const { return &slot->value; } + iterator &operator++(); + bool operator==(const iterator &o) const; + bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); + private: + Slot **buckets; + Slot *slot; + size_t index; + iterator(Slot **buckets, Slot *slot, size_t index); +#ifdef _MSC_VER + template + friend class BasicBankMap; +#else + friend class BasicBankMap; +#endif + }; + +private: + struct Slot { + Slot *next, *prev; + value_type value; + Slot() : next(NULL), prev(NULL) {} + }; + AdlMIDI_SPtrArray m_buckets; + std::list< AdlMIDI_SPtrArray > m_allocations; + Slot *m_freeslots; + size_t m_size; + size_t m_capacity; + static size_t hash(key_type key); + Slot *allocate_slot(); + Slot *ensure_allocate_slot(); + void free_slot(Slot *slot); + Slot *bucket_find(size_t index, key_type key); + void bucket_add(size_t index, Slot *slot); + void bucket_remove(size_t index, Slot *slot); +}; + +#include "opnmidi_bankmap.tcc" + +#endif // OPNMIDI_BANKMAP_H diff --git a/src/sound/opnmidi/opnmidi_bankmap.tcc b/src/sound/opnmidi/opnmidi_bankmap.tcc new file mode 100644 index 000000000..80ced116b --- /dev/null +++ b/src/sound/opnmidi/opnmidi_bankmap.tcc @@ -0,0 +1,283 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_bankmap.h" +#include + +template +inline BasicBankMap::BasicBankMap() + : m_freeslots(NULL), + m_size(0), + m_capacity(0) +{ + m_buckets.reset(new Slot *[hash_buckets]()); +} + +template +inline size_t BasicBankMap::hash(key_type key) +{ + // disregard the 0 high bit in LSB + key = (key & 127) | ((key >> 8) << 7); + // take low part as hash value + return key & (hash_buckets - 1); +} + +template +void BasicBankMap::reserve(size_t capacity) +{ + if(m_capacity >= capacity) + return; + + size_t need = capacity - m_capacity; + const size_t minalloc = static_cast(minimum_allocation); + need = (need < minalloc) ? minalloc : need; + + AdlMIDI_SPtrArray slotz; + slotz.reset(new Slot[need]); + m_allocations.push_back(slotz); + m_capacity += need; + + for(size_t i = need; i-- > 0;) + free_slot(&slotz[i]); +} + +template +typename BasicBankMap::iterator +BasicBankMap::begin() const +{ + iterator it(m_buckets.get(), NULL, 0); + while(it.index < hash_buckets && !(it.slot = m_buckets[it.index])) + ++it.index; + return it; +} + +template +typename BasicBankMap::iterator +BasicBankMap::end() const +{ + iterator it(m_buckets.get(), NULL, hash_buckets); + return it; +} + +template +typename BasicBankMap::iterator BasicBankMap::find(key_type key) +{ + size_t index = hash(key); + Slot *slot = bucket_find(index, key); + if(!slot) + return end(); + return iterator(m_buckets.get(), slot, index); +} + +template +void BasicBankMap::erase(iterator it) +{ + bucket_remove(it.index, it.slot); + free_slot(it.slot); + --m_size; +} + +template +inline BasicBankMap::iterator::iterator() + : buckets(NULL), slot(NULL), index(0) +{ +} + +template +inline BasicBankMap::iterator::iterator(Slot **buckets, Slot *slot, size_t index) + : buckets(buckets), slot(slot), index(index) +{ +} + +template +typename BasicBankMap::iterator & +BasicBankMap::iterator::operator++() +{ + if(slot->next) + slot = slot->next; + else { + Slot *slot = NULL; + ++index; + while(index < hash_buckets && !(slot = buckets[index])) + ++index; + this->slot = slot; + } + return *this; +} + +template +bool BasicBankMap::iterator::operator==(const iterator &o) const +{ + return buckets == o.buckets && slot == o.slot && index == o.index; +} + +template +inline bool BasicBankMap::iterator::operator!=(const iterator &o) const +{ + return !operator==(o); +} + +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) { + reserve(m_capacity + minimum_allocation); + slot = ensure_allocate_slot(); + } + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value, do_not_expand_t) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) + return std::make_pair(end(), false); + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +void BasicBankMap::clear() +{ + for(size_t i = 0; i < hash_buckets; ++i) { + Slot *slot = m_buckets[i]; + while (Slot *cur = slot) { + slot = slot->next; + free_slot(cur); + } + m_buckets[i] = NULL; + } + m_size = 0; +} + +template +inline T &BasicBankMap::operator[](key_type key) +{ + return insert(value_type(key, T())).first->second; +} + +template +typename BasicBankMap::Slot * +BasicBankMap::allocate_slot() +{ + Slot *slot = m_freeslots; + if(!slot) + return NULL; + Slot *next = slot->next; + if(next) + next->prev = NULL; + m_freeslots = next; + return slot; +} + +template +inline typename BasicBankMap::Slot * +BasicBankMap::ensure_allocate_slot() +{ + Slot *slot = allocate_slot(); + assert(slot); + return slot; +} + +template +void BasicBankMap::free_slot(Slot *slot) +{ + Slot *next = m_freeslots; + if(next) + next->prev = slot; + slot->prev = NULL; + slot->next = next; + m_freeslots = slot; + m_freeslots->value.second = T(); +} + +template +typename BasicBankMap::Slot * +BasicBankMap::bucket_find(size_t index, key_type key) +{ + Slot *slot = m_buckets[index]; + while(slot && slot->value.first != key) + slot = slot->next; + return slot; +} + +template +void BasicBankMap::bucket_add(size_t index, Slot *slot) +{ + assert(slot); + Slot *next = m_buckets[index]; + if(next) + next->prev = slot; + slot->next = next; + m_buckets[index] = slot; +} + +template +void BasicBankMap::bucket_remove(size_t index, Slot *slot) +{ + assert(slot); + Slot *prev = slot->prev; + Slot *next = slot->next; + if(!prev) + m_buckets[index] = next; + else + prev->next = next; + if(next) + next->prev = prev; +} diff --git a/src/sound/opnmidi/opnmidi_load.cpp b/src/sound/opnmidi/opnmidi_load.cpp index 15d0a5d35..c52b8093c 100644 --- a/src/sound/opnmidi/opnmidi_load.cpp +++ b/src/sound/opnmidi/opnmidi_load.cpp @@ -161,7 +161,7 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) uint16_t version = 1; uint16_t count_melodic_banks = 1; - uint16_t count_percusive_banks = 1; + uint16_t count_percussive_banks = 1; if(fr.read(magic, 1, 11) != 11) { @@ -194,15 +194,14 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) } } - opn.dynamic_instruments.clear(); - opn.dynamic_metainstruments.clear(); - if((readU16BE(fr, count_melodic_banks) != 2) || (readU16BE(fr, count_percusive_banks) != 2)) + opn.cleanInstrumentBanks(); + if((readU16BE(fr, count_melodic_banks) != 2) || (readU16BE(fr, count_percussive_banks) != 2)) { errorStringOut = "Can't load bank file: Can't read count of banks!"; return false; } - if((count_melodic_banks < 1) || (count_percusive_banks < 1)) + if((count_melodic_banks < 1) || (count_percussive_banks < 1)) { errorStringOut = "Custom bank: Too few banks in this file!"; return false; @@ -214,8 +213,10 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) return false; } - opn.dynamic_melodic_banks.clear(); - opn.dynamic_percussion_banks.clear(); + opn.cleanInstrumentBanks(); + + std::vector banks; + banks.reserve(count_melodic_banks + count_percussive_banks); if(version >= 2)//Read bank meta-entries { @@ -224,49 +225,44 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) uint8_t bank_meta[34]; if(fr.read(bank_meta, 1, 34) != 34) { - opn.dynamic_melodic_banks.clear(); + opn.cleanInstrumentBanks(); errorStringOut = "Custom bank: Fail to read melodic bank meta-data!"; return false; } - uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); - size_t offset = opn.dynamic_melodic_banks.size(); - opn.dynamic_melodic_banks[bank] = offset; - //strncpy(bankMeta.name, char_p(bank_meta), 32); + uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); + OPN2::Bank &bank = opn.dynamic_banks[bankno]; + //strncpy(bank.name, char_p(bank_meta), 32); + banks.push_back(&bank); } - for(uint16_t i = 0; i < count_percusive_banks; i++) + for(uint16_t i = 0; i < count_percussive_banks; i++) { uint8_t bank_meta[34]; if(fr.read(bank_meta, 1, 34) != 34) { - opn.dynamic_melodic_banks.clear(); - opn.dynamic_percussion_banks.clear(); + opn.cleanInstrumentBanks(); errorStringOut = "Custom bank: Fail to read percussion bank meta-data!"; return false; } - uint16_t bank = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); - size_t offset = opn.dynamic_percussion_banks.size(); - opn.dynamic_percussion_banks[bank] = offset; - //strncpy(bankMeta.name, char_p(bank_meta), 32); + uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]) + OPN2::PercussionTag; + OPN2::Bank &bank = opn.dynamic_banks[bankno]; + //strncpy(bank.name, char_p(bank_meta), 32); + banks.push_back(&bank); } } - opn.dynamic_percussion_offset = count_melodic_banks * 128; - uint16_t total = 128 * count_melodic_banks + 128 * count_percusive_banks; + size_t total = 128 * opn.dynamic_banks.size(); - for(uint16_t i = 0; i < total; i++) + for(size_t i = 0; i < total; i++) { - opnInstData data; - opnInstMeta meta; + opnInstMeta2 &meta = banks[i / 128]->ins[i % 128]; + opnInstData &data = meta.opn[0]; uint8_t idata[WOPL_INST_SIZE_V2]; size_t readSize = version >= 2 ? WOPL_INST_SIZE_V2 : WOPL_INST_SIZE_V1; if(fr.read(idata, 1, readSize) != readSize) { - opn.dynamic_instruments.clear(); - opn.dynamic_metainstruments.clear(); - opn.dynamic_melodic_banks.clear(); - opn.dynamic_percussion_banks.clear(); + opn.cleanInstrumentBanks(); errorStringOut = "Can't load bank file: Failed to read instrument data"; return false; } @@ -295,15 +291,11 @@ bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) meta.ms_sound_koff = 500; } - meta.opnno1 = uint16_t(opn.dynamic_instruments.size()); - meta.opnno2 = uint16_t(opn.dynamic_instruments.size()); + meta.opn[1] = meta.opn[0]; /* Junk, delete later */ meta.fine_tune = 0.0; /* Junk, delete later */ - - opn.dynamic_instruments.push_back(data); - opn.dynamic_metainstruments.push_back(meta); } applySetup(); @@ -336,7 +328,7 @@ bool OPNMIDIplay::LoadMIDI(OPNMIDIplay::fileReader &fr) AdlMIDI_CPtr cvt_buf; errorString.clear(); - if(opn.dynamic_instruments.empty()) + if(opn.dynamic_banks.empty()) { errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; return false; @@ -470,7 +462,6 @@ riffskip: fr.seek(0x7D, SEEK_SET); TrackCount = 1; DeltaTicks = 60; - opn.LogarithmicVolumes = true; //opl.CartoonersVolumes = true; opn.m_musicMode = OPN2::MODE_RSXX; opn.m_volumeScale = OPN2::VOLUME_CMF; @@ -494,11 +485,8 @@ riffskip: TrackData.clear(); TrackData.resize(TrackCount, std::vector()); - //CurrentPosition.track.clear(); - //CurrentPosition.track.resize(TrackCount); InvDeltaTicks = fraction(1, 1000000l * static_cast(DeltaTicks)); - //Tempo = 1000000l * InvDeltaTicks; - Tempo = fraction(1, static_cast(DeltaTicks)); + Tempo = fraction(1, static_cast(DeltaTicks) * 2); static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00}; size_t totalGotten = 0; @@ -560,14 +548,14 @@ riffskip: return false; } - //Build new MIDI events table (ALPHA!!!) + //Build new MIDI events table if(!buildTrackData()) { errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; return false; } - opn.Reset(m_setup.PCM_RATE); // Reset AdLib + opn.Reset(m_setup.emulator, m_setup.PCM_RATE); // Reset OPN2 chip ch.clear(); ch.resize(opn.NumChannels); return true; diff --git a/src/sound/opnmidi/opnmidi_midiplay.cpp b/src/sound/opnmidi/opnmidi_midiplay.cpp index cd77429d8..5a93bb36e 100644 --- a/src/sound/opnmidi/opnmidi_midiplay.cpp +++ b/src/sound/opnmidi/opnmidi_midiplay.cpp @@ -77,18 +77,19 @@ inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) void OPNMIDIplay::OpnChannel::AddAge(int64_t ms) { - if(users.empty()) + const int64_t neg = static_cast(-0x1FFFFFFFl); + if(users_empty()) koff_time_until_neglible = - std::max(int64_t(koff_time_until_neglible - ms), static_cast(-0x1FFFFFFFl)); + std::max(int64_t(koff_time_until_neglible - ms), neg); else { koff_time_until_neglible = 0; - for(users_t::iterator i = users.begin(); i != users.end(); ++i) + for(LocationData *i = users_first; i; i = i->next) { - i->second.kon_time_until_neglible = - std::max(i->second.kon_time_until_neglible - ms, static_cast(-0x1FFFFFFFl)); - i->second.vibdelay += ms; + if(!i->fixed_sustain) + i->kon_time_until_neglible = std::max(i->kon_time_until_neglible - ms, neg); + i->vibdelay += ms; } } } @@ -655,9 +656,10 @@ bool OPNMIDIplay::buildTrackData() #endif //OPNMIDI_DISABLE_MIDI_SEQUENCER -OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) +OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) : + m_arpeggioCounter(0) #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - : fullSongTimeLength(0.0), + , fullSongTimeLength(0.0), postSongWaitDelay(1.0), loopStartTime(-1.0), loopEndTime(-1.0), @@ -670,6 +672,9 @@ OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) { devices.clear(); + m_setup.emulator = OPNMIDI_EMU_MAME; + m_setup.runAtPcmRate = false; + m_setup.PCM_RATE = sampleRate; m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; @@ -681,6 +686,7 @@ OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) //m_setup.SkipForward = 0; m_setup.loopingIsEnabled = false; m_setup.ScaleModulators = 0; + m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; @@ -694,18 +700,24 @@ void OPNMIDIplay::applySetup() { m_setup.tick_skip_samples_delay = 0; - opn.ScaleModulators = m_setup.ScaleModulators; - opn.LogarithmicVolumes = m_setup.LogarithmicVolumes; + opn.ScaleModulators = (m_setup.ScaleModulators != 0); + opn.runAtPcmRate = m_setup.runAtPcmRate; opn.m_musicMode = OPN2::MODE_MIDI; - opn.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); + if(m_setup.LogarithmicVolumes != 0) + opn.ChangeVolumeRangesModel(OPNMIDI_VolumeModel_CMF); + else + opn.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); if(m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO) opn.m_volumeScale = OPN2::VOLUME_Generic; opn.NumCards = m_setup.NumCards; - opn.Reset(m_setup.PCM_RATE); + opn.Reset(m_setup.emulator, m_setup.PCM_RATE); ch.clear(); - ch.resize(opn.NumChannels); + ch.resize(opn.NumChannels, OpnChannel()); + + // Reset the arpeggio counter + m_arpeggioCounter = 0; } uint64_t OPNMIDIplay::ReadVarLen(uint8_t **ptr) @@ -898,20 +910,12 @@ void OPNMIDIplay::realTime_ResetState() for(size_t ch = 0; ch < Ch.size(); ch++) { MIDIchannel &chan = Ch[ch]; + chan.resetAllControllers(); chan.volume = (opn.m_musicMode == OPN2::MODE_RSXX) ? 127 : 100; - chan.expression = 127; - chan.panning = 0xC0; - chan.vibrato = 0; - chan.sustain = 0; - chan.bend = 0.0; - chan.bendsense = 2 / 8192.0; chan.vibpos = 0.0; - chan.vibdepth = 0.5 / 127.0; - chan.vibdelay = 0; chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; - chan.brightness = 127; NoteUpdate_All(uint16_t(ch), Upd_All); NoteUpdate_All(uint16_t(ch), Upd_Off); } @@ -919,13 +923,16 @@ void OPNMIDIplay::realTime_ResetState() bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { + if(note >= 127) + note = 127; + if((opn.m_musicMode == OPN2::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch - MIDIchannel::activenoteiterator i = Ch[channel].activenotes.find(note); - if(i != Ch[channel].activenotes.end()) + MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(i) { - i->second.vol = velocity; + i->vol = velocity; NoteUpdate(channel, i, Upd_Volume); return false; } @@ -941,14 +948,16 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(velocity == 0) return false; - size_t midiins = Ch[channel].patch; + MIDIchannel &midiChan = Ch[channel]; + + size_t midiins = midiChan.patch; bool isPercussion = (channel % 16 == 9); bool isXgPercussion = false; uint16_t bank = 0; - if(Ch[channel].bank_msb || Ch[channel].bank_lsb) + if(midiChan.bank_msb || midiChan.bank_lsb) { - bank = (uint16_t(Ch[channel].bank_msb) * 256) + uint16_t(Ch[channel].bank_lsb); + bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) //0x7F00 - XG Percussion channel (16256 signed decimal) if(bank == 0x7E00 || bank == 0x7F00) @@ -956,7 +965,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) //Let XG Percussion bank will use (0...127 range in WOPN file) bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); // MIDI instrument defines the patch - midiins = opn.dynamic_percussion_offset + note; // Percussion instrument + midiins = note; // Percussion instrument isXgPercussion = true; isPercussion = false; } @@ -965,43 +974,43 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(isPercussion) { bank = (uint16_t)midiins; // MIDI instrument defines the patch - midiins = opn.dynamic_percussion_offset + note; // Percussion instrument + midiins = note; // Percussion instrument } + if(isPercussion || isXgPercussion) + bank += OPN2::PercussionTag; + + const opnInstMeta2 *ains = &OPN2::emptyInstrument; //Set bank bank - if(bank > 0) + const OPN2::Bank *bnk = NULL; + if((bank & ~(uint16_t)OPN2::PercussionTag) > 0) { - if(isPercussion || isXgPercussion) + OPN2::BankMap::iterator b = opn.dynamic_banks.find(bank); + if(b != opn.dynamic_banks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + else if(hooks.onDebugMessage) { - OPN2::BankMap::iterator b = opn.dynamic_percussion_banks.find(bank); - if(b != opn.dynamic_percussion_banks.end()) - midiins += b->second * 128; - else - if(hooks.onDebugMessage) - { - if(!caugh_missing_banks_melodic.count(bank)) - { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing percussion MIDI bank %i (patch %i)", channel, bank, midiins); - caugh_missing_banks_melodic.insert(bank); - } - } - } - else - { - OPN2::BankMap::iterator b = opn.dynamic_melodic_banks.find(bank); - if(b != opn.dynamic_melodic_banks.end()) - midiins += b->second * 128; - else - if(hooks.onDebugMessage) - { - if(!caugh_missing_banks_melodic.count(bank)) - { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing melodic MIDI bank %i (patch %i)", channel, bank, midiins); - caugh_missing_banks_melodic.insert(bank); - } - } + std::set &missing = (isPercussion || isXgPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion || isXgPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); } } + //Or fall back to first bank + if(ains->flags & opnInstMeta::Flag_NoSound) + { + OPN2::BankMap::iterator b = opn.dynamic_banks.find(bank & OPN2::PercussionTag); + if(b != opn.dynamic_banks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + } /* if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50)) @@ -1011,8 +1020,6 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit */ //if(midiins == 56) vol = vol*6/10; // HACK - size_t meta = opn.GetAdlMetaNumber(midiins); - const opnInstMeta *ains = &opn.GetAdlMetaIns(meta); int16_t tone = note; if(!isPercussion && !isXgPercussion && (bank > 0)) // For non-zero banks @@ -1023,14 +1030,12 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit { if(!caugh_missing_instruments.count(static_cast(midiins))) { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, midiChan.patch, midiins, bank); caugh_missing_instruments.insert(static_cast(midiins)); } } bank = 0; - midiins = Ch[channel].patch; - meta = opn.GetAdlMetaNumber(midiins); - ains = &opn.GetAdlMetaIns(meta); + midiins = midiChan.patch; } } @@ -1045,7 +1050,10 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit tone -= ains->tone - 128; } - uint16_t i[2] = { ains->opnno1, ains->opnno2 }; + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = { + {0, ains->opn[0], /*false*/}, + {0, ains->opn[1], /*pseudo_4op*/}, + }; //bool pseudo_4op = ains.flags & opnInstMeta::Flag_Pseudo8op; //if((opn.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0]; @@ -1059,13 +1067,13 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit } // Allocate AdLib channel (the physical sound channel for the note) - int32_t adlchannel[2] = { -1, -1 }; + int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; - for(uint32_t ccount = 0; ccount < 2; ++ccount) + for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { if(ccount == 1) { - if(i[0] == i[1]) + if(voices[0] == voices[1]) break; // No secondary channel if(adlchannel[0] == -1) break; // No secondary if primary failed @@ -1079,7 +1087,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary // ===== Kept for future pseudo-8-op mode - //if(i[0] == i[1] || pseudo_4op) + //if(voices[0] == voices[1] || pseudo_4op) //{ // // Only use regular channels // uint8_t expected_mode = 0; @@ -1093,7 +1101,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit // if(opn.four_op_category[a] != expected_mode) // continue; //} - int64_t s = CalculateAdlChannelGoodness(a, i[ccount], channel); + int64_t s = CalculateAdlChannelGoodness(a, voices[ccount], channel); if(s > bs) { bs = (int32_t)s; // Best candidate wins @@ -1106,11 +1114,11 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "ignored unplaceable note [bank %i, inst %i, note %i, MIDI channel %i]", - bank, Ch[channel].patch, note, channel); + bank, midiChan.patch, note, channel); continue; // Could not play this note. Ignore it. } - PrepareAdlChannelForNewNote(static_cast(c), i[ccount]); + PrepareAdlChannelForNewNote(static_cast(c), voices[ccount]); adlchannel[ccount] = c; } @@ -1125,19 +1133,21 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit // Allocate active note for MIDI channel std::pair - ir = Ch[channel].activenotes.insert(std::make_pair(note, MIDIchannel::NoteInfo())); - ir.first->second.vol = velocity; - ir.first->second.tone = tone; - ir.first->second.midiins = midiins; - ir.first->second.insmeta = meta; + ir = midiChan.activenotes_insert(note); + ir.first->vol = velocity; + ir.first->vibrato = midiChan.noteAftertouch[note]; + ir.first->tone = tone; + ir.first->midiins = midiins; + ir.first->ains = ains; + ir.first->chip_channels_count = 0; - for(unsigned ccount = 0; ccount < 2; ++ccount) + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; if(c < 0) continue; uint16_t chipChan = static_cast(adlchannel[ccount]); - ir.first->second.phys[chipChan] = i[ccount]; + ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); return true; @@ -1152,31 +1162,28 @@ void OPNMIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) void OPNMIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) { channel = channel % 16; - MIDIchannel::activenoteiterator - i = Ch[channel].activenotes.find(note); - if(i == Ch[channel].activenotes.end()) + MIDIchannel &chan = Ch[channel]; + MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(i) { - // Ignore touch if note is not active - return; + i->vibrato = atVal; + } + + uint8_t oldAtVal = chan.noteAftertouch[note % 128]; + if(atVal != oldAtVal) + { + chan.noteAftertouch[note % 128] = atVal; + bool inUse = atVal != 0; + for(unsigned n = 0; !inUse && n < 128; ++n) + inUse = chan.noteAftertouch[n] != 0; + chan.noteAfterTouchInUse = inUse; } - i->second.vol = 127 - atVal; - NoteUpdate(channel, i, Upd_Volume); } void OPNMIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) { - // TODO: Verify, is this correct action? channel = channel % 16; - for(MIDIchannel::activenoteiterator - i = Ch[channel].activenotes.begin(); - i != Ch[channel].activenotes.end(); - ++i) - { - // Set this pressure to all active notes on the channel - i->second.vol = 127 - atVal; - } - - NoteUpdate_All(channel, Upd_Volume); + Ch[channel].aftertouch = atVal; } void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) @@ -1236,29 +1243,23 @@ void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t val case 10: // Change panning Ch[channel].panning = 0x00; - if(value < 64 + 32) Ch[channel].panning |= 0x80; - if(value >= 64 - 32) Ch[channel].panning |= 0x40; + if(value < 64 + 32) Ch[channel].panning |= OPN_PANNING_LEFT; + if(value >= 64 - 32) Ch[channel].panning |= OPN_PANNING_RIGHT; NoteUpdate_All(channel, Upd_Pan); break; case 121: // Reset all controllers - Ch[channel].bend = 0; - Ch[channel].volume = 100; - Ch[channel].expression = 127; - Ch[channel].sustain = 0; - Ch[channel].vibrato = 0; - Ch[channel].vibspeed = 2 * 3.141592653 * 5.0; - Ch[channel].vibdepth = 0.5 / 127; - Ch[channel].vibdelay = 0; - Ch[channel].panning = 0xC0; - Ch[channel].portamento = 0; - Ch[channel].brightness = 127; + Ch[channel].resetAllControllers(); //UpdatePortamento(MidCh); NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes KillSustainingNotes(channel); break; + case 120: // All sounds off + NoteUpdate_All(channel, Upd_OffMute); + break; + case 123: // All notes off NoteUpdate_All(channel, Upd_Off); break; @@ -1328,14 +1329,14 @@ void OPNMIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) { channel = channel % 16; - Ch[channel].bend = (uint32_t(pitch) - 8192) * Ch[channel].bendsense; + Ch[channel].bend = int(pitch) - 8192; NoteUpdate_All(channel, Upd_Pitch); } void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) { channel = channel % 16; - Ch[channel].bend = (int(lsb) + int(msb) * 128 - 8192) * Ch[channel].bendsense; + Ch[channel].bend = int(lsb) + int(msb) * 128 - 8192; NoteUpdate_All(channel, Upd_Pitch); } @@ -1370,79 +1371,81 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, unsigned props_mask, int32_t select_adlchn) { - MIDIchannel::NoteInfo &info = i->second; + MIDIchannel::NoteInfo &info = *i; const int16_t tone = info.tone; const uint8_t vol = info.vol; const size_t midiins = info.midiins; - const size_t insmeta = info.insmeta; - const opnInstMeta &ains = opn.GetAdlMetaIns(insmeta); + const opnInstMeta2 &ains = *info.ains; OpnChannel::Location my_loc; my_loc.MidCh = MidCh; - my_loc.note = i->first; + my_loc.note = info.note; - for(MIDIchannel::NoteInfo::PhysMap::iterator - jnext = info.phys.begin(); - jnext != info.phys.end(); - ) + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) { - MIDIchannel::NoteInfo::PhysMap::iterator j(jnext++); - uint16_t c = j->first; - const MIDIchannel::NoteInfo::Phys &ins = j->second; + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; if(select_adlchn >= 0 && c != select_adlchn) continue; if(props_mask & Upd_Patch) { - opn.Patch(c, ins); - OpnChannel::LocationData &d = ch[c].users[my_loc]; - d.sustained = false; // inserts if necessary - d.vibdelay = 0; - d.kon_time_until_neglible = ains.ms_sound_kon; - d.ins = ins; + opn.Patch(c, ins.ains); + OpnChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + if(d) { // inserts if necessary + d->sustained = false; + d->vibdelay = 0; + d->fixed_sustain = (ains.ms_sound_kon == static_cast(opnNoteOnMaxTime)); + d->kon_time_until_neglible = ains.ms_sound_kon; + d->ins = ins; + } } } - for(MIDIchannel::NoteInfo::PhysMap::iterator - jnext = info.phys.begin(); - jnext != info.phys.end(); - ) + for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) { - MIDIchannel::NoteInfo::PhysMap::iterator j(jnext++); - uint16_t c = j->first; - const MIDIchannel::NoteInfo::Phys &ins = j->second; + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; - if(select_adlchn >= 0 && c != select_adlchn) continue; + if(select_adlchn >= 0 && c != select_adlchn) + continue; if(props_mask & Upd_Off) // note off { if(Ch[MidCh].sustain == 0) { - OpnChannel::users_t::iterator k = ch[c].users.find(my_loc); + OpnChannel::LocationData *k = ch[c].users_find(my_loc); - if(k != ch[c].users.end()) - ch[c].users.erase(k); + if(k) + ch[c].users_erase(k); if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, 0, 0.0); - if(ch[c].users.empty()) + if(ch[c].users_empty()) { opn.NoteOff(c); - ch[c].koff_time_until_neglible = - ains.ms_sound_koff; + if(props_mask & Upd_Mute) // Mute the note + { + opn.Touch_Real(c, 0); + ch[c].koff_time_until_neglible = 0; + } else { + ch[c].koff_time_until_neglible = ains.ms_sound_koff; + } } } else { // Sustain: Forget about the note, but don't key it off. // Also will avoid overwriting it very soon. - OpnChannel::LocationData &d = ch[c].users[my_loc]; - d.sustained = true; // note: not erased! + OpnChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + if(d) + d->sustained = true; // note: not erased! if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, -1, 0.0); } - info.phys.erase(j); + info.phys_erase_at(&ins); // decrements channel count + --ccount; // adjusts index accordingly continue; } @@ -1455,13 +1458,20 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, bool is_percussion = (MidCh == 9) || Ch[MidCh].is_xg_percussion; uint8_t brightness = is_percussion ? 127 : Ch[MidCh].brightness; + if(!m_setup.fullRangeBrightnessCC74) + { + // Simulate post-High-Pass filter result which affects sounding by half level only + if(brightness >= 64) + brightness = 127; + else + brightness *= 2; + } + switch(opn.m_volumeScale) { case OPN2::VOLUME_Generic: - case OPN2::VOLUME_CMF: { volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; - /* If the channel has arpeggio, the effective volume of * *this* instrument is actually lower due to timesharing. * To compensate, add extra volume that corresponds to the @@ -1471,18 +1481,20 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, */ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); - if(opn.LogarithmicVolumes) - volume = volume * 127 / (2048383/*127 * 127 * 127*/); - else - { - // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - volume = volume > 8725 ? static_cast((std::log(static_cast(volume)) * (11.541561) + (0.5 - 104.22845)) * 2.0) : 0; - // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) - //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); - } + // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) + volume = volume > 8725 ? static_cast((std::log(static_cast(volume)) * (11.541561) + (0.5 - 104.22845)) * 2.0) : 0; + // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) + //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); opn.Touch_Real(c, volume, brightness); - //opl.Touch(c, volume); + } + break; + + case OPN2::VOLUME_CMF: + { + volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = volume * 127 / (2048383/*127 * 127 * 127*/); + opn.Touch_Real(c, volume, brightness); } break; @@ -1532,33 +1544,36 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Pitch) { - OpnChannel::LocationData &d = ch[c].users[my_loc]; + OpnChannel::LocationData *d = ch[c].users_find(my_loc); // Don't bend a sustained note - if(!d.sustained) + if(!d || !d->sustained) { - double bend = Ch[MidCh].bend + opn.GetAdlIns(ins).finetune; + double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; + double bend = midibend + ins.ains.finetune; double phase = 0.0; + uint8_t vibrato = std::max(Ch[MidCh].vibrato, Ch[MidCh].aftertouch); + vibrato = std::max(vibrato, i->vibrato); - if((ains.flags & opnInstMeta::Flag_Pseudo8op) && ins == ains.opnno2) + if((ains.flags & opnInstMeta::Flag_Pseudo8op) && ins.ains == ains.opn[1]) { phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does) } - if(Ch[MidCh].vibrato && d.vibdelay >= Ch[MidCh].vibdelay) - bend += Ch[MidCh].vibrato * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); + if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) + bend += static_cast(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); #define BEND_COEFFICIENT 321.88557 - opn.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (tone + bend + phase))); + opn.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast(tone) + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, vol, Ch[MidCh].bend); + hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, vol, midibend); } } } - if(info.phys.empty()) - Ch[MidCh].activenotes.erase(i); + if(info.chip_channels_count == 0) + Ch[MidCh].activenotes_erase(i); } #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER @@ -1723,6 +1738,14 @@ OPNMIDIplay::MidiEvent OPNMIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int evt.subtype = evtype; evt.data.insert(evt.data.begin(), data.begin(), data.end()); +#if 0 /* Print all tempo events */ + if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); + } +#endif + /* TODO: Store those meta-strings separately and give ability to read them * by external functions (to display song title and copyright in the player) */ if(evt.subtype == MidiEvent::ST_COPYRIGHT) @@ -2050,41 +2073,38 @@ void OPNMIDIplay::HandleEvent(size_t tk, const OPNMIDIplay::MidiEvent &evt, int } #endif //OPNMIDI_DISABLE_MIDI_SEQUENCER -int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, uint16_t ins, uint16_t) const +int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const { int64_t s = -ch[c].koff_time_until_neglible; // Same midi-instrument = some stability //if(c == MidCh) s += 4; - for(OpnChannel::users_t::const_iterator - j = ch[c].users.begin(); - j != ch[c].users.end(); - ++j) + for (OpnChannel::LocationData *j = ch[c].users_first; j; j = j->next) { s -= 4000; - if(!j->second.sustained) - s -= j->second.kon_time_until_neglible; + if(!j->sustained) + s -= j->kon_time_until_neglible; else - s -= (j->second.kon_time_until_neglible / 2); + s -= (j->kon_time_until_neglible / 2); - MIDIchannel::activenotemap_t::const_iterator - k = Ch[j->first.MidCh].activenotes.find(j->first.note); + MIDIchannel::activenoteiterator + k = const_cast(Ch[j->loc.MidCh]).activenotes_find(j->loc.note); - if(k != Ch[j->first.MidCh].activenotes.end()) + if(k) { // Same instrument = good - if(j->second.ins == ins) + if(j->ins == ins) { s += 300; // Arpeggio candidate = even better - if(j->second.vibdelay < 70 - || j->second.kon_time_until_neglible > 20000) + if(j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) s += 0; } // Percussion is inferior to melody - s += 50 * (int64_t)(k->second.midiins / 128); + s += 50 * (int64_t)(k->midiins / 128); /* if(k->second.midiins >= 25 && k->second.midiins < 40 @@ -2107,14 +2127,11 @@ int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, uint16_t ins, uint16_ // if(opn.four_op_category[c2] // != opn.four_op_category[c]) continue; -// for(OpnChannel::users_t::const_iterator -// m = ch[c2].users.begin(); -// m != ch[c2].users.end(); -// ++m) +// for(OpnChannel::LocationData *m = ch[c2].users_first; m; m = m->next) // { -// if(m->second.sustained) continue; -// if(m->second.vibdelay >= 200) continue; -// if(m->second.ins != j->second.ins) continue; +// if(m->sustained) continue; +// if(m->vibdelay >= 200) continue; +// if(m->ins != j->second.ins) continue; // n_evacuation_stations += 1; // } // } @@ -2126,29 +2143,27 @@ int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, uint16_t ins, uint16_ } -void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, size_t ins) +void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) { - if(ch[c].users.empty()) return; // Nothing to do + if(ch[c].users_empty()) return; // Nothing to do //bool doing_arpeggio = false; - for(OpnChannel::users_t::iterator - jnext = ch[c].users.begin(); - jnext != ch[c].users.end(); - ) + for(OpnChannel::LocationData *jnext = ch[c].users_first; jnext;) { - OpnChannel::users_t::iterator j(jnext++); + OpnChannel::LocationData *j = jnext; + jnext = jnext->next; - if(!j->second.sustained) + if(!j->sustained) { // Collision: Kill old note, // UNLESS we're going to do arpeggio MIDIchannel::activenoteiterator i - (Ch[j->first.MidCh].activenotes.find(j->first.note)); + (Ch[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); // Check if we can do arpeggio. - if((j->second.vibdelay < 70 - || j->second.kon_time_until_neglible > 20000) - && j->second.ins == ins) + if((j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) + && j->ins == ins) { // Do arpeggio together with this note. //doing_arpeggio = true; @@ -2167,11 +2182,13 @@ void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, size_t ins) // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. - if(ch[c].users.empty()) + if(ch[c].users_empty()) opn.NoteOff(c); } -void OPNMIDIplay::KillOrEvacuate(size_t from_channel, OpnChannel::users_t::iterator j, OPNMIDIplay::MIDIchannel::activenoteiterator i) +void OPNMIDIplay::KillOrEvacuate(size_t from_channel, + OpnChannel::LocationData *j, + OPNMIDIplay::MIDIchannel::activenoteiterator i) { // Before killing the note, check if it can be // evacuated to another channel as an arpeggio @@ -2189,32 +2206,34 @@ void OPNMIDIplay::KillOrEvacuate(size_t from_channel, OpnChannel::users_t::itera //if(opn.four_op_category[c] != opn.four_op_category[from_channel]) // continue; - for(OpnChannel::users_t::iterator - m = ch[c].users.begin(); - m != ch[c].users.end(); - ++m) + OpnChannel &adlch = ch[c]; + if(adlch.users_size == OpnChannel::users_max) + continue; // no room for more arpeggio on channel + + for(OpnChannel::LocationData *m = adlch.users_first; m; m = m->next) { - if(m->second.vibdelay >= 200 - && m->second.kon_time_until_neglible < 10000) continue; - if(m->second.ins != j->second.ins) + if(m->vibdelay >= 200 + && m->kon_time_until_neglible < 10000) continue; + if(m->ins != j->ins) continue; if(hooks.onNote) { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->second.tone, - (int)i->second.midiins, 0, 0.0); + i->tone, + (int)i->midiins, 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->second.tone, - (int)i->second.midiins, - i->second.vol, 0.0); + i->tone, + (int)i->midiins, + i->vol, 0.0); } - i->second.phys.erase(static_cast(from_channel)); - i->second.phys[cs] = j->second.ins; - ch[cs].users.insert(*j); - ch[from_channel].users.erase(j); + i->phys_erase(static_cast(from_channel)); + i->phys_ensure_find_or_create(cs)->assign(j->ins); + if(!ch[cs].users_insert(*j)) + assert(false); + ch[from_channel].users_erase(j); return; } } @@ -2227,7 +2246,7 @@ void OPNMIDIplay::KillOrEvacuate(size_t from_channel, OpnChannel::users_t::itera ins );*/ // Kill it - NoteUpdate(j->first.MidCh, + NoteUpdate(j->loc.MidCh, i, Upd_Off, static_cast(from_channel)); @@ -2254,27 +2273,25 @@ void OPNMIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) for(unsigned c = first; c < last; ++c) { - if(ch[c].users.empty()) continue; // Nothing to do + if(ch[c].users_empty()) continue; // Nothing to do - for(OpnChannel::users_t::iterator - jnext = ch[c].users.begin(); - jnext != ch[c].users.end(); - ) + for(OpnChannel::LocationData *jnext = ch[c].users_first; jnext;) { - OpnChannel::users_t::iterator j(jnext++); + OpnChannel::LocationData *j = jnext; + jnext = jnext->next; - if((MidCh < 0 || j->first.MidCh == MidCh) - && j->second.sustained) + if((MidCh < 0 || j->loc.MidCh == MidCh) + && j->sustained) { int midiins = '?'; if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, (int)c, j->first.note, midiins, 0, 0.0); - ch[c].users.erase(j); + hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); + ch[c].users_erase(j); } } // Keyoff the channel, if there are no users left. - if(ch[c].users.empty()) + if(ch[c].users_empty()) opn.NoteOff(c); } } @@ -2287,7 +2304,12 @@ void OPNMIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) switch(addr + nrpn * 0x10000 + MSB * 0x20000) { case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity - Ch[MidCh].bendsense = value / 8192.0; + Ch[MidCh].bendsense_msb = value; + Ch[MidCh].updateBendSensitivity(); + break; + case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB + Ch[MidCh].bendsense_lsb = value; + Ch[MidCh].updateBendSensitivity(); break; case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed if(value == 64) Ch[MidCh].vibspeed = 1.0; @@ -2320,9 +2342,7 @@ void OPNMIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) void OPNMIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) { for(MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes.begin(); - i != Ch[MidCh].activenotes.end(); - ) + i = Ch[MidCh].activenotes_begin(); i;) { MIDIchannel::activenoteiterator j(i++); NoteUpdate(MidCh, j, props_mask); @@ -2332,9 +2352,9 @@ void OPNMIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) void OPNMIDIplay::NoteOff(uint16_t MidCh, uint8_t note) { MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes.find(note); + i = Ch[MidCh].activenotes_find(note); - if(i != Ch[MidCh].activenotes.end()) + if(i) NoteUpdate(MidCh, i, Upd_Off); } @@ -2343,7 +2363,7 @@ void OPNMIDIplay::UpdateVibrato(double amount) { for(size_t a = 0, b = Ch.size(); a < b; ++a) { - if(Ch[a].vibrato && !Ch[a].activenotes.empty()) + if(Ch[a].hasVibrato() && !Ch[a].activenotes_empty()) { NoteUpdate_All(static_cast(a), Upd_Pitch); Ch[a].vibpos += amount * Ch[a].vibspeed; @@ -2392,8 +2412,8 @@ void OPNMIDIplay::UpdateArpeggio(double) // amount = amount of time passed arpeggio_cache = 0.0; #endif #endif - static unsigned arpeggio_counter = 0; - ++arpeggio_counter; + + ++m_arpeggioCounter; for(uint32_t c = 0; c < opn.NumChannels; ++c) { @@ -2401,11 +2421,11 @@ retry_arpeggio: if(c > uint32_t(std::numeric_limits::max())) break; - size_t n_users = ch[c].users.size(); + size_t n_users = ch[c].users_size; if(n_users > 1) { - OpnChannel::users_t::const_iterator i = ch[c].users.begin(); + OpnChannel::LocationData *i = ch[c].users_first; size_t rate_reduction = 3; if(n_users >= 3) @@ -2414,23 +2434,25 @@ retry_arpeggio: if(n_users >= 4) rate_reduction = 1; - std::advance(i, (arpeggio_counter / rate_reduction) % n_users); + for(size_t count = (m_arpeggioCounter / rate_reduction) % n_users, + n = 0; n < count; ++n) + i = i->next; - if(i->second.sustained == false) + if(i->sustained == false) { - if(i->second.kon_time_until_neglible <= 0l) + if(i->kon_time_until_neglible <= 0l) { NoteUpdate( - i->first.MidCh, - Ch[ i->first.MidCh ].activenotes.find(i->first.note), + i->loc.MidCh, + Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Off, static_cast(c)); goto retry_arpeggio; } NoteUpdate( - i->first.MidCh, - Ch[ i->first.MidCh ].activenotes.find(i->first.note), + i->loc.MidCh, + Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Pitch | Upd_Volume | Upd_Pan, static_cast(c)); } @@ -2629,3 +2651,122 @@ retry_arpeggio: //} //#endif//ADLMIDI_DISABLE_CPP_EXTRAS + +// Implement the user map data structure. + +bool OPNMIDIplay::OpnChannel::users_empty() const +{ + return !users_first; +} + +OPNMIDIplay::OpnChannel::LocationData *OPNMIDIplay::OpnChannel::users_find(Location loc) +{ + LocationData *user = NULL; + for(LocationData *curr = users_first; !user && curr; curr = curr->next) + if(curr->loc == loc) + user = curr; + return user; +} + +OPNMIDIplay::OpnChannel::LocationData *OPNMIDIplay::OpnChannel::users_allocate() +{ + // remove free cells front + LocationData *user = users_free_cells; + if(!user) + return NULL; + users_free_cells = user->next; + if(users_free_cells) + users_free_cells->prev = NULL; + // add to users front + if(users_first) + users_first->prev = user; + user->prev = NULL; + user->next = users_first; + users_first = user; + ++users_size; + return user; +} + +OPNMIDIplay::OpnChannel::LocationData *OPNMIDIplay::OpnChannel::users_find_or_create(Location loc) +{ + LocationData *user = users_find(loc); + if(!user) { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = LocationData(); + user->prev = prev; user->next = next; + user->loc = loc; + } + return user; +} + +OPNMIDIplay::OpnChannel::LocationData *OPNMIDIplay::OpnChannel::users_insert(const LocationData &x) +{ + LocationData *user = users_find(x.loc); + if(!user) + { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = x; + user->prev = prev; user->next = next; + } + return user; +} + +void OPNMIDIplay::OpnChannel::users_erase(LocationData *user) +{ + if(user->prev) + user->prev->next = user->next; + if(user->next) + user->next->prev = user->prev; + if(user == users_first) + users_first = user->next; + user->prev = NULL; + user->next = users_free_cells; + users_free_cells = user; + --users_size; +} + +void OPNMIDIplay::OpnChannel::users_clear() +{ + users_first = NULL; + users_free_cells = users_cells; + users_size = 0; + for(size_t i = 0; i < users_max; ++i) + { + users_cells[i].prev = (i > 0) ? &users_cells[i - 1] : NULL; + users_cells[i].next = (i + 1 < users_max) ? &users_cells[i + 1] : NULL; + } +} + +void OPNMIDIplay::OpnChannel::users_assign(const LocationData *users, size_t count) +{ + ADL_UNUSED(count);//Avoid warning for release builds + assert(count <= users_max); + if(users == users_first && users) { + // self assignment + assert(users_size == count); + return; + } + users_clear(); + const LocationData *src_cell = users; + // move to the last + if(src_cell) { + while(src_cell->next) + src_cell = src_cell->next; + } + // push cell copies in reverse order + while(src_cell) { + LocationData *dst_cell = users_allocate(); + assert(dst_cell); + LocationData *prev = dst_cell->prev, *next = dst_cell->next; + *dst_cell = *src_cell; + dst_cell->prev = prev; dst_cell->next = next; + src_cell = src_cell->prev; + } + assert(users_size == count); +} diff --git a/src/sound/opnmidi/opnmidi_opn2.cpp b/src/sound/opnmidi/opnmidi_opn2.cpp index 398ba8980..392294802 100644 --- a/src/sound/opnmidi/opnmidi_opn2.cpp +++ b/src/sound/opnmidi/opnmidi_opn2.cpp @@ -23,6 +23,32 @@ #include "opnmidi_private.hpp" +#if defined(OPNMIDI_DISABLE_NUKED_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_EMULATOR) && \ + defined(OPNMIDI_DISABLE_GENS_EMULATOR) && defined(OPNMIDI_DISABLE_GX_EMULATOR) +#error "No emulators enabled. You must enable at least one emulator to use this library!" +#endif + +// Nuked OPN2 emulator, Most accurate, but requires the powerful CPU +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR +#include "chips/nuked_opn2.h" +#endif + +// MAME YM2612 emulator, Well-accurate and fast +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR +#include "chips/mame_opn2.h" +#endif + +// GENS 2.10 emulator, very outdated and inaccurate, but gives the best performance +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR +#include "chips/gens_opn2.h" +#endif + +// Genesis Plus GX emulator, Variant of MAME with enhancements +#ifndef OPNMIDI_DISABLE_GX_EMULATOR +#include "chips/gx_opn2.h" +#endif + + static const uint8_t NoteChannels[6] = { 0, 1, 2, 4, 5, 6 }; static inline void getOpnChannel(uint32_t in_channel, @@ -36,43 +62,30 @@ static inline void getOpnChannel(uint32_t in_channel, out_ch = ch4 % 3; } -const opnInstMeta &OPN2::GetAdlMetaIns(size_t n) +void OPN2::cleanInstrumentBanks() { - return dynamic_metainstruments[n]; + dynamic_banks.clear(); } -size_t OPN2::GetAdlMetaNumber(size_t midiins) +static opnInstMeta2 makeEmptyInstrument() { - return midiins; + opnInstMeta2 ins; + memset(&ins, 0, sizeof(opnInstMeta2)); + ins.flags = opnInstMeta::Flag_NoSound; + return ins; } -static const opnInstData opn2_emptyInstrument = { - { - {{0, 0, 0, 0, 0, 0, 0}}, - {{0, 0, 0, 0, 0, 0, 0}}, - {{0, 0, 0, 0, 0, 0, 0}}, - {{0, 0, 0, 0, 0, 0, 0}} - }, - 0, 0, 0 -}; - -const opnInstData &OPN2::GetAdlIns(size_t insno) -{ - if(insno >= dynamic_instruments.size()) - return opn2_emptyInstrument; - return dynamic_instruments[insno]; -} +const opnInstMeta2 OPN2::emptyInstrument = makeEmptyInstrument(); OPN2::OPN2() : regLFO(0), - dynamic_percussion_offset(128), - DynamicInstrumentTag(0x8000u), - DynamicMetaInstrumentTag(0x4000000u), NumCards(1), - LogarithmicVolumes(false), m_musicMode(MODE_MIDI), m_volumeScale(VOLUME_Generic) -{} +{ + // Initialize blank instruments banks + cleanInstrumentBanks(); +} OPN2::~OPN2() { @@ -81,15 +94,7 @@ OPN2::~OPN2() void OPN2::PokeO(size_t card, uint8_t port, uint8_t index, uint8_t value) { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - if(port == 1) - cardsOP2[card]->write1(index, value); - else - cardsOP2[card]->write0(index, value); - #else - OPN2_WriteBuffered(cardsOP2[card], 0 + (port) * 2, index); - OPN2_WriteBuffered(cardsOP2[card], 1 + (port) * 2, value); - #endif + cardsOP2[card]->writeReg(port, index, value); } void OPN2::NoteOff(size_t c) @@ -113,7 +118,7 @@ void OPN2::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071 if(hertz < 0 || hertz > 262143) // Avoid infinite loop return; - while(hertz >= 2047.5) + while((hertz >= 1023.75) && (x2 < 0x3800)) { hertz /= 2.0; // Calculate octave x2 += 0x800; @@ -134,8 +139,7 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) uint8_t port, cc; getOpnChannel(c, card, port, cc); - size_t i = ins[c]; - const opnInstData &adli = GetAdlIns(i); + const opnInstData &adli = ins[c]; uint8_t op_vol[4] = { @@ -185,13 +189,12 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) // 63 + chanvol * (instrvol / 63.0 - 1) } -void OPN2::Patch(uint16_t c, size_t i) +void OPN2::Patch(uint16_t c, const opnInstData &adli) { unsigned card; uint8_t port, cc; getOpnChannel(uint16_t(c), card, port, cc); - ins[c] = i; - const opnInstData &adli = GetAdlIns(i); + ins[c] = adli; #if 1 //Reg1-Op1, Reg1-Op2, Reg1-Op3, Reg1-Op4,.... for(uint8_t d = 0; d < 7; d++) { @@ -221,7 +224,7 @@ void OPN2::Pan(unsigned c, unsigned value) unsigned card; uint8_t port, cc; getOpnChannel(uint16_t(c), card, port, cc); - const opnInstData &adli = GetAdlIns(ins[c]); + const opnInstData &adli = ins[c]; uint8_t val = (value & 0xC0) | (adli.lfosens & 0x3F); regBD[c] = val; PokeO(card, port, 0xB4 + cc, val); @@ -248,7 +251,6 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel) break; case OPNMIDI_VolumeModel_CMF: - LogarithmicVolumes = true; m_volumeScale = OPN2::VOLUME_CMF; break; @@ -269,34 +271,51 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel) void OPN2::ClearChips() { for(size_t i = 0; i < cardsOP2.size(); i++) - delete cardsOP2[i]; + cardsOP2[i].reset(NULL); cardsOP2.clear(); } -void OPN2::Reset(unsigned long PCM_RATE) +void OPN2::Reset(int emulator, unsigned long PCM_RATE) { ClearChips(); ins.clear(); pit.clear(); regBD.clear(); - cardsOP2.resize(NumCards, NULL); + cardsOP2.resize(NumCards, AdlMIDI_SPtr()); - #ifndef OPNMIDI_USE_LEGACY_EMULATOR - OPN2_SetChipType(ym3438_type_asic); - #endif for(size_t i = 0; i < cardsOP2.size(); i++) { - #ifdef OPNMIDI_USE_LEGACY_EMULATOR - cardsOP2[i] = new OPNMIDI_Ym2612_Emu(); - cardsOP2[i]->set_rate(PCM_RATE, 7670454.0); - #else - cardsOP2[i] = new ym3438_t; - std::memset(cardsOP2[i], 0, sizeof(ym3438_t)); - OPN2_Reset(cardsOP2[i], (Bit32u)PCM_RATE, 7670454); - #endif + switch(emulator) + { + default: +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR + case OPNMIDI_EMU_MAME: + cardsOP2[i].reset(new MameOPN2()); + break; +#endif +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR + case OPNMIDI_EMU_NUKED: + cardsOP2[i].reset(new NukedOPN2()); + break; +#endif +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR + case OPNMIDI_EMU_GENS: + cardsOP2[i].reset(new GensOPN2()); + break; +#endif +#ifndef OPNMIDI_DISABLE_GX_EMULATOR + case OPNMIDI_EMU_GX: + cardsOP2[i].reset(new GXOPN2()); + break; +#endif + } + cardsOP2[i]->setRate((uint32_t)PCM_RATE, 7670454); + if(runAtPcmRate) + cardsOP2[i]->setRunningAtPcmRate(true); } + NumChannels = NumCards * 6; - ins.resize(NumChannels, 189); + ins.resize(NumChannels, emptyInstrument.opn[0]); pit.resize(NumChannels, 0); regBD.resize(NumChannels, 0); diff --git a/src/sound/opnmidi/opnmidi_private.hpp b/src/sound/opnmidi/opnmidi_private.hpp index 6e956d10f..ed10dcd10 100644 --- a/src/sound/opnmidi/opnmidi_private.hpp +++ b/src/sound/opnmidi/opnmidi_private.hpp @@ -63,6 +63,7 @@ typedef __int32 ssize_t; #include #include #include +#include #include // vector #include // deque #include // exp, log, ceil @@ -77,63 +78,88 @@ typedef __int32 ssize_t; #include #include -#ifdef _MSC_VER -#pragma warning(disable:4244) -#pragma warning(disable:4267) -#pragma warning(disable:4146) -#pragma warning(disable:4800) +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT8_MIN +#define INT8_MIN (-0x7f - 1) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-0x7fffffff - 1) +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7f +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff #endif - #include "fraction.hpp" -#ifdef OPNMIDI_USE_LEGACY_EMULATOR -#include "Ym2612_ChipEmu.h" -#else -#include "ym3438.h" -#endif +#include "chips/opn_chip_base.h" #include "opnbank.h" #include "opnmidi.h" +#include "opnmidi_ptr.hpp" +#include "opnmidi_bankmap.h" #define ADL_UNUSED(x) (void)x +#define OPN_PANNING_LEFT 0x80 +#define OPN_PANNING_RIGHT 0x40 +#define OPN_PANNING_BOTH 0xC0 + extern std::string OPN2MIDI_ErrorString; /* - Smart pointer for C heaps, created with malloc() call. - FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it + Sample conversions to various formats */ -template -class AdlMIDI_CPtr +template +inline Real opn2_cvtReal(int32_t x) { - PTR *m_p; -public: - AdlMIDI_CPtr() : m_p(NULL) {} - ~AdlMIDI_CPtr() - { - reset(NULL); - } - - void reset(PTR *p = NULL) - { - if(m_p) - free(m_p); - m_p = p; - } - - PTR *get() - { - return m_p; - } - PTR &operator*() - { - return *m_p; - } - PTR *operator->() - { - return m_p; - } -}; + return x * ((Real)1 / INT16_MAX); +} +inline int32_t opn2_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? INT16_MIN : x; + x = (x > INT16_MAX) ? INT16_MAX : x; + return x; +} +inline int32_t opn2_cvtS8(int32_t x) +{ + return opn2_cvtS16(x) / 256; +} +inline int32_t opn2_cvtS24(int32_t x) +{ + return opn2_cvtS16(x) * 256; +} +inline int32_t opn2_cvtS32(int32_t x) +{ + return opn2_cvtS16(x) * 65536; +} +inline int32_t opn2_cvtU16(int32_t x) +{ + return opn2_cvtS16(x) - INT16_MIN; +} +inline int32_t opn2_cvtU8(int32_t x) +{ + return opn2_cvtS8(x) - INT8_MIN; +} +inline int32_t opn2_cvtU24(int32_t x) +{ + enum { int24_min = -(1 << 23) }; + return opn2_cvtS24(x) - int24_min; +} +inline int32_t opn2_cvtU32(int32_t x) +{ + // unsigned operation because overflow on signed integers is undefined + return (uint32_t)opn2_cvtS32(x) - (uint32_t)INT32_MIN; +} class OPNMIDIplay; class OPN2 @@ -142,39 +168,32 @@ public: friend class OPNMIDIplay; uint32_t NumChannels; char ____padding[4]; -#ifdef OPNMIDI_USE_LEGACY_EMULATOR - std::vector cardsOP2; -#else - std::vector cardsOP2; -#endif + std::vector > cardsOP2; private: - std::vector ins; // index to adl[], cached, needed by Touch() + std::vector ins; // patch data, cached, needed by Touch() std::vector pit; // value poked to B0, cached, needed by NoteOff)( std::vector regBD; uint8_t regLFO; - //! Meta information about every instrument - std::vector dynamic_metainstruments; - //! Raw instrument data ready to be sent to the chip - std::vector dynamic_instruments; - size_t dynamic_percussion_offset; - - typedef std::map BankMap; - BankMap dynamic_melodic_banks; - BankMap dynamic_percussion_banks; - const unsigned DynamicInstrumentTag /* = 0x8000u*/, - DynamicMetaInstrumentTag /* = 0x4000000u*/; - const opnInstMeta &GetAdlMetaIns(size_t n); - size_t GetAdlMetaNumber(size_t midiins); - const opnInstData &GetAdlIns(size_t insno); - + void cleanInstrumentBanks(); public: + struct Bank + { + opnInstMeta2 ins[128]; + }; + typedef BasicBankMap BankMap; + BankMap dynamic_banks; +public: + static const opnInstMeta2 emptyInstrument; + enum { PercussionTag = 1 << 15 }; + //! Total number of running concurrent emulated chips unsigned int NumCards; //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. bool ScaleModulators; - //! Required to play CMF files. Can be turned on by using of "CMF" volume model - bool LogarithmicVolumes; + //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. + bool runAtPcmRate; + char ___padding2[3]; enum MusicMode @@ -211,12 +230,12 @@ public: void NoteOn(unsigned c, double hertz); void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127); - void Patch(uint16_t c, size_t i); + void Patch(uint16_t c, const opnInstData &adli); void Pan(unsigned c, unsigned value); void Silence(); void ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel); void ClearChips(); - void Reset(unsigned long PCM_RATE); + void Reset(int emulator, unsigned long PCM_RATE); }; @@ -402,7 +421,7 @@ public: bool eof() { if(fp) - return std::feof(fp); + return (std::feof(fp) != 0); else return mp_tell >= mp_size; } @@ -420,9 +439,15 @@ public: uint8_t bank_lsb, bank_msb; uint8_t patch; uint8_t volume, expression; - uint8_t panning, vibrato, sustain; + uint8_t panning, vibrato, aftertouch, sustain; + //! Per note Aftertouch values + uint8_t noteAftertouch[128]; + //! Is note aftertouch has any non-zero value + bool noteAfterTouchInUse; char ____padding[6]; - double bend, bendsense; + int bend; + double bendsense; + int bendsense_lsb, bendsense_msb; double vibpos, vibspeed, vibdepth; int64_t vibdelay; uint8_t lastlrpn, lastmrpn; @@ -431,53 +456,218 @@ public: bool is_xg_percussion; struct NoteInfo { + uint8_t note; + bool active; // Current pressure uint8_t vol; + // Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; char ____padding[1]; // Tone selected on noteon: int16_t tone; - char ____padding2[4]; + char ____padding2[10]; // Patch selected on noteon; index to banks[AdlBank][] size_t midiins; - // Index to physical adlib data structure, adlins[] - size_t insmeta; - typedef std::map PhysMap; - typedef uint16_t Phys; + // Patch selected + const opnInstMeta2 *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans, + }; + struct Phys + { + //! Destination chip channel + uint16_t chip_chan; + //! ins, inde to adl[] + opnInstData ains; + + void assign(const Phys &oth) + { + ains = oth.ains; + } + bool operator==(const Phys &oth) const + { + return (ains == oth.ains); + } + bool operator!=(const Phys &oth) const + { + return !operator==(oth); + } + }; // List of OPN2 channels it is currently occupying. - std::map phys; + Phys chip_channels[MaxNumPhysItemCount]; + //! Count of used channels. + unsigned chip_channels_count; + // + Phys *phys_find(unsigned chip_chan) + { + Phys *ph = NULL; + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + return ph; + } + Phys *phys_find_or_create(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = (uint16_t)chip_chan; + } + } + return ph; + } + Phys *phys_ensure_find_or_create(unsigned chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos < static_cast(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + phys_erase_at(ph); + } }; - typedef std::map activenotemap_t; - typedef activenotemap_t::iterator activenoteiterator; char ____padding2[5]; - activenotemap_t activenotes; + NoteInfo activenotes[128]; + + struct activenoteiterator + { + explicit activenoteiterator(NoteInfo *info = NULL) + : ptr(info) {} + activenoteiterator &operator++() + { + if(ptr->note == 127) + ptr = NULL; + else + for(++ptr; ptr && !ptr->active;) + ptr = (ptr->note == 127) ? NULL : (ptr + 1); + return *this; + } + activenoteiterator operator++(int) + { + activenoteiterator pos = *this; + ++*this; + return pos; + } + NoteInfo &operator*() const + { return *ptr; } + NoteInfo *operator->() const + { return ptr; } + bool operator==(activenoteiterator other) const + { return ptr == other.ptr; } + bool operator!=(activenoteiterator other) const + { return ptr != other.ptr; } + operator NoteInfo *() const + { return ptr; } + private: + NoteInfo *ptr; + }; + + activenoteiterator activenotes_begin() + { + activenoteiterator it(activenotes); + return (it->active) ? it : ++it; + } + + activenoteiterator activenotes_find(uint8_t note) + { + assert(note < 128); + return activenoteiterator( + activenotes[note].active ? &activenotes[note] : NULL); + } + + activenoteiterator activenotes_ensure_find(uint8_t note) + { + activenoteiterator it = activenotes_find(note); + assert(it); + return it; + } + + std::pair activenotes_insert(uint8_t note) + { + assert(note < 128); + NoteInfo &info = activenotes[note]; + bool inserted = !info.active; + if(inserted) info.active = true; + return std::pair(activenoteiterator(&info), inserted); + } + + void activenotes_erase(activenoteiterator pos) + { + if(pos) + pos->active = false; + } + + bool activenotes_empty() + { + return !activenotes_begin(); + } + + void activenotes_clear() + { + for(uint8_t i = 0; i < 128; ++i) { + activenotes[i].note = i; + activenotes[i].active = false; + } + } void reset() { - portamento = 0; + resetAllControllers(); + patch = 0; + vibpos = 0; bank_lsb = 0; bank_msb = 0; - patch = 0; - volume = 100; - expression = 127; - panning = 0xC0; - vibrato = 0; - sustain = 0; - bend = 0.0; - bendsense = 2 / 8192.0; - vibpos = 0; - vibspeed = 2 * 3.141592653 * 5.0; - vibdepth = 0.5 / 127; - vibdelay = 0; lastlrpn = 0; lastmrpn = 0; nrpn = false; - brightness = 127; is_xg_percussion = false; } - - MIDIchannel() - : activenotes() + void resetAllControllers() { + bend = 0; + bendsense_msb = 2; + bendsense_lsb = 0; + updateBendSensitivity(); + volume = 100; + expression = 127; + sustain = 0; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay = 0; + panning = OPN_PANNING_BOTH; + portamento = 0; + brightness = 127; + } + bool hasVibrato() + { + return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; + } + void updateBendSensitivity() + { + int cent = bendsense_msb * 128 + bendsense_lsb; + bendsense = cent * (1.0 / (128 * 8192)); + } + MIDIchannel() + { + activenotes_clear(); reset(); } }; @@ -485,37 +675,71 @@ public: // Additional information about OPN channels struct OpnChannel { - // For collisions struct Location { uint16_t MidCh; uint8_t note; - bool operator==(const Location &b) const - { - return MidCh == b.MidCh && note == b.note; - } - bool operator< (const Location &b) const - { - return MidCh < b.MidCh || (MidCh == b.MidCh && note < b.note); - } + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } char ____padding[1]; }; struct LocationData { + LocationData *prev, *next; + Location loc; bool sustained; - char ____padding[1]; + char ____padding[3]; MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] - char ____padding2[4]; + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on int64_t kon_time_until_neglible; int64_t vibdelay; }; - typedef std::map users_t; - users_t users; // If the channel is keyoff'd int64_t koff_time_until_neglible; + + enum { users_max = 128 }; + LocationData *users_first, *users_free_cells; + LocationData users_cells[users_max]; + unsigned users_size; + + bool users_empty() const; + LocationData *users_find(Location loc); + LocationData *users_allocate(); + LocationData *users_find_or_create(Location loc); + LocationData *users_insert(const LocationData &x); + void users_erase(LocationData *user); + void users_clear(); + void users_assign(const LocationData *users, size_t count); + // For channel allocation: - OpnChannel(): users(), koff_time_until_neglible(0) { } + OpnChannel(): koff_time_until_neglible(0) + { + users_clear(); + } + + OpnChannel(const OpnChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible) + { + if(oth.users_first) + { + users_first = NULL; + users_assign(oth.users_first, oth.users_size); + } + else + users_clear(); + } + + OpnChannel &operator=(const OpnChannel &oth) + { + koff_time_until_neglible = oth.koff_time_until_neglible; + users_assign(oth.users_first, oth.users_size); + return *this; + } + void AddAge(int64_t ms); }; @@ -648,6 +872,8 @@ public: struct Setup { + int emulator; + bool runAtPcmRate; unsigned int OpnBank; unsigned int NumCards; unsigned int LogarithmicVolumes; @@ -655,6 +881,7 @@ public: //unsigned int SkipForward; bool loopingIsEnabled; int ScaleModulators; + bool fullRangeBrightnessCC74; double delay; double carry; @@ -688,6 +915,8 @@ private: std::map current_device; std::vector ch; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER std::vector > TrackData; @@ -760,7 +989,7 @@ public: #endif OPN2 opn; - int16_t outBuf[1024]; + int32_t outBuf[1024]; Setup m_setup; @@ -879,7 +1108,9 @@ private: Upd_Volume = 0x4, Upd_Pitch = 0x8, Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, - Upd_Off = 0x20 + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upd_OffMute = Upd_Off + Upd_Mute }; void NoteUpdate(uint16_t MidCh, @@ -894,15 +1125,15 @@ private: // Determine how good a candidate this adlchannel // would be for playing a note from this instrument. - int64_t CalculateAdlChannelGoodness(size_t c, uint16_t ins, uint16_t /*MidCh*/) const; + int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; // A new note will be played on this channel using this instrument. // Kill existing notes on this channel (or don't, if we do arpeggio) - void PrepareAdlChannelForNewNote(size_t c, size_t ins); + void PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); void KillOrEvacuate( size_t from_channel, - OpnChannel::users_t::iterator j, + OpnChannel::LocationData *j, MIDIchannel::activenoteiterator i); void Panic(); void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); diff --git a/src/sound/opnmidi/opnmidi_ptr.hpp b/src/sound/opnmidi/opnmidi_ptr.hpp new file mode 100644 index 000000000..14e7591e1 --- /dev/null +++ b/src/sound/opnmidi/opnmidi_ptr.hpp @@ -0,0 +1,217 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_PTR_HPP_THING +#define OPNMIDI_PTR_HPP_THING + +#include // swap +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct ADLMIDI_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; +template +struct ADLMIDI_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; +struct ADLMIDI_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_UPtr +{ + T *m_p; +public: + explicit AdlMIDI_UPtr(T *p) + : m_p(p) {} + ~AdlMIDI_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p) { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + void swap(AdlMIDI_UPtr &other) + { + std::swap(m_p, other.m_p); + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +private: + AdlMIDI_UPtr(const AdlMIDI_UPtr &); + AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &); +}; + +template +void swap(AdlMIDI_UPtr &a, AdlMIDI_UPtr &b) +{ + a.swap(b); +} + +/** + Unique pointer for arrays. + */ +template +class AdlMIDI_UPtrArray : + public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_UPtrArray(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +/** + Unique pointer for C memory. + */ +template +class AdlMIDI_CPtr : + public AdlMIDI_UPtr< T, ADLMIDI_CDelete > +{ +public: + explicit AdlMIDI_CPtr(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {} +}; + +/* + Shared pointer with non-atomic counter + FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_SPtr +{ + T *m_p; + size_t *m_counter; +public: + explicit AdlMIDI_SPtr(T *p = NULL) + : m_p(p), m_counter(p ? new size_t(1) : NULL) {} + ~AdlMIDI_SPtr() + { + reset(NULL); + } + + AdlMIDI_SPtr(const AdlMIDI_SPtr &other) + : m_p(other.m_p), m_counter(other.m_counter) + { + if(m_counter) + ++*m_counter; + } + + AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other) + { + if(this == &other) + return *this; + reset(); + m_p = other.m_p; + m_counter = other.m_counter; + if(m_counter) + ++*m_counter; + return *this; + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p && --*m_counter == 0) { + Deleter del; + del(m_p); + if(!p) { + delete m_counter; + m_counter = NULL; + } + } + m_p = p; + if(p) { + if(!m_counter) + m_counter = new size_t; + *m_counter = 1; + } + } + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +}; + +/** + Shared pointer for arrays. + */ +template +class AdlMIDI_SPtrArray : + public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_SPtrArray(T *p = NULL) + : AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +#endif //ADLMIDI_PTR_HPP_THING diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 1a034207c..b73520259 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2158,6 +2158,7 @@ ADVSNDMNU_OPLSYNTHESIS = "OPL Synthesis"; ADVSNDMNU_OPLNUMCHIPS = "Number of emulated OPL chips"; ADVSNDMNU_OPLFULLPAN = "Full MIDI stereo panning"; ADVSNDMNU_OPLCORES = "OPL Emulator Core"; +ADVSNDMNU_OPNCORES = "OPN2 Emulator Core"; ADVSNDMNU_GUSEMULATION = "GUS Emulation"; ADVSNDMNU_GUSCONFIG = "GUS config file"; ADVSNDMNU_MIDIVOICES = "MIDI voices"; @@ -2183,10 +2184,14 @@ ADVSNDMNU_FREEVERB = "Freeverb"; ADVSNDMNU_GLOBAL_FREEVERB = "Global Freeverb"; ADVSNDMNU_ADVRESAMPLING = "Advanced Resampling"; ADVSNDMNU_OPLBANK = "OPL Bank"; +ADVSNDMNU_ADLOPLCORES = "OPL Emulator Core"; +ADVSNDMNU_RUNPCMRATE = "Run emulator at PCM rate"; ADVSNDMNU_ADLNUMCHIPS = "Number of emulated OPL chips"; ADVSNDMNU_VLMODEL = "Volume model"; ADVSNDMNU_OPNNUMCHIPS = "Number of emulated OPN chips"; +// ADLMIDI's emulation cores + // ADLMIDI's volume models ADLVLMODEL_AUTO = "Auto (Use setup of bank)"; ADLVLMODEL_GENERIC = "Generic"; @@ -2373,9 +2378,13 @@ OPTVAL_512K = "512K"; OPTVAL_768K = "768K"; OPTVAL_1024K = "1024K"; OPTVAL_MAMEOPL2 = "MAME OPL2"; +OPTVAL_MAMEOPN2 = "MAME YM2612"; OPTVAL_DOSBOXOPL3 = "DOSBox OPL3"; OPTVAL_JAVAOPL3 = "Java OPL3"; OPTVAL_NUKEDOPL3 = "Nuked OPL3"; +OPTVAL_NUKEDOPL3174 = "Nuked OPL3 v1.7.4"; +OPTVAL_NUKEDOPN2 = "Nuked OPN2"; +OPTVAL_GENSOPN2 = "GENS YM2612"; OPTVAL_SOUNDSYSTEM = "Sound System"; OPTVAL_FOO_DUMB = "foo_dumb"; OPTVAL_ALIASING = "Aliasing"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 28f9e17d7..71043ea58 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1823,18 +1823,36 @@ OptionMenu ModReplayerOptions protected 5, "$ADLVLMODEL_WIN9X" } + OptionValue ADLOplCores + { + 0, "$OPTVAL_NUKEDOPL3" + 1, "$OPTVAL_NUKEDOPL3174" + 2, "$OPTVAL_DOSBOXOPL3" + } + + OptionValue OpnCores + { + 0, "$OPTVAL_MAMEOPN2" + 1, "$OPTVAL_NUKEDOPN2" + 2, "$OPTVAL_GENSOPN2" + } + OptionMenu ADLOptions protected { - Title "$ADVSNDMNU_ADLMIDI" - LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu" - Slider "$ADVSNDMNU_ADLNUMCHIPS", "adl_chips_count", 1, 32, 1, 0 - Option "$ADVSNDMNU_VLMODEL", "adl_volume_model", "AdlVolumeModels" + Title "$ADVSNDMNU_ADLMIDI" + LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu" + Option "$ADVSNDMNU_OPLCORES", "adl_emulator_id", "ADLOplCores" + Option "$ADVSNDMNU_RUNPCMRATE", "adl_run_at_pcm_rate", "OnOff" + Slider "$ADVSNDMNU_ADLNUMCHIPS", "adl_chips_count", 1, 32, 1, 0 + Option "$ADVSNDMNU_VLMODEL", "adl_volume_model", "AdlVolumeModels" } OptionMenu OPNOptions protected { - Title "$ADVSNDMNU_OPNMIDI" - Slider "$ADVSNDMNU_OPNNUMCHIPS", "opn_chips_count", 1, 32, 1, 0 + Title "$ADVSNDMNU_OPNMIDI" + Option "$ADVSNDMNU_OPNCORES", "opn_emulator_id", "OpnCores" + Option "$ADVSNDMNU_RUNPCMRATE", "opn_run_at_pcm_rate", "OnOff" + Slider "$ADVSNDMNU_OPNNUMCHIPS", "opn_chips_count", 1, 32, 1, 0 } /*======================================= diff --git a/wadsrc/static/xg.wopn b/wadsrc/static/xg.wopn index baeb34af216e049c5f71bc92971a95f90e133a29..ccb34797763eb46530ea50425cc2f9c52947b069 100644 GIT binary patch delta 9718 zcmcaNpSj^FGk>^$fS-}BlaZe{0}}%S(?ot%wg`8HV7G|L8zrR~9VVJQ6=Rys!m^CP z-<5HnmV#4JW_m_RKxSgzWJ6hTC0hm+#b{2}Z44no(VU#+42m4lt*px!!Z@QjIm{Vs z1sQKn&R|rRi(q9-VQ`WPV`ocYU}T%g$|A~O$T_oxC6&Q^CIiFdwT!wde3EQz>I^LG zlI#rE84Lv^IT_R$<~T4EYAFO|pVv$|T1Utj4fxvN@9@i?u7`ugNt`jx2^% zj1rR%GHEHZF$BupZ(v|zFjSn+z{t)}$R^Lm=FXt^g;7vT!8tW2wYX&RM{b$PTFgTH z>{4tDh1~KCoNNpNXP68odow$-$oBCyPM*N5F2~Qu@SVZRn4gc~D?=fJJP*S+h5~*t z#a6>4Gx;jBrVQIMhAQQF0k$*-MwZAn)@2MPOmnzcQyGlim_sK^u{g3Yr83G*j%RUX zVJl@an7o9=M36&*S^fw^5r;67{1k@94J;*-U$Q7Gi7_yX`*X8>W#G~Y=3{eXU}T=b z!SI!Vk#iac!)FGOEDpcPdaN#t3nmw{s>?}>bMIgf6cm+V)n{O17vW^kU|{4HXOv$L z3a`4!J6KHw*{3ita4`gNfvGtT3}KW1vZ||0;bKr`U||#HV$ft@)| z92jqEDFmgarcB<)Dy7Q6z+fUEC^8W3Un|?fp}q)1=-c*_*odF z7?`E_Kolc;FAIYfLl8$Fhypt^jNOr?yn|72@@#fTma|S_*ENdFiP|llizLCqLm- z;4SH72+&dp$V|?5N-UlnXeFV-!@$A7DJL(=(9dAN7thP0&ro0}&&OiVz`zjC#jMXz zB*qXh*_X>ihSisuA&Vh_(~F1U7uXgph2WgT0;j~{V#CQBxy2@L=2GAmc?3$IVDZTh z1f?eb;8I`_HDZ*QEW_={B4EjoH93*nkz4vBqpy}iSZ01s=H!WNl9P{d^NI6tGP5(7 z%kv5_urM$&cXKg_GR$#cR+#*nTTh%DBxfSW2a)T6%Ng*f%L%YBh%yxD^KmkBFqq2u zL6r1CQ^VZJrEn$kd|*>_Af_<%K}=zan7oTeMN!pI^g<(pFax7-FE@h=10x$h7lS$j zBLg27g9OO4GbjJ&QJ1?c%)OjJh&7mB;yZ&Nhk&s7cLqV8AU?k13`|cLzD;)Fb!5?A z%5iJ*|3D>W1|9|mt`L5P?+lC#@_Y=xKomE_HwGqmeqM%u3>+;?29wY6swfH@%AVt9 zU}0dC>FZ(;Wng9F>jLEj27ai`3nmNmsVE8>%AV$CU}RvF>F#0>WME|D;$To?U}Wav zU=U*fCHu({eCl#iO$#?>0oA9!oa}b(FIXbrlsJVk)K(l;Fy>(InYW>i-&=Q#Xu{tfgzBgK(3d8A&|j< z!?%Hfm7##68$``K*Y10#cP3oHn8 z_|@fnz-H<7fXp&C_5&GGzyLLiY0Bh{{OSq;yr42eREU+~69c257$?JL1}26eE{3lR zJa?I2XemrSBOs~800RHNFxWCMNl)No6k%Wz73O1*WMJVE;AZ&Cz{DxS%fJVUxq!*m z0xF8i3{pM3j9Lr~LR}mTS_}-F)7zP(7y_B6w?lGuz~lx2b-6w^#xw>-=3ZVdRR#u@ zzIG;2hCtSd?cgA0csco~fD2>5WG+EncX!ej$GGbyB5o2Tc#!#pw&%nULU??Ncz`)DE$RG?Vk>)rsESr2&P+d-t zg+Y>`$Xt+UQmF66O`$>7{o!I`7l{dNL?k6fzg9OfzOwLNrJ(M*N2tKoI!}8 zuYrjZq$EpA!7-;KfAV@EsmXRiZ1PflVAb5gAk~JfJs{O0EPV}3;Nq`!@(Lk!xqh$` zt{?^`aRx)yZdN8U1|gPSsFH(|-wL^~FVIr(Ps&f7d{It%@+&!I&R8u4*Sz$c%;L#{ zg3^;`33Ewki^(%^F))e?%xB3|uZH=jY^4z9=WI`u_`qxRDX(DNx`T3HJ1GonR

*8RjU=ZRy z)yq%;(o#3MTSP@spp-#`i&>PxP;4?UgDV5GVHgin1_L95A2i`Dn|wh;T{(o0IhDak zrI&?4m4V4TM1U!jfeEAp91Lr<6ao_SCijZSDVZ>oG6;jp1|#7~Fyk0Rk&Ig~IZD)? z4P2{VojhMuU0sNcNu41?S%{5EjiHE*pOc}2!HAo$i$N3QL?xm*_@pML z1Q(Vj7EPWgBB5;~Xe2eCf#Ev?lN^{bPiG8zrSCUlS9QW07ZI z;Ab#`P;(qWwF1ac=fs?x%>07M6Gg<7Y*f>YB&%mzT&&LtZz{nE9%(0&#_a4kVQ|FAtqR9`L#V0Qjm*ip*VPyCZ z(y2H3xVXAfKO{K>K$3$fMsn~;oh%?>!5PND%*N1K%h)+NP{NssQDX8m33WMsZniQ8 zL&Z)e24w~gF?n`Sk-@;n3eKvGZztcDP?Hxp&cMLTU?AYmz`(-5AixUBr3Mg6atn*t zWCck*#ZV3wQHCJqK(HKZ2&AZF`Ugr5Dd3`T@K;vWkJPvw?wyVU7cv z!{k+x>T;)<850>;mAOEL6eC+F8&fd@lPnh-!xx6~y}Y%P-%FZGuro6(WnkiFXJmK^ z@_H=TY>&j8$%e90S`0cYiagFN4518#49+Z!Aq-O=^RyJ)A{2@e!S$*N1B0Csn~1y+ zgE@ndAP>m>Orr9WcvTq+A-p*bEV7fCrPbxw*jT6Q_B~U@+pB;9yr};EQCMGPze;MMtSJ|D1)G(7z-;KgCXnu76w;_ zKmp!PwlGk=lmTmM`=x@L86iBbg96E$-iXP z1G5QZ;^Z0fCW1UtpvHb7j}#xnHwM;aOiGjG zEfgmIl~?3vzQV9fOCcmNGdo|wrKof=<8%paIR*x{`9eY*3=BN-0s`y|j4bm71h^R( znB_TmrZNagGM8y7_@?IMr(`BiX4Ds(XuvwTTS1zQje$9a$z}2y1xFTUNycB3KPs3C zxb%u~GBC)tNV6?t5ID+EG1*K}T~3;jp@E@TQJ#T8gh5!)ubq#RfkmN*k8>FVixwlx z z-FyPu7+5tJ_f7Uv0@-b?AkW0`gMq;aOxZF72r_)-V`C5#X86W8$AQ6Va=((gx+^0C z0|P@nYcHr;7GZB<5M^MfXYU6W3CuBC3XTN@Ifvej(#Dw3S)S{ursjM zGjQ{NWnimk;OF_qz{n8Kz{k$Ow3ShC@>v~aB`F351}O%ZvM*p|ybxtP>pC$nU|^U$N7qo~Cj%qHd;$3%V2Zblf$ZIDPR!pJ3MbFe)tKz3$05oGRrMXL zil>Z$$&1lC3b5&SNi{%xU1r!Y0V%GTG0-k%i?R1Hn4&`42}Ap~qPD_a1P$=ochAd{uomNF=wWmeNtaLzBEyv|U1@-!nw7RFNM zmdU$~6qy*@Cf_rXV^N4>lbB@e$inlAdCg>3Vf0pATGoON~m*07=AJsvj~BC9~cTJpEuT0!X^KbAxlfax6~uk z8Dx|;gN_*kBl~=Q29SC=kO_xdjO;QZ48Ir(c*VJSzA-TH$nf)jV=!PB;^Fzuz{12RH(A3}SAt8N zTPg-DDHg+EASA>i77OYy229R1b>v|6WPG5d;FAw3+$Uc#mFH%4V*H||;GCG0SUmY7 zxAbI5GkGS)(#ck4F3gh{JSR7rIWc!K1WZ0==E%Yv$`CM_+uT%upHDyn76*(s8D>rn zFgF$8K@wz&W0*8q-a={ec5?;hwG1=C>_6rT%m*2kO_sNC;yMAU5qwKaQWYkzw~*G- zwP7ey=i%pPXJC{DQxyz;Y=UeI91MLRiqV(R4HUqsd0^$r>NX6FYG8GYay$$S>AsR|yGH%dxQ z-d7|tIi{G0{RBfrNPe#2s*WTQ&NC;u+t;^2JBm=}_tYXUY_yHuWq z@e<>g$$q7d5XNK!M-EVe4FkhuSwlxpAwP;r7-mSD$CllN ztTvB2F>M}bi)W$%i?WFwg8_p)s0C@nAI~e)&Je;c&n2$TP{J?IEB>8p+Ea&{Z^h8h^keO^&DbK|y!H@zL@&)&9t1IPM7~L2cCeN#MJ-% zn1teVc|MR1TrCusy4BO6Cc^|iOqXY2k7MLQbfRD~RWqP6R47YE`%k0&C+z*F(SnGI z1rgN;g|<7gFurAYgi>00?2u<++{jQkIa|+>h0O@m)S>6V4#n-7(Xwc?EE+9~h#aWF JY!sc~0st^$fS-}BlaZe{0}}%S<3xVdiD6H<7-qAuEMwpaWMG)=%BU;B8_mgC z&Y;K~-O9R*A&e!Olf#^WQ;Sn;au=hzTo@}`3WJkW2s>K}10%!CRu)kPL-v_1EU63} zEes-)&ok;Oa7(hWsWY&!NU}3rXE5ZI-_qjDWT*U!$$|XhDm)B42I6vjd<@?htc-bi7`}ltu`_&ODBza| zQS1%u4U@k!YsxS!W2jP&7hp_dU}T7FWL(Bz!Z3%6F@<4{1Ea)b8x}_vhM5dACpWS< zvM{O!7P7-p@>14Nq!0gs}qCBWNubvB{K#V!4N@CX9h;WJ}!o@42(>@ z91LF=7#XGsFnniVp2$3LasaCfqv_S)NT@WfB*IG6M^n5Ep|c10yF77lSAR0}Gg% z9gz@Vkzl~`5fUYc2wI9V}9 zVzMfS9E+$NL%?Kr4o4P&XAFvydpI0fL{2cUO}@yXCmz7au$_TPvY(OR76TJQKO=)1 zg9-~n%49K4J@Ei0nA}&G+#Cmnl*!SY`eIBB-xw-*r?Rm(G6*sVF!Fx`#bMOs1)Pp7 z>>t@bPJYVi$j7`Bj)P*l|1ZF>hktq@@sCnFms6G1&Muy27ise`swlHj&{DD_Rk=0Q43^xNa z1EWk&7lQ}`E6f63E(URs1v4j`@_|*I=4N1IV3g_ZVi06tWMk)GP-9?ZX6FF=hmmV? z6`#7CL=!_2gQ1K>Bg19}MhSLsYG-0+U|?lX&}3LPc|V`JCMydAD+7Z94yb|xu?K!)kP|3v#N4U{K)mW?+(FFyi%MWin?FV(4yQ;slwZsHNbTQ<6XVqL9>NePK3vsXnl3?mz}6 zaRx)yE|6*wmhJ{7sGX;T)a4*bxDZOr7=)0N@C&=Jn`$ZeC*`Mtf@-23+hjg@MMkN~ zGlg{}G{oc?xEL7a1m-hvF)%Uk%0oPpHTj;f3pX3MCM?R#ODzHERAFG?Q5KTxW?Jz5IEg{6r_sR|ka`9&qEDGF&t`MC-%z7d+>qzOu9EKE!)9*q*e7>r~C7$sIQ zFfa%-FqktKFo-ZP++xsq!%(NC5S&;(xlvhe@((dVVFq~y27U%32sOt6RQgX=faHD~ z)=~zcE(TEsBZ(efhEN7&&6&ImR~ZsPxnCO;5Nwm<#r4E{`55*yuxt16al|t)G6XYo z>}L>7WMG)QMBJK-L4=XvKLhVl2DZsB#nrVs@umbV1s~VM6mz%Cw36b<4?{#I`$@=f zMldk5G4Sg#^h_?6aAsndIeCYKx*RVzTN#6)VmlLqG6RR0JUgf;Vc=n95My9YVziw6 zS3*sT|0n|kGlK!YE2yw!;AaI<1`tYHmtm5YLO^~_WpHI)Nd~Bn)8%2XVK9^m;$RVD z2oMY5U=d{qVh#Wmw+5_1Agyy880UggOA4qs1=sc~B!vYbMJEGmX9EKZ!yE^u6_d|M zs>_{ZW=v#YRptPdX^d>`Y)nNAOtKto44)Y|FLGX-EGT6v!OYCCl!1wxnUUcsNZ&%R z(H@DQx=vb2heeUck&PjQp^(9ejWL9Qk(r~N!IXhf3`}u;V)#3Gwv@U$Ge}9P1Tz;a zNQoFo36n5biR?B;IV}aZ2!)_Ta3w6Iq{A#ICkApPybzOTU}A$6VmxgOypvU>)#X^& zSiUd>$uY69d}Rn`5M*Xh0i|hX22}>eQiffVv!qP~ndCXx(;18yBskbr7?>rQB`2?! zR?!hH5}(G%5W*m6D8|Cd%3#PkzlFhtAy9yqlP!#aCyp^sOTnoqGd-ikFSU5Gqm0C4 zaTx_BrVEoTWz^-E*cgHs3Kn&ZGCrlk;^oavT1xlvh0LxROnX$m8Q3WKuJd?p4Lh76VY zARZ%&U;~2+!yE@zQ1XW~*rX?G$||r3t1!e)_LX(yV>V$n(NYM`NX*SFP6ahxBqwi` zmEdQH2c<3!4-OA41@HV4P-A5BPgx;Bh6phhWd>#*2{8^)296mF43lN#)a62_ik35^ zh{o}0i!CT>g0n_3X|m(6!{sl7_zh!LJ~8x^A%i*N+$=JN+`)O zFtE)R5@Kgy;E@*)U}IopnJ*x~&A`Ac&%rZ=fkA^oV{)g0JsT?ng9wAjQa?R=aJEDBwGoXZ#(n;4rW z&s9{`)?qMF;DHpJV5*3Li-&zXD3Nt?mN77wF_&p61g9njXXF=6o)|7R(ST*Lq%yZS z6AP%v0i{I2JWCfQrOE0_+ENUng8aOETnronC-?-y7}yxP`2@BxFzPVsOwLhKR#H)5 zV33;5&JOtzC*LKK-C878ZT$U$1r zo*_!gZZZtK4D##@4`K1bDhLp*%p^8jy43ir|<#?I+nD{{5lBCq&oW#isr6eaG z36+Pq?0Kk?vbPQc4}&}pg8a zGZ-@nfq9@%FAUR?$0HBw$WQ(sCZ}v;#=yurho9j;Lm`S;hDc`p4pYLXj+g5j12|M2 z!j&}5ahnHIr={SLnVvEEe7KCVyDbAFyNn3KFNOjJac-V(3=9l1{QTb-3>buXc)l|* zaxrpEJ{7Jj!642p6$6$OivdfD#WH|X27iPj4=6utDfr~WOQNy}d2YrU#u_aJ=ftGM zV$i?_DDUo$kY{4>oO~z3g-Z(5CIaQ_lF50I(vw{y6_`Q&r;JEP7N!+UD<OB1X?65dwV&LQB=Ya9RNfn%?LCRGaY%rB`^YO!LNKh7- zEKmo{w^ySbSy=2?>?TXaKr&iUZvPDoKIsGOL5I7W#D9G)*@l$0$b z7?{}Pg&F=cFtW;nk`9yNd~pzuK^{ag@G|gDURUo3D!9KvGFxzIQOab)Fp0^c4e~6E z5{wd?8V^ZE~=KnjFvu2dJjAn#C=_5YH>r&Je;N&n2$TP{JV3EB>8< z5j40?_jZUPo_2_HeqMT_2dJ|&`NnK{E`|Vx0I-lRSV(q`JS6ZO<~VYJLUFX|z`!sp znhr!44DYu&@`1{N?9AkBvv`eUBhATy{1TJHwnH;NtccjR-I0YMj3Er(-PpHXX>!&M zc@}WrX5mhy$?JE>3qYERkU{~uvjJ&7>{Oa;xl^8n@fYJSMDt;%BOj=>hqL*xQ)#mA zE@=M)WX+^q@~ljvOrq$Ghn-53Wp>N6Fv&2<(6lTV?SA0s&Wv_HhFSN6=rV-Tk&RoB kaTJuI<<%{D7Eo<9I)*VihA}#Z0UE9Z4Yv)wF^m&j0Dn{)iU0rr From a5b4d9d9a98c1dba03f4c492496f7bff9fc4c4ef Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Wed, 20 Jun 2018 01:28:55 +0300 Subject: [PATCH 37/58] ADLMIDI: Fix one small MSVC Win64 warning --- src/sound/adlmidi/adlmidi_opl3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/adlmidi/adlmidi_opl3.cpp b/src/sound/adlmidi/adlmidi_opl3.cpp index 49e2e76c5..5d15d002c 100644 --- a/src/sound/adlmidi/adlmidi_opl3.cpp +++ b/src/sound/adlmidi/adlmidi_opl3.cpp @@ -545,7 +545,7 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) #endif } cardsOP2[i].reset(chip); - chip->setChipId(i); + chip->setChipId((uint32_t)i); chip->setRate((uint32_t)PCM_RATE); if(runAtPcmRate) chip->setRunningAtPcmRate(true); From 43e1a2d249446fa47435a09464bb3b9eaeab905f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 21:05:36 +0200 Subject: [PATCH 38/58] - moved uViewHeight and uCameraPos to the viewpoint uniform struct. --- src/gl/renderer/gl_renderer.cpp | 2 +- src/gl/renderer/gl_renderstate.cpp | 3 --- src/gl/renderer/gl_renderstate.h | 6 ------ src/gl/scene/gl_drawinfo.cpp | 20 +++++++++++++++----- src/gl/scene/gl_drawinfo.h | 12 +----------- src/gl/scene/gl_portal.cpp | 18 +++++++++--------- src/gl/scene/gl_scene.cpp | 10 +++------- src/gl/scene/gl_skydome.cpp | 2 +- src/gl/shaders/gl_shader.cpp | 7 +++++-- src/gl/shaders/gl_shader.h | 12 +++++++----- src/hwrenderer/scene/hw_drawinfo.h | 7 +++++++ src/hwrenderer/scene/hw_viewpointuniforms.h | 11 ++++++++--- 12 files changed, 57 insertions(+), 53 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 55009e2ce..3a3f6b328 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -468,8 +468,8 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer) glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); HWViewpointUniforms matrices; + matrices.SetDefaults(); matrices.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); - matrices.mViewMatrix.loadIdentity(); matrices.CalcDependencies(); GLRenderer->mShaderManager->ApplyMatrices(&matrices, NORMAL_PASS); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 0d891efc8..c830499fd 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -96,7 +96,6 @@ void FRenderState::Reset() mInterpolationFactor = 0.0f; mColor.Set(1.0f, 1.0f, 1.0f, 1.0f); - mCameraPos.Set(0.0f, 0.0f, 0.0f, 0.0f); mGlowTop.Set(0.0f, 0.0f, 0.0f, 0.0f); mGlowBottom.Set(0.0f, 0.0f, 0.0f, 0.0f); mGlowTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f); @@ -163,7 +162,6 @@ bool FRenderState::ApplyShader() activeShader->muPalLightLevels.Set(static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8)); activeShader->muGlobVis.Set(GLRenderer->mGlobVis / 32.0f); activeShader->muTextureMode.Set(mTextureMode == TM_MODULATE && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode); - activeShader->muCameraPos.Set(mCameraPos.vec); activeShader->muLightParms.Set(mLightParms); activeShader->muFogColor.Set(mFogColor); activeShader->muObjectColor.Set(mObjectColor); @@ -176,7 +174,6 @@ bool FRenderState::ApplyShader() activeShader->muAlphaThreshold.Set(mAlphaThreshold); activeShader->muLightIndex.Set(-1); activeShader->muClipSplit.Set(mClipSplit); - activeShader->muViewHeight.Set(viewheight); activeShader->muSpecularMaterial.Set(mGlossiness, mSpecularLevel); if (mGlowEnabled) diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 21d0f2265..257ef56a8 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -103,7 +103,6 @@ class FRenderState FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer; FStateVec4 mNormal; FStateVec4 mColor; - FStateVec4 mCameraPos; FStateVec4 mGlowTop, mGlowBottom; FStateVec4 mGlowTopPlane, mGlowBottomPlane; FStateVec4 mSplitTopPlane, mSplitBottomPlane; @@ -344,11 +343,6 @@ public: mTextureMatrixEnabled = on; } - void SetCameraPos(float x, float y, float z) - { - mCameraPos.Set(x, z, y, 0); - } - void SetGlowParams(float *t, float *b) { mGlowTop.Set(t[0], t[1], t[2], t[3]); diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index da5c5fa38..c123007ed 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -44,6 +44,17 @@ #include "gl/renderer/gl_quaddrawer.h" #include "gl/dynlights/gl_lightbuffer.h" +class FDrawInfoList +{ +public: + TDeletingArray mList; + + + FDrawInfo * GetNew(); + void Release(FDrawInfo *); +}; + + static FDrawInfo * gl_drawinfo; FDrawInfoList di_list; @@ -228,11 +239,10 @@ void FDrawInfo::StartScene() //========================================================================== FDrawInfo *FDrawInfo::EndDrawInfo() { - FDrawInfo * di = gl_drawinfo; - - for(int i=0;idrawlists[i].Reset(); - gl_drawinfo=di->next; - di_list.Release(di); + assert(this == gl_drawinfo); + for(int i=0;i mList; - -public: - - FDrawInfo *GetNew(); - void Release(FDrawInfo *); -}; - void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index d516e37ee..1c3b7c3b1 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -158,7 +158,6 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn rendered_portals++; Clocker c(PortalAll); - *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms); if (usestencil) { if (!gl_portals) @@ -254,6 +253,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn glDisable(GL_DEPTH_TEST); } } + *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms); // save viewpoint savedvisibility = outer_di->Viewpoint.camera ? outer_di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); @@ -316,7 +316,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -370,7 +370,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -577,7 +577,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) vp.ViewActor = origin; inskybox = true; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); di->SetViewArea(); ClearClipper(di); @@ -684,7 +684,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); SetupCoverage(di); ClearClipper(di); @@ -740,7 +740,7 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); @@ -911,7 +911,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) vp.ViewActor = nullptr; MirrorFlag++; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); di->mClipper->Clear(); @@ -988,7 +988,7 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) } vp.ViewActor = nullptr; - di->SetupView(vp, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); @@ -1104,7 +1104,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) ClearScreen(di); return; } - gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); + di->SetCameraPos(vp.Pos); if (gltexture && gltexture->tex->isFullbright()) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 10c67c809..86cb361fb 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -104,10 +104,12 @@ void FDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float // Setup the view rotation matrix for the given viewpoint // //----------------------------------------------------------------------------- -void FDrawInfo::SetupView(FRenderViewpoint &vp, float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) +void FDrawInfo::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) { + auto &vp = Viewpoint; vp.SetViewAngle(r_viewwindow); SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); + SetCameraPos(vp.Pos); ApplyVPUniforms(); } @@ -182,8 +184,6 @@ void FDrawInfo::RenderScene(int recursion) glDepthMask(true); if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, this); - gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); - gl_RenderState.EnableFog(true); gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); @@ -287,12 +287,8 @@ void FDrawInfo::RenderScene(int recursion) void FDrawInfo::RenderTranslucent() { - const auto &vp = Viewpoint; - RenderAll.Clock(); - gl_RenderState.SetCameraPos(vp.Pos.X, vp.Pos.Y, vp.Pos.Z); - // final pass: translucent stuff gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold); gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 1ca523705..52db58670 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -230,7 +230,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bool oldClamp = gl_RenderState.SetDepthClamp(true); - di->SetupView(vp, 0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->bSkybox) diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index f50fb595a..d1ba1f8a6 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -311,7 +311,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muPalLightLevels.Init(hShader, "uPalLightLevels"); muGlobVis.Init(hShader, "uGlobVis"); muTextureMode.Init(hShader, "uTextureMode"); - muCameraPos.Init(hShader, "uCameraPos"); muLightParms.Init(hShader, "uLightAttr"); muClipSplit.Init(hShader, "uClipSplit"); muLightIndex.Init(hShader, "uLightIndex"); @@ -331,7 +330,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muClipHeightDirection.Init(hShader, "uClipHeightDirection"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); muSpecularMaterial.Init(hShader, "uSpecularMaterial"); - muViewHeight.Init(hShader, "uViewHeight"); muTimer.Init(hShader, "timer"); lights_index = glGetUniformLocation(hShader, "lights"); @@ -345,6 +343,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * normalviewmatrix_index = glGetUniformLocation(hShader, "NormalViewMatrix"); normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix"); quadmode_index = glGetUniformLocation(hShader, "uQuadMode"); + viewheight_index = glGetUniformLocation(hShader, "uViewHeight"); + camerapos_index = glGetUniformLocation(hShader, "uCameraPos"); if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { @@ -441,6 +441,9 @@ void FShader::ApplyMatrices(HWViewpointUniforms *u) glUniformMatrix4fv(projectionmatrix_index, 1, false, u->mProjectionMatrix.get()); glUniformMatrix4fv(viewmatrix_index, 1, false, u->mViewMatrix.get()); glUniformMatrix4fv(normalviewmatrix_index, 1, false, u->mNormalViewMatrix.get()); + + glUniform4fv(camerapos_index, 1, &u->mCameraPos[0]); + glUniform1i(viewheight_index, u->mViewHeight); } //========================================================================== diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 10503f527..26d66fe28 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -246,7 +246,6 @@ class FShader FBufferedUniform1i muPalLightLevels; FBufferedUniform1f muGlobVis; FBufferedUniform1i muTextureMode; - FBufferedUniform4f muCameraPos; FBufferedUniform4f muLightParms; FBufferedUniform2f muClipSplit; FBufferedUniform1i muLightIndex; @@ -265,17 +264,20 @@ class FShader FBufferedUniform1f muClipHeight; FBufferedUniform1f muClipHeightDirection; FBufferedUniform1f muAlphaThreshold; - FBufferedUniform1i muViewHeight; FBufferedUniform2f muSpecularMaterial; FBufferedUniform1f muTimer; int lights_index; - int projectionmatrix_index; - int viewmatrix_index; - int normalviewmatrix_index; int modelmatrix_index; int normalmodelmatrix_index; int texturematrix_index; + + int projectionmatrix_index; + int viewmatrix_index; + int normalviewmatrix_index; + int viewheight_index; + int camerapos_index; + public: int vertexmatrix_index; int texcoordmatrix_index; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 11c3c8e5f..f62c12a62 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -1,6 +1,7 @@ #pragma once #include +#include "vectors.h" #include "r_defs.h" #include "r_utility.h" #include "hw_viewpointuniforms.h" @@ -151,6 +152,12 @@ private: void RenderThings(subsector_t * sub, sector_t * sector); void DoSubsector(subsector_t * sub); public: + + void SetCameraPos(const DVector3 &pos) + { + VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f }; + } + void RenderBSPNode(void *node); void ClearBuffers(); diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 1fdbcc4d1..7dc76317e 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -1,13 +1,17 @@ #pragma once #include "r_data/matrix.h" +#include "r_utility.h" struct HWViewpointUniforms { - VSMatrix mProjectionMatrix; - VSMatrix mViewMatrix; - VSMatrix mNormalViewMatrix; + VSMatrix mProjectionMatrix; + VSMatrix mViewMatrix; + VSMatrix mNormalViewMatrix; + FVector4 mCameraPos; + FVector4 mClipLine; + int mViewHeight; void CalcDependencies() { mNormalViewMatrix.computeNormalMatrix(mViewMatrix); @@ -18,6 +22,7 @@ struct HWViewpointUniforms mProjectionMatrix.loadIdentity(); mViewMatrix.loadIdentity(); mNormalViewMatrix.loadIdentity(); + mViewHeight = viewheight; } }; From d5af93911943ef4190a927e10bef52311ac2942d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 21:32:38 +0200 Subject: [PATCH 39/58] - more uniforms moved. (not working yet.) --- src/gl/renderer/gl_renderer.h | 2 -- src/gl/renderer/gl_renderstate.cpp | 3 --- src/gl/scene/gl_scene.cpp | 2 -- src/gl/shaders/gl_shader.cpp | 6 ++++-- src/gl/shaders/gl_shader.h | 4 ++-- src/hwrenderer/scene/hw_drawinfo.cpp | 19 +++++++++++++++++++ src/hwrenderer/scene/hw_viewpointuniforms.h | 19 ++++++++----------- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 4cd28c730..46c097ba1 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -114,8 +114,6 @@ public: float mSceneClearColor[3]; - float mGlobVis = 0.0f; - FGLRenderer(OpenGLFrameBuffer *fb); ~FGLRenderer() ; diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index c830499fd..a692145d3 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -42,7 +42,6 @@ void gl_SetTextureMode(int type); FRenderState gl_RenderState; CVAR(Bool, gl_direct_state_change, true, 0) -CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE) static VSMatrix identityMatrix(1); @@ -159,8 +158,6 @@ bool FRenderState::ApplyShader() activeShader->muDesaturation.Set(mDesaturation / 255.f); activeShader->muFogEnabled.Set(fogset); - activeShader->muPalLightLevels.Set(static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8)); - activeShader->muGlobVis.Set(GLRenderer->mGlobVis / 32.0f); activeShader->muTextureMode.Set(mTextureMode == TM_MODULATE && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode); activeShader->muLightParms.Set(mLightParms); activeShader->muFogColor.Set(mFogColor); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 86cb361fb..7db0911ed 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -487,8 +487,6 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came { R_SetupFrame (mainvp, r_viewwindow, camera); - mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility); - // Render (potentially) multiple views for stereo 3d float viewShift[3]; const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode(); diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index d1ba1f8a6..953b31e90 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -308,8 +308,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muDesaturation.Init(hShader, "uDesaturationFactor"); muFogEnabled.Init(hShader, "uFogEnabled"); - muPalLightLevels.Init(hShader, "uPalLightLevels"); - muGlobVis.Init(hShader, "uGlobVis"); muTextureMode.Init(hShader, "uTextureMode"); muLightParms.Init(hShader, "uLightAttr"); muClipSplit.Init(hShader, "uClipSplit"); @@ -345,6 +343,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * quadmode_index = glGetUniformLocation(hShader, "uQuadMode"); viewheight_index = glGetUniformLocation(hShader, "uViewHeight"); camerapos_index = glGetUniformLocation(hShader, "uCameraPos"); + pallightlevels_index = glGetUniformLocation(hShader, "uPalLightLevels"); + globvis_index = glGetUniformLocation(hShader, "uGlobVis"); if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { @@ -444,6 +444,8 @@ void FShader::ApplyMatrices(HWViewpointUniforms *u) glUniform4fv(camerapos_index, 1, &u->mCameraPos[0]); glUniform1i(viewheight_index, u->mViewHeight); + glUniform1i(pallightlevels_index, u->mPalLightLevels); + glUniform1f(globvis_index, u->mGlobVis); } //========================================================================== diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 26d66fe28..9a0b52eb1 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -243,8 +243,6 @@ class FShader FBufferedUniform1f muDesaturation; FBufferedUniform1i muFogEnabled; - FBufferedUniform1i muPalLightLevels; - FBufferedUniform1f muGlobVis; FBufferedUniform1i muTextureMode; FBufferedUniform4f muLightParms; FBufferedUniform2f muClipSplit; @@ -277,6 +275,8 @@ class FShader int normalviewmatrix_index; int viewheight_index; int camerapos_index; + int pallightlevels_index; + int globvis_index; public: int vertexmatrix_index; diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 75943691d..2f3936d87 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -35,6 +35,9 @@ #include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/utility/hw_cvars.h" +EXTERN_CVAR(Float, r_visibility) +CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE) + sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); //========================================================================== @@ -211,3 +214,19 @@ angle_t HWDrawInfo::FrustumAngle() return a1; } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWViewpointUniforms::SetDefaults() +{ + mProjectionMatrix.loadIdentity(); + mViewMatrix.loadIdentity(); + mNormalViewMatrix.loadIdentity(); + mViewHeight = viewheight; + mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; + mPalLightLevels = static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8); + +} diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 7dc76317e..2727fbd25 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -11,18 +11,15 @@ struct HWViewpointUniforms FVector4 mCameraPos; FVector4 mClipLine; + float mGlobVis; + int mPalLightLevels; int mViewHeight; - void CalcDependencies() - { - mNormalViewMatrix.computeNormalMatrix(mViewMatrix); - } - void SetDefaults() - { - mProjectionMatrix.loadIdentity(); - mViewMatrix.loadIdentity(); - mNormalViewMatrix.loadIdentity(); - mViewHeight = viewheight; - } + void CalcDependencies() + { + mNormalViewMatrix.computeNormalMatrix(mViewMatrix); + } + + void SetDefaults(); }; From e7531bb57960258ce20ab0678e28f639e215047b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 21:44:53 +0200 Subject: [PATCH 40/58] - fixed camerapos setup. --- src/gl/scene/gl_drawinfo.h | 2 +- src/gl/scene/gl_portal.cpp | 14 +++++++------- src/gl/scene/gl_scene.cpp | 7 +++---- src/gl/scene/gl_skydome.cpp | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index cd37b4552..2859be999 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -114,7 +114,7 @@ struct FDrawInfo : public HWDrawInfo // These should go into hwrenderer later. void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); - void SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror); + void SetupView(float vx, float vy, float vz, bool mirror, bool planemirror); static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 1c3b7c3b1..22687eaa7 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -316,7 +316,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -370,7 +370,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -577,7 +577,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) vp.ViewActor = origin; inskybox = true; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); di->SetViewArea(); ClearClipper(di); @@ -684,7 +684,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); SetupCoverage(di); ClearClipper(di); @@ -740,7 +740,7 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); @@ -911,7 +911,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) vp.ViewActor = nullptr; MirrorFlag++; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); di->mClipper->Clear(); @@ -988,7 +988,7 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) } vp.ViewActor = nullptr; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); gl_RenderState.SetClipLine(glport->lines[0]->mDestination); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 7db0911ed..72f3d8915 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -104,7 +104,7 @@ void FDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float // Setup the view rotation matrix for the given viewpoint // //----------------------------------------------------------------------------- -void FDrawInfo::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror) +void FDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool planemirror) { auto &vp = Viewpoint; vp.SetViewAngle(r_viewwindow); @@ -506,12 +506,11 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came // Stereo mode specific perspective projection di->VPUniforms.mProjectionMatrix = eye->GetProjection(fov, ratio, fovratio); - vp.SetViewAngle(r_viewwindow); // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); ScopedViewShifter viewShifter(vp.Pos, viewShift); - di->SetViewMatrix(vp.HWAngles, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); - di->ApplyVPUniforms(); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); + di->ProcessScene(toscreen); diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 52db58670..9f3ef2bd7 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -230,7 +230,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bool oldClamp = gl_RenderState.SetDepthClamp(true); - di->SetupView(0, 0, 0, vp.Angles.Yaw, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(0, 0, 0, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->bSkybox) From 80a90289386e6e1136bf1df37aedff7b2d7be23f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 22:45:05 +0200 Subject: [PATCH 41/58] - mirror clip planes moved - not working yet. --- src/gl/renderer/gl_renderstate.cpp | 21 --------------------- src/gl/renderer/gl_renderstate.h | 2 -- src/gl/scene/gl_portal.cpp | 18 +----------------- src/gl/scene/gl_portal.h | 2 -- src/gl/scene/gl_scene.cpp | 12 ++++++++++++ src/gl/shaders/gl_shader.cpp | 6 ++++-- src/gl/shaders/gl_shader.h | 4 ++-- src/hwrenderer/scene/hw_drawinfo.h | 6 ++++++ src/hwrenderer/scene/hw_viewpointuniforms.h | 8 +++++--- 9 files changed, 30 insertions(+), 49 deletions(-) diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index a692145d3..1e7addf62 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -80,8 +80,6 @@ void FRenderState::Reset() mLightParms[0] = mLightParms[1] = mLightParms[2] = 0.0f; mLightParms[3] = -1.f; mSpecialEffect = EFF_NONE; - mClipHeight = 0.f; - mClipHeightDirection = 0.f; mGlossiness = 0.0f; mSpecularLevel = 0.0f; mShaderTimer = 0.0f; @@ -165,8 +163,6 @@ bool FRenderState::ApplyShader() activeShader->muObjectColor2.Set(mObjectColor2); activeShader->muDynLightColor.Set(mDynColor.vec); activeShader->muInterpolationFactor.Set(mInterpolationFactor); - activeShader->muClipHeight.Set(mClipHeight); - activeShader->muClipHeightDirection.Set(mClipHeightDirection); activeShader->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.); activeShader->muAlphaThreshold.Set(mAlphaThreshold); activeShader->muLightIndex.Set(-1); @@ -304,20 +300,3 @@ void FRenderState::ApplyLightIndex(int index) } activeShader->muLightIndex.Set(index); } - -void FRenderState::SetClipHeight(float height, float direction) -{ - mClipHeight = height; - mClipHeightDirection = direction; - - if (gl.flags & RFL_NO_CLIP_PLANES) return; - - if (direction != 0.f) - { - glEnable(GL_CLIP_DISTANCE0); - } - else - { - glDisable(GL_CLIP_DISTANCE0); // GL_CLIP_PLANE0 is the same value so no need to make a distinction - } -} diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 257ef56a8..00338dbe1 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -196,8 +196,6 @@ public: return mClipLineShouldBeActive; } - void SetClipHeight(float height, float direction); - void SetNormal(FVector3 norm) { mNormal.Set(norm.X, norm.Y, norm.Z, 0.f); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 22687eaa7..192f17945 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -745,29 +745,13 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) di->UpdateCurrentMapSection(); - gl_RenderState.SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); + di->SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); di->DrawScene(DM_PORTAL); - gl_RenderState.SetClipHeight(0.f, 0.f); PlaneMirrorFlag--; PlaneMirrorMode = old_pm; std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); } -void GLPlaneMirrorPortal::PushState() -{ - planestack.Push(gl_RenderState.GetClipHeight()); - planestack.Push(gl_RenderState.GetClipHeightDirection()); - gl_RenderState.SetClipHeight(0.f, 0.f); -} - -void GLPlaneMirrorPortal::PopState() -{ - float d, f; - planestack.Pop(d); - planestack.Pop(f); - gl_RenderState.SetClipHeight(f, d); -} - //----------------------------------------------------------------------------- // // Common code for line to line and mirror portals diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index a6d153aa7..fd0e0fbf0 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -302,8 +302,6 @@ protected: virtual void DrawContents(FDrawInfo *di); virtual void * GetSource() const { return origin; } virtual const char *GetName(); - virtual void PushState(); - virtual void PopState(); secplane_t * origin; public: diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 72f3d8915..f39c428df 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -76,6 +76,18 @@ void FDrawInfo::ApplyVPUniforms() { VPUniforms.CalcDependencies(); GLRenderer->mShaderManager->ApplyMatrices(&VPUniforms, NORMAL_PASS); + + if (!(gl.flags & RFL_NO_CLIP_PLANES)) + { + if (VPUniforms.mClipHeightDirection != 0.f) + { + glEnable(GL_CLIP_DISTANCE0); + } + else + { + glDisable(GL_CLIP_DISTANCE0); + } + } } //----------------------------------------------------------------------------- diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 953b31e90..d14d1ee34 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -324,8 +324,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muSplitTopPlane.Init(hShader, "uSplitTopPlane"); muClipLine.Init(hShader, "uClipLine"); muInterpolationFactor.Init(hShader, "uInterpolationFactor"); - muClipHeight.Init(hShader, "uClipHeight"); - muClipHeightDirection.Init(hShader, "uClipHeightDirection"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); muSpecularMaterial.Init(hShader, "uSpecularMaterial"); muTimer.Init(hShader, "timer"); @@ -345,6 +343,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * camerapos_index = glGetUniformLocation(hShader, "uCameraPos"); pallightlevels_index = glGetUniformLocation(hShader, "uPalLightLevels"); globvis_index = glGetUniformLocation(hShader, "uGlobVis"); + clipheight_index = glGetUniformLocation(hShader, "uClipHeight"); + clipheightdirection_index = glGetUniformLocation(hShader, "uClipHeightDirection"); if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { @@ -446,6 +446,8 @@ void FShader::ApplyMatrices(HWViewpointUniforms *u) glUniform1i(viewheight_index, u->mViewHeight); glUniform1i(pallightlevels_index, u->mPalLightLevels); glUniform1f(globvis_index, u->mGlobVis); + glUniform1f(clipheight_index, u->mClipHeight); + glUniform1f(clipheightdirection_index, u->mClipHeightDirection); } //========================================================================== diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 9a0b52eb1..4a84cf0ed 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -259,8 +259,6 @@ class FShader FUniform4f muSplitTopPlane; FUniform4f muClipLine; FBufferedUniform1f muInterpolationFactor; - FBufferedUniform1f muClipHeight; - FBufferedUniform1f muClipHeightDirection; FBufferedUniform1f muAlphaThreshold; FBufferedUniform2f muSpecularMaterial; FBufferedUniform1f muTimer; @@ -277,6 +275,8 @@ class FShader int camerapos_index; int pallightlevels_index; int globvis_index; + int clipheight_index; + int clipheightdirection_index; public: int vertexmatrix_index; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index f62c12a62..a2464931d 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -158,6 +158,12 @@ public: VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f }; } + void SetClipHeight(float h, float d) + { + VPUniforms.mClipHeight = h; + VPUniforms.mClipHeightDirection = d; + } + void RenderBSPNode(void *node); void ClearBuffers(); diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 2727fbd25..39fa1dbb7 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -11,9 +11,11 @@ struct HWViewpointUniforms FVector4 mCameraPos; FVector4 mClipLine; - float mGlobVis; - int mPalLightLevels; - int mViewHeight; + float mGlobVis = 1.f; + int mPalLightLevels = 0; + int mViewHeight = 0; + float mClipHeight = 0.f; + float mClipHeightDirection = 0.f; void CalcDependencies() { From 7f0e29db48f2433b0f608a51c4ba5d127705f3b0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 22:50:33 +0200 Subject: [PATCH 42/58] - fixed clip plane setup. This had to be reordered so that the clip plane gets set before applying the uniforms. --- src/gl/scene/gl_portal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 192f17945..c337d113c 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -740,12 +740,12 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; PlaneMirrorFlag++; + di->SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); - di->SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); di->DrawScene(DM_PORTAL); PlaneMirrorFlag--; PlaneMirrorMode = old_pm; From 82d7e5970f18d37f7249100acf5e937affc6bd29 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 22 Jun 2018 23:49:39 +0200 Subject: [PATCH 43/58] - moved the clipline uniform to the viewpoint buffer. This was the final one of the per-scene values that could be taken out of the render state. --- src/gl/renderer/gl_renderstate.cpp | 14 +----- src/gl/renderer/gl_renderstate.h | 55 --------------------- src/gl/scene/gl_drawinfo.cpp | 8 ++- src/gl/scene/gl_flats.cpp | 4 +- src/gl/scene/gl_portal.cpp | 33 ++----------- src/gl/scene/gl_portal.h | 2 - src/gl/scene/gl_scene.cpp | 3 +- src/gl/shaders/gl_shader.cpp | 2 +- src/gl/shaders/gl_shader.h | 2 +- src/hwrenderer/scene/hw_drawinfo.cpp | 3 +- src/hwrenderer/scene/hw_drawinfo.h | 13 +++++ src/hwrenderer/scene/hw_viewpointuniforms.h | 3 ++ 12 files changed, 35 insertions(+), 107 deletions(-) diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 1e7addf62..258fc9a05 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -61,7 +61,7 @@ static void matrixToGL(const VSMatrix &mat, int loc) void FRenderState::Reset() { mTextureEnabled = true; - mClipLineShouldBeActive = mClipLineEnabled = mSplitEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false; + mSplitEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false; mColorMask[0] = mColorMask[1] = mColorMask[2] = mColorMask[3] = true; currentColorMask[0] = currentColorMask[1] = currentColorMask[2] = currentColorMask[3] = true; mFogColor.d = -1; @@ -99,7 +99,6 @@ void FRenderState::Reset() mGlowBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f); mSplitTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f); mSplitBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f); - mClipLine.Set(0.0f, 0.0f, 0.0f, 0.0f); mDynColor.Set(0.0f, 0.0f, 0.0f, 0.0f); mEffectState = 0; activeShader = nullptr; @@ -201,17 +200,6 @@ bool FRenderState::ApplyShader() activeShader->currentsplitstate = 0; } - if (mClipLineEnabled) - { - activeShader->muClipLine.Set(mClipLine.vec); - activeShader->currentcliplinestate = 1; - } - else if (activeShader->currentcliplinestate) - { - activeShader->muClipLine.Set(-10000000.0, 0, 0, 0); - activeShader->currentcliplinestate = 0; - } - if (mTextureMatrixEnabled) { matrixToGL(mTextureMatrix, activeShader->texturematrix_index); diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 00338dbe1..e1dc4bec0 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -79,8 +79,6 @@ class FRenderState uint8_t mFogEnabled; bool mGlowEnabled; bool mSplitEnabled; - bool mClipLineEnabled; - bool mClipLineShouldBeActive; bool mBrightmapEnabled; bool mColorMask[4]; bool currentColorMask[4]; @@ -96,7 +94,6 @@ class FRenderState bool mTextureMatrixEnabled; bool mLastDepthClamp; float mInterpolationFactor; - float mClipHeight, mClipHeightDirection; float mGlossiness, mSpecularLevel; float mShaderTimer; @@ -106,7 +103,6 @@ class FRenderState FStateVec4 mGlowTop, mGlowBottom; FStateVec4 mGlowTopPlane, mGlowBottomPlane; FStateVec4 mSplitTopPlane, mSplitBottomPlane; - FStateVec4 mClipLine; PalEntry mFogColor; PalEntry mObjectColor; PalEntry mObjectColor2; @@ -171,31 +167,6 @@ public: mCurrentVertexBuffer = NULL; } - float GetClipHeight() - { - return mClipHeight; - } - - float GetClipHeightDirection() - { - return mClipHeightDirection; - } - - FStateVec4 &GetClipLine() - { - return mClipLine; - } - - bool GetClipLineState() - { - return mClipLineEnabled; - } - - bool GetClipLineShouldBeActive() - { - return mClipLineShouldBeActive; - } - void SetNormal(FVector3 norm) { mNormal.Set(norm.X, norm.Y, norm.Z, 0.f); @@ -300,32 +271,6 @@ public: } } - void SetClipLine(line_t *line) - { - mClipLine.Set(line->v1->fX(), line->v1->fY(), line->Delta().X, line->Delta().Y); - } - - void EnableClipLine(bool on) - { - if (!(gl.flags & RFL_NO_CLIP_PLANES)) - { - mClipLineEnabled = on; - if (on) - { - glEnable(GL_CLIP_DISTANCE0); - } - else - { - glDisable(GL_CLIP_DISTANCE0); - } - } - else - { - // this needs to be flagged because in this case per-sector plane rendering needs to be disabled if a clip plane is active. - mClipLineShouldBeActive = on; - } - } - void EnableBrightmap(bool on) { mBrightmapEnabled = on; diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index c123007ed..b8a3d7c70 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -207,7 +207,13 @@ FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUnifo di->mVBO = GLRenderer->mVBO; di->mClipper = &staticClipper; di->Viewpoint = parentvp; - if (uniforms) di->VPUniforms = *uniforms; + if (uniforms) + { + di->VPUniforms = *uniforms; + // The clip planes will never be inherited from the parent drawinfo. + di->VPUniforms.mClipLine.X = -1000001.f; + di->VPUniforms.mClipHeight = 0; + } else di->VPUniforms.SetDefaults(); di->mClipper->SetViewpoint(di->Viewpoint); staticClipper.Clear(); diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index f7b0729f1..d50a00f9c 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -166,7 +166,7 @@ void FDrawInfo::ProcessLights(GLFlat *flat, bool istrans) { flat->dynlightindex = GLRenderer->mLights->GetIndexPtr(); - if (flat->sector->ibocount > 0 && !gl_RenderState.GetClipLineShouldBeActive()) + if (flat->sector->ibocount > 0 && !ClipLineShouldBeActive()) { SetupSectorLights(flat, GLPASS_LIGHTSONLY, nullptr); } @@ -215,7 +215,7 @@ void FDrawInfo::DrawSubsectors(GLFlat *flat, int pass, bool processlights, bool if (iboindex >= 0) { - if (vcount > 0 && !gl_RenderState.GetClipLineShouldBeActive()) + if (vcount > 0 && !ClipLineShouldBeActive()) { if (processlights) SetupSectorLights(flat, GLPASS_ALL, &dli); drawcalls.Clock(); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index c337d113c..2b4930976 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -758,29 +758,6 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) // //----------------------------------------------------------------------------- -void GLLinePortal::PushState() -{ - FStateVec4 &v = gl_RenderState.GetClipLine(); - planestack.Push(v.vec[0]); - planestack.Push(v.vec[1]); - planestack.Push(v.vec[2]); - planestack.Push(v.vec[3]); - planestack.Push(gl_RenderState.GetClipLineState()); - gl_RenderState.EnableClipLine(false); -} - -void GLLinePortal::PopState() -{ - FStateVec4 &v = gl_RenderState.GetClipLine(); - float e = 0; - planestack.Pop(e); - planestack.Pop(v.vec[3]); - planestack.Pop(v.vec[2]); - planestack.Pop(v.vec[1]); - planestack.Pop(v.vec[0]); - gl_RenderState.EnableClipLine(e != 0); -} - int GLLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) { line_t *linedef = seg->linedef; @@ -793,7 +770,7 @@ int GLLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) int GLLinePortal::ClipSubsector(subsector_t *sub) { - // this seg is completely behind the mirror! + // this seg is completely behind the mirror for(unsigned int i=0;inumlines;i++) { if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; @@ -895,6 +872,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) vp.ViewActor = nullptr; MirrorFlag++; + di->SetClipLine(linedef); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); di->mClipper->Clear(); @@ -904,10 +882,7 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); - gl_RenderState.SetClipLine(linedef); - gl_RenderState.EnableClipLine(true); di->DrawScene(DM_PORTAL); - gl_RenderState.EnableClipLine(false); MirrorFlag--; } @@ -972,13 +947,11 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) } vp.ViewActor = nullptr; + di->SetClipLine(glport->lines[0]->mDestination); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(di); - gl_RenderState.SetClipLine(glport->lines[0]->mDestination); - gl_RenderState.EnableClipLine(true); di->DrawScene(DM_PORTAL); - gl_RenderState.EnableClipLine(false); } void GLLineToLinePortal::RenderAttached(FDrawInfo *di) diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index fd0e0fbf0..b7d04b971 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -180,8 +180,6 @@ struct GLLinePortal : public GLPortal virtual int ClipSubsector(subsector_t *sub); virtual int ClipPoint(const DVector2 &pos); virtual bool NeedCap() { return false; } - virtual void PushState(); - virtual void PopState(); }; diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index f39c428df..057683caf 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -79,7 +79,7 @@ void FDrawInfo::ApplyVPUniforms() if (!(gl.flags & RFL_NO_CLIP_PLANES)) { - if (VPUniforms.mClipHeightDirection != 0.f) + if (VPUniforms.mClipHeightDirection != 0.f || VPUniforms.mClipLine.X > -10000000.0f) { glEnable(GL_CLIP_DISTANCE0); } @@ -90,6 +90,7 @@ void FDrawInfo::ApplyVPUniforms() } } + //----------------------------------------------------------------------------- // // Setup the modelview matrix diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index d14d1ee34..a5a7a7249 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -322,7 +322,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muGlowTopPlane.Init(hShader, "uGlowTopPlane"); muSplitBottomPlane.Init(hShader, "uSplitBottomPlane"); muSplitTopPlane.Init(hShader, "uSplitTopPlane"); - muClipLine.Init(hShader, "uClipLine"); muInterpolationFactor.Init(hShader, "uInterpolationFactor"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); muSpecularMaterial.Init(hShader, "uSpecularMaterial"); @@ -345,6 +344,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * globvis_index = glGetUniformLocation(hShader, "uGlobVis"); clipheight_index = glGetUniformLocation(hShader, "uClipHeight"); clipheightdirection_index = glGetUniformLocation(hShader, "uClipHeightDirection"); + clipline_index = glGetUniformLocation(hShader, "uClipLine"); if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 4a84cf0ed..cb63b502d 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -257,7 +257,6 @@ class FShader FUniform4f muGlowTopPlane; FUniform4f muSplitBottomPlane; FUniform4f muSplitTopPlane; - FUniform4f muClipLine; FBufferedUniform1f muInterpolationFactor; FBufferedUniform1f muAlphaThreshold; FBufferedUniform2f muSpecularMaterial; @@ -277,6 +276,7 @@ class FShader int globvis_index; int clipheight_index; int clipheightdirection_index; + int clipline_index; public: int vertexmatrix_index; diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 2f3936d87..3b73d72c6 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -226,7 +226,8 @@ void HWViewpointUniforms::SetDefaults() mViewMatrix.loadIdentity(); mNormalViewMatrix.loadIdentity(); mViewHeight = viewheight; - mGlobVis = R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; + mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; mPalLightLevels = static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8); + mClipLine.X = -10000000.0f; } diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index a2464931d..2c55145c9 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -5,6 +5,7 @@ #include "r_defs.h" #include "r_utility.h" #include "hw_viewpointuniforms.h" +#include "v_video.h" struct FSectorPortalGroup; @@ -162,6 +163,18 @@ public: { VPUniforms.mClipHeight = h; VPUniforms.mClipHeightDirection = d; + VPUniforms.mClipLine.X = -1000001.f; + } + + void SetClipLine(line_t *line) + { + VPUniforms.mClipLine = { (float)line->v1->fX(), (float)line->v1->fY(), (float)line->Delta().X, (float)line->Delta().Y }; + VPUniforms.mClipHeight = 0; + } + + bool ClipLineShouldBeActive() + { + return (screen->hwcaps & RFL_NO_CLIP_PLANES) && VPUniforms.mClipLine.X > -1000000.f; } void RenderBSPNode(void *node); diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 39fa1dbb7..27a78aef4 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -25,3 +25,6 @@ struct HWViewpointUniforms void SetDefaults(); }; + + + From 573758bc556a4ca7cf8e6c588e99259d21c9c6eb Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 23 Jun 2018 12:44:48 +0300 Subject: [PATCH 44/58] - removed obsolete code from Cocoa backend --- src/posix/cocoa/i_input.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 125587b79..0c631713e 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -487,11 +487,6 @@ void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) outEvent->data1 = static_cast( viewPos.x); outEvent->data2 = static_cast(frameHeight - viewPos.y); - // Compensate letterbox adjustment done by cross-platform code - // More elegant solution is a bit problematic due to HiDPI/Retina support - // What does this do? Add 0? - //outEvent->data2 += (screen->GetTrueHeight() - screen->ClientHeight()) / 2; - screen->ScaleCoordsFromWindow(outEvent->data1, outEvent->data2); } From 009acf3fb256f7a6ce2d56484f590f08a0275b6d Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 23 Jun 2018 12:45:44 +0300 Subject: [PATCH 45/58] - added window position and size restoration to Cocoa backend --- src/posix/cocoa/i_video.mm | 57 +++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 39f744e27..869dc4e3b 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -49,7 +49,6 @@ #include "st_console.h" #include "v_text.h" #include "version.h" -#include "videomodes.h" #include "gl/renderer/gl_renderer.h" #include "gl/system/gl_framebuffer.h" @@ -96,6 +95,11 @@ EXTERN_CVAR(Int, vid_defwidth) EXTERN_CVAR(Int, vid_defheight) EXTERN_CVAR(Bool, fullscreen) +CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_w, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_h, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + CUSTOM_CVAR(Bool, vid_autoswitch, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { Printf("You must restart " GAMENAME " to apply graphics switching mode\n"); @@ -154,6 +158,15 @@ namespace [super setTitle:m_title]; } +- (void)frameDidChange:(NSNotification*)notification +{ + const NSRect frame = [self frame]; + win_x = frame.origin.x; + win_y = frame.origin.y; + win_w = frame.size.width; + win_h = frame.size.height; +} + @end @@ -266,11 +279,8 @@ namespace CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) { - static const CGFloat TEMP_WIDTH = VideoModes[0].width - 1; - static const CGFloat TEMP_HEIGHT = VideoModes[0].height - 1; - CocoaWindow* const window = [CocoaWindow alloc]; - [window initWithContentRect:NSMakeRect(0, 0, TEMP_WIDTH, TEMP_HEIGHT) + [window initWithContentRect:NSMakeRect(0, 0, vid_defwidth, vid_defheight) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; @@ -278,6 +288,16 @@ CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) [window makeFirstResponder:appCtrl]; [window setAcceptsMouseMovedEvents:YES]; + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:window + selector:@selector(frameDidChange:) + name:NSWindowDidEndLiveResizeNotification + object:nil]; + [nc addObserver:window + selector:@selector(frameDidChange:) + name:NSWindowDidMoveNotification + object:nil]; + return window; } @@ -441,13 +461,6 @@ void CocoaVideo::SetWindowTitle(const char* title) void CocoaVideo::SetFullscreenMode() { - NSScreen* screen = [m_window screen]; - - const NSRect screenFrame = [screen frame]; - const NSRect displayRect = vid_hidpi - ? [screen convertRectToBacking:screenFrame] - : screenFrame; - if (!m_fullscreen) { [m_window setLevel:LEVEL_FULLSCREEN]; @@ -456,16 +469,12 @@ void CocoaVideo::SetFullscreenMode() [m_window setHidesOnDeactivate:YES]; } + const NSRect screenFrame = [[m_window screen] frame]; [m_window setFrame:screenFrame display:YES]; } void CocoaVideo::SetWindowedMode() { - const NSSize windowPixelSize = NSMakeSize(vid_defwidth, vid_defheight); - const NSSize windowSize = vid_hidpi - ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] - : windowPixelSize; - if (m_fullscreen) { [m_window setLevel:LEVEL_WINDOWED]; @@ -474,20 +483,18 @@ void CocoaVideo::SetWindowedMode() [m_window setHidesOnDeactivate:NO]; } - [m_window setContentSize:windowSize]; - [m_window center]; + const bool isFrameValid = win_x >= 0 && win_y >= 0 && win_w > 320 && win_h > 200; + const NSRect frameSize = isFrameValid + ? NSMakeRect(win_x, win_y, win_w, win_h) + : NSMakeRect(0, 0, vid_defwidth, vid_defheight); + + [m_window setFrame:frameSize display:YES]; [m_window enterFullscreenOnZoom]; [m_window exitAppOnClose]; } void CocoaVideo::SetMode(const bool fullscreen, const bool hiDPI) { - if (fullscreen == m_fullscreen - && hiDPI == m_hiDPI) - { - return; - } - NSOpenGLView* const glView = [m_window contentView]; [glView setWantsBestResolutionOpenGLSurface:hiDPI]; From 282fdac6602aa2e408ded7b89a1570b19430692b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Jun 2018 13:25:23 +0200 Subject: [PATCH 46/58] - made the static portal state a struct inside the Renderer object --- src/CMakeLists.txt | 1 + src/gl/models/gl_models.cpp | 4 +- src/gl/renderer/gl_lightdata.cpp | 2 +- src/gl/renderer/gl_renderer.cpp | 8 +- src/gl/renderer/gl_renderer.h | 5 + src/gl/scene/gl_drawinfo.cpp | 4 +- src/gl/scene/gl_drawinfo.h | 5 - src/gl/scene/gl_portal.cpp | 335 ++++++--------------------- src/gl/scene/gl_portal.h | 121 +++------- src/gl/scene/gl_scene.cpp | 44 +--- src/gl/scene/gl_skydome.cpp | 4 +- src/gl/scene/gl_walls_draw.cpp | 37 +-- src/hwrenderer/scene/hw_drawinfo.cpp | 36 +++ src/hwrenderer/scene/hw_drawinfo.h | 2 + src/hwrenderer/scene/hw_portal.cpp | 157 +++++++++++++ src/hwrenderer/scene/hw_portal.h | 85 +++++++ 16 files changed, 420 insertions(+), 430 deletions(-) create mode 100644 src/hwrenderer/scene/hw_portal.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a298824a..75e2d36fa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -841,6 +841,7 @@ set( FASTMATH_SOURCES hwrenderer/scene/hw_drawlist.cpp hwrenderer/scene/hw_clipper.cpp hwrenderer/scene/hw_flats.cpp + hwrenderer/scene/hw_portal.cpp hwrenderer/scene/hw_renderhacks.cpp hwrenderer/scene/hw_sky.cpp hwrenderer/scene/hw_sprites.cpp diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index b92b832a8..97c5c09fd 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -65,7 +65,7 @@ void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, con if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) { glEnable(GL_CULL_FACE); - glFrontFace((mirrored ^ GLPortal::isMirrored()) ? GL_CCW : GL_CW); + glFrontFace((mirrored ^ GLRenderer->mPortalState.isMirrored()) ? GL_CCW : GL_CW); } gl_RenderState.mModelMatrix = objectToWorldMatrix; @@ -91,7 +91,7 @@ void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectTo if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])) { glEnable(GL_CULL_FACE); - glFrontFace((mirrored ^ GLPortal::isMirrored()) ? GL_CW : GL_CCW); + glFrontFace((mirrored ^ GLRenderer->mPortalState.isMirrored()) ? GL_CW : GL_CCW); } gl_RenderState.mModelMatrix = objectToWorldMatrix; diff --git a/src/gl/renderer/gl_lightdata.cpp b/src/gl/renderer/gl_lightdata.cpp index 1810f4bfc..7566c0f12 100644 --- a/src/gl/renderer/gl_lightdata.cpp +++ b/src/gl/renderer/gl_lightdata.cpp @@ -173,7 +173,7 @@ void gl_SetFog(int lightlevel, int rellight, bool fullbright, const FColormap *c } // Make fog a little denser when inside a skybox - if (GLPortal::inskybox) fogdensity+=fogdensity/2; + if (GLRenderer->mPortalState.inskybox) fogdensity+=fogdensity/2; // no fog in enhanced vision modes! diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 3a3f6b328..a605025d2 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -150,6 +150,8 @@ void FGLRenderer::Initialize(int width, int height) mCustomPostProcessShaders = new FCustomPostProcessShaders(); // needed for the core profile, because someone decided it was a good idea to remove the default VAO. + glGenQueries(1, &PortalQueryObject); + glGenVertexArrays(1, &mVAOID); glBindVertexArray(mVAOID); FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID"); @@ -164,14 +166,10 @@ void FGLRenderer::Initialize(int width, int height) SetupLevel(); mShaderManager = new FShaderManager; mSamplerManager = new FSamplerManager; - - GLPortal::Initialize(); } FGLRenderer::~FGLRenderer() { - GLPortal::Shutdown(); - FlushModels(); AActor::DeleteAllAttachedLights(); FMaterial::FlushAll(); @@ -186,6 +184,8 @@ FGLRenderer::~FGLRenderer() glBindVertexArray(0); glDeleteVertexArrays(1, &mVAOID); } + if (PortalQueryObject != 0) glDeleteQueries(1, &PortalQueryObject); + if (swdrawer) delete swdrawer; if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 46c097ba1..2aebd0169 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -6,6 +6,7 @@ #include "vectors.h" #include "r_renderer.h" #include "r_data/matrix.h" +#include "hwrenderer/scene/hw_portal.h" #include "gl/dynlights/gl_shadowmap.h" #include @@ -73,6 +74,8 @@ public: FSamplerManager *mSamplerManager; unsigned int mFBID; unsigned int mVAOID; + unsigned int PortalQueryObject; + int mOldFBID; FGLRenderBuffers *mBuffers; @@ -110,6 +113,8 @@ public: FLightBuffer *mLights; SWSceneDrawer *swdrawer = nullptr; + FPortalSceneState mPortalState; + bool buffersActive = false; float mSceneClearColor[3]; diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index b8a3d7c70..de3cd91ee 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -265,7 +265,7 @@ FDrawInfo *FDrawInfo::EndDrawInfo() void FDrawInfo::SetupFloodStencil(wallseg * ws) { - int recursion = GLPortal::GetRecursion(); + int recursion = GLRenderer->mPortalState.GetRecursion(); // Create stencil glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil @@ -297,7 +297,7 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws) void FDrawInfo::ClearFloodStencil(wallseg * ws) { - int recursion = GLPortal::GetRecursion(); + int recursion = GLRenderer->mPortalState.GetRecursion(); glStencilOp(GL_KEEP,GL_KEEP,GL_DECR); gl_RenderState.EnableTexture(false); diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index 2859be999..ec0a6ed85 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -112,11 +112,6 @@ struct FDrawInfo : public HWDrawInfo void EndDrawScene(sector_t * viewsector); void DrawEndScene2D(sector_t * viewsector); - // These should go into hwrenderer later. - void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); - void SetupView(float vx, float vy, float vz, bool mirror, bool planemirror); - - static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); FDrawInfo *EndDrawInfo(); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 2b4930976..f8d560f27 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -58,42 +58,12 @@ EXTERN_CVAR(Bool, gl_portals) EXTERN_CVAR(Bool, gl_noquery) EXTERN_CVAR(Int, r_mirror_recursions) -TArray GLPortal::portals; -TArray GLPortal::planestack; -int GLPortal::recursion; -int GLPortal::MirrorFlag; -int GLPortal::PlaneMirrorFlag; -int GLPortal::renderdepth; -int GLPortal::PlaneMirrorMode; -GLuint GLPortal::QueryObject; - -bool GLPortal::inskybox; - -UniqueList UniqueSkies; -UniqueList UniqueHorizons; -UniqueList UniquePlaneMirrors; - -int skyboxrecursion = 0; - //========================================================================== // // // //========================================================================== - -void GLPortal::BeginScene() -{ - UniqueSkies.Clear(); - UniqueHorizons.Clear(); - UniquePlaneMirrors.Clear(); -} - -//========================================================================== -// -// -// -//========================================================================== -void GLPortal::ClearScreen(FDrawInfo *di) +void GLPortal::ClearScreen(HWDrawInfo *di) { bool multi = !!glIsEnabled(GL_MULTISAMPLE); @@ -142,17 +112,13 @@ void GLPortal::DrawPortalStencil() } - - - - //----------------------------------------------------------------------------- // // Start // //----------------------------------------------------------------------------- -bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawInfo **pDi) +bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) { *pDi = nullptr; rendered_portals++; @@ -166,7 +132,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn } // Create stencil - glStencilFunc(GL_EQUAL,recursion,~0); // create stencil + glStencilFunc(GL_EQUAL, mState->recursion, ~0); // create stencil glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer @@ -182,19 +148,15 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn if (!NeedDepthBuffer()) doquery = false; // too much overhead and nothing to gain. else if (gl_noquery) doquery = false; - // If occlusion query is supported let's use it to avoid rendering portals that aren't visible - if (QueryObject) - { - glBeginQuery(GL_SAMPLES_PASSED, QueryObject); - } - else doquery = false; // some kind of error happened + // Use occlusion query to avoid rendering portals that aren't visible + glBeginQuery(GL_SAMPLES_PASSED, GLRenderer->PortalQueryObject); DrawPortalStencil(); glEndQuery(GL_SAMPLES_PASSED); // Clear Z-buffer - glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil + glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil glDepthMask(true); // enable z-buffer again glDepthRange(1, 1); @@ -210,17 +172,14 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn GLuint sampleCount; - if (QueryObject) - { - glGetQueryObjectuiv(QueryObject, GL_QUERY_RESULT, &sampleCount); + glGetQueryObjectuiv(GLRenderer->PortalQueryObject, GL_QUERY_RESULT, &sampleCount); - if (sampleCount == 0) // not visible - { - // restore default stencil op. - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_EQUAL, recursion, ~0); // draw sky into stencil - return false; - } + if (sampleCount == 0) // not visible + { + // restore default stencil op. + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil + return false; } } else @@ -232,7 +191,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn glDepthMask(true); DrawPortalStencil(); - glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil + glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil gl_RenderState.EnableTexture(true); // glColorMask(1,1,1,1); @@ -241,7 +200,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn glDepthMask(false); // don't write to Z-buffer! } } - recursion++; + mState->recursion++; } @@ -262,14 +221,13 @@ bool GLPortal::Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawIn PrevPortal = GLRenderer->mCurrentPortal; GLRenderer->mCurrentPortal = this; - if (PrevPortal != nullptr) PrevPortal->PushState(); return true; } -inline void GLPortal::ClearClipper(FDrawInfo *di) +inline void GLPortal::ClearClipper(HWDrawInfo *di) { - auto outer_di = di->next; + auto outer_di = static_cast(di)->next; DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); di->mClipper->Clear(); @@ -300,15 +258,14 @@ inline void GLPortal::ClearClipper(FDrawInfo *di) // End // //----------------------------------------------------------------------------- -void GLPortal::End(FDrawInfo *di, bool usestencil) +void GLPortal::End(HWDrawInfo *di, bool usestencil) { bool needdepth = NeedDepthBuffer(); Clocker c(PortalAll); - if (PrevPortal != nullptr) PrevPortal->PopState(); GLRenderer->mCurrentPortal = PrevPortal; - di = di->EndDrawInfo(); + di = static_cast(di)->EndDrawInfo(); di->ApplyVPUniforms(); if (usestencil) { @@ -316,7 +273,6 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); { ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics @@ -341,7 +297,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) glDepthFunc(GL_LEQUAL); glDepthRange(0, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); - glStencilFunc(GL_EQUAL, recursion, ~0); // draw sky into stencil + glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil DrawPortalStencil(); glDepthFunc(GL_LESS); @@ -349,11 +305,11 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) gl_RenderState.EnableTexture(true); gl_RenderState.SetEffect(EFF_NONE); } // glColorMask(1, 1, 1, 1); - recursion--; + mState->recursion--; // restore old stencil op. glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); - glStencilFunc(GL_EQUAL,recursion,~0); // draw sky into stencil + glStencilFunc(GL_EQUAL, mState->recursion,~0); // draw sky into stencil } else { @@ -370,7 +326,7 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); // This draws a valid z-buffer into the stencil's contents to ensure it // doesn't get overwritten by the level's geometry. @@ -394,141 +350,6 @@ void GLPortal::End(FDrawInfo *di, bool usestencil) } -//----------------------------------------------------------------------------- -// -// StartFrame -// -//----------------------------------------------------------------------------- -void GLPortal::StartFrame() -{ - GLPortal * p = nullptr; - portals.Push(p); - if (renderdepth == 0) - { - inskybox = false; - screen->instack[sector_t::floor] = screen->instack[sector_t::ceiling] = 0; - } - renderdepth++; -} - - -//----------------------------------------------------------------------------- -// -// printing portal info -// -//----------------------------------------------------------------------------- - -static bool gl_portalinfo; - -CCMD(gl_portalinfo) -{ - gl_portalinfo = true; -} - -static FString indent; - -//----------------------------------------------------------------------------- -// -// EndFrame -// -//----------------------------------------------------------------------------- - -void GLPortal::EndFrame(FDrawInfo *outer_di) -{ - GLPortal * p; - - if (gl_portalinfo) - { - Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), portals.Size(), renderdepth, indent.GetChars()); - indent += " "; - } - - // Only use occlusion query if there are more than 2 portals. - // Otherwise there's too much overhead. - // (And don't forget to consider the separating nullptr pointers!) - bool usequery = portals.Size() > 2 + (unsigned)renderdepth; - - while (portals.Pop(p) && p) - { - if (gl_portalinfo) - { - Printf("%sProcessing %s, depth = %d, query = %d\n", indent.GetChars(), p->GetName(), renderdepth, usequery); - } - if (p->lines.Size() > 0) - { - p->RenderPortal(true, usequery, outer_di); - } - delete p; - } - renderdepth--; - - if (gl_portalinfo) - { - indent.Truncate(long(indent.Len()-2)); - Printf("%s}\n", indent.GetChars()); - if (portals.Size() == 0) gl_portalinfo = false; - } -} - - -//----------------------------------------------------------------------------- -// -// Renders one sky portal without a stencil. -// In more complex scenes using a stencil for skies can severely stall -// the GPU and there's rarely more than one sky visible at a time. -// -//----------------------------------------------------------------------------- -bool GLPortal::RenderFirstSkyPortal(int recursion, FDrawInfo *outer_di) -{ - GLPortal * p; - GLPortal * best = nullptr; - unsigned bestindex=0; - - // Find the one with the highest amount of lines. - // Normally this is also the one that saves the largest amount - // of time by drawing it before the scene itself. - for(int i = portals.Size()-1; i >= 0 && portals[i] != nullptr; --i) - { - p=portals[i]; - if (p->lines.Size() > 0 && p->IsSky()) - { - // Cannot clear the depth buffer inside a portal recursion - if (recursion && p->NeedDepthBuffer()) continue; - - if (!best || p->lines.Size()>best->lines.Size()) - { - best=p; - bestindex=i; - } - } - } - - if (best) - { - portals.Delete(bestindex); - best->RenderPortal(false, false, outer_di); - delete best; - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// -// FindPortal -// -//----------------------------------------------------------------------------- - -GLPortal * GLPortal::FindPortal(const void * src) -{ - int i=portals.Size()-1; - - while (i>=0 && portals[i] && portals[i]->GetSource()!=src) i--; - return i>=0? portals[i]:nullptr; -} - - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // @@ -545,23 +366,23 @@ GLPortal * GLPortal::FindPortal(const void * src) // //----------------------------------------------------------------------------- -void GLSkyboxPortal::DrawContents(FDrawInfo *di) +void GLSkyboxPortal::DrawContents(HWDrawInfo *di) { - int old_pm = PlaneMirrorMode; + int old_pm = mState->PlaneMirrorMode; - if (skyboxrecursion >= 3) + if (mState->skyboxrecursion >= 3) { ClearScreen(di); return; } auto &vp = di->Viewpoint; - skyboxrecursion++; + mState->skyboxrecursion++; AActor *origin = portal->mSkybox; portal->mFlags |= PORTSF_INSKYBOX; vp.extralight = 0; - PlaneMirrorMode = 0; + mState->PlaneMirrorMode = 0; bool oldclamp = gl_RenderState.SetDepthClamp(false); vp.Pos = origin->InterpolatedPosition(vp.TicFrac); @@ -576,20 +397,20 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) vp.ViewActor = origin; - inskybox = true; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + mState->inskybox = true; + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); di->SetViewArea(); ClearClipper(di); di->UpdateCurrentMapSection(); - di->DrawScene(DM_SKYPORTAL); + static_cast(di)->DrawScene(DM_PORTAL); portal->mFlags &= ~PORTSF_INSKYBOX; - inskybox = false; + mState->inskybox = false; gl_RenderState.SetDepthClamp(oldclamp); - skyboxrecursion--; + mState->skyboxrecursion--; - PlaneMirrorMode = old_pm; + mState->PlaneMirrorMode = old_pm; } //----------------------------------------------------------------------------- @@ -611,7 +432,7 @@ void GLSkyboxPortal::DrawContents(FDrawInfo *di) GLSectorStackPortal *FSectorPortalGroup::GetRenderState() { - if (glportal == nullptr) glportal = new GLSectorStackPortal(this); + if (glportal == nullptr) glportal = new GLSectorStackPortal(&GLRenderer->mPortalState, this); return glportal; } @@ -631,7 +452,7 @@ GLSectorStackPortal::~GLSectorStackPortal() // //----------------------------------------------------------------------------- -static uint8_t SetCoverage(FDrawInfo *di, void *node) +static uint8_t SetCoverage(HWDrawInfo *di, void *node) { if (level.nodes.Size() == 0) { @@ -651,7 +472,7 @@ static uint8_t SetCoverage(FDrawInfo *di, void *node) } } -void GLSectorStackPortal::SetupCoverage(FDrawInfo *di) +void GLSectorStackPortal::SetupCoverage(HWDrawInfo *di) { for(unsigned i=0; iViewpoint; @@ -684,7 +505,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) // avoid recursions! if (origin->plane != -1) screen->instack[origin->plane]++; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); SetupCoverage(di); ClearClipper(di); @@ -697,7 +518,7 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) di->mClipper->SetBlocked(true); } - di->DrawScene(DM_PORTAL); + static_cast(di)->DrawScene(DM_PORTAL); if (origin->plane != -1) screen->instack[origin->plane]--; } @@ -718,9 +539,9 @@ void GLSectorStackPortal::DrawContents(FDrawInfo *di) // //----------------------------------------------------------------------------- -void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) +void GLPlaneMirrorPortal::DrawContents(HWDrawInfo *di) { - if (renderdepth > r_mirror_recursions) + if (mState->renderdepth > r_mirror_recursions) { ClearScreen(di); return; @@ -729,7 +550,7 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); auto &vp = di->Viewpoint; - int old_pm = PlaneMirrorMode; + int old_pm = mState->PlaneMirrorMode; // the player is always visible in a mirror. vp.showviewer = true; @@ -737,18 +558,18 @@ void GLPlaneMirrorPortal::DrawContents(FDrawInfo *di) double planez = origin->ZatPoint(vp.Pos); vp.Pos.Z = 2 * planez - vp.Pos.Z; vp.ViewActor = nullptr; - PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; + mState->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; - PlaneMirrorFlag++; - di->SetClipHeight(planez, PlaneMirrorMode < 0 ? -1.f : 1.f); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + mState->PlaneMirrorFlag++; + di->SetClipHeight(planez, mState->PlaneMirrorMode < 0 ? -1.f : 1.f); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); ClearClipper(di); di->UpdateCurrentMapSection(); - di->DrawScene(DM_PORTAL); - PlaneMirrorFlag--; - PlaneMirrorMode = old_pm; + static_cast(di)->DrawScene(DM_PORTAL); + mState->PlaneMirrorFlag--; + mState->PlaneMirrorMode = old_pm; std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); } @@ -803,9 +624,9 @@ int GLLinePortal::ClipPoint(const DVector2 &pos) // R_EnterMirror // //----------------------------------------------------------------------------- -void GLMirrorPortal::DrawContents(FDrawInfo *di) +void GLMirrorPortal::DrawContents(HWDrawInfo *di) { - if (renderdepth>r_mirror_recursions) + if (mState->renderdepth>r_mirror_recursions) { ClearScreen(di); return; @@ -864,16 +685,16 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) FVector2 v(-dx, dy); v.MakeUnit(); - vp.Pos.X+= v[1] * renderdepth / 2; - vp.Pos.Y+= v[0] * renderdepth / 2; + vp.Pos.X+= v[1] * mState->renderdepth / 2; + vp.Pos.Y+= v[0] * mState->renderdepth / 2; } vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; vp.ViewActor = nullptr; - MirrorFlag++; + mState->MirrorFlag++; di->SetClipLine(linedef); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); di->mClipper->Clear(); @@ -882,9 +703,9 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); - di->DrawScene(DM_PORTAL); + static_cast(di)->DrawScene(DM_PORTAL); - MirrorFlag--; + mState->MirrorFlag--; } //----------------------------------------------------------------------------- @@ -902,10 +723,10 @@ void GLMirrorPortal::DrawContents(FDrawInfo *di) // // //----------------------------------------------------------------------------- -void GLLineToLinePortal::DrawContents(FDrawInfo *di) +void GLLineToLinePortal::DrawContents(HWDrawInfo *di) { // TODO: Handle recursion more intelligently - if (renderdepth>r_mirror_recursions) + if (mState->renderdepth>r_mirror_recursions) { ClearScreen(di); return; @@ -948,13 +769,13 @@ void GLLineToLinePortal::DrawContents(FDrawInfo *di) vp.ViewActor = nullptr; di->SetClipLine(glport->lines[0]->mDestination); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); ClearClipper(di); - di->DrawScene(DM_PORTAL); + static_cast(di)->DrawScene(DM_PORTAL); } -void GLLineToLinePortal::RenderAttached(FDrawInfo *di) +void GLLineToLinePortal::RenderAttached(HWDrawInfo *di) { di->ProcessActorsInPortal(glport, di->in_area); } @@ -981,8 +802,8 @@ void GLLineToLinePortal::RenderAttached(FDrawInfo *di) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -GLHorizonPortal::GLHorizonPortal(GLHorizonInfo * pt, FRenderViewpoint &vp, bool local) - : GLPortal(local) +GLHorizonPortal::GLHorizonPortal(FPortalSceneState *s, GLHorizonInfo * pt, FRenderViewpoint &vp, bool local) + : GLPortal(s, local) { origin = pt; @@ -1045,8 +866,9 @@ GLHorizonPortal::GLHorizonPortal(GLHorizonInfo * pt, FRenderViewpoint &vp, bool // GLHorizonPortal::DrawContents // //----------------------------------------------------------------------------- -void GLHorizonPortal::DrawContents(FDrawInfo *di) +void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi) { + auto di = static_cast(hwdi); Clocker c(PortalAll); FMaterial * gltexture; @@ -1117,7 +939,7 @@ void GLHorizonPortal::DrawContents(FDrawInfo *di) // //----------------------------------------------------------------------------- -void GLEEHorizonPortal::DrawContents(FDrawInfo *di) +void GLEEHorizonPortal::DrawContents(HWDrawInfo *di) { auto &vp = di->Viewpoint; sector_t *sector = portal->mOrigin; @@ -1126,7 +948,7 @@ void GLEEHorizonPortal::DrawContents(FDrawInfo *di) { GLSkyInfo skyinfo; skyinfo.init(sector->sky, 0); - GLSkyPortal sky(&skyinfo, true); + GLSkyPortal sky(mState, &skyinfo, true); sky.DrawContents(di); } if (sector->GetTexture(sector_t::ceiling) != skyflatnum) @@ -1140,7 +962,7 @@ void GLEEHorizonPortal::DrawContents(FDrawInfo *di) { horz.plane.Texheight = vp.Pos.Z + fabs(horz.plane.Texheight); } - GLHorizonPortal ceil(&horz, di->Viewpoint, true); + GLHorizonPortal ceil(mState, &horz, di->Viewpoint, true); ceil.DrawContents(di); } if (sector->GetTexture(sector_t::floor) != skyflatnum) @@ -1154,26 +976,11 @@ void GLEEHorizonPortal::DrawContents(FDrawInfo *di) { horz.plane.Texheight = vp.Pos.Z - fabs(horz.plane.Texheight); } - GLHorizonPortal floor(&horz, di->Viewpoint, true); + GLHorizonPortal floor(mState, &horz, di->Viewpoint, true); floor.DrawContents(di); } } -void GLPortal::Initialize() -{ - assert(0 == QueryObject); - glGenQueries(1, &QueryObject); -} - -void GLPortal::Shutdown() -{ - if (0 != QueryObject) - { - glDeleteQueries(1, &QueryObject); - QueryObject = 0; - } -} - const char *GLSkyPortal::GetName() { return "Sky"; } const char *GLSkyboxPortal::GetName() { return "Skybox"; } const char *GLSectorStackPortal::GetName() { return "Sectorstack"; } diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index b7d04b971..d94461765 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -44,92 +44,27 @@ #include "hwrenderer/scene/hw_drawstructs.h" #include "hwrenderer/scene/hw_portal.h" -extern UniqueList UniqueSkies; -extern UniqueList UniqueHorizons; -extern UniqueList UniquePlaneMirrors; struct GLEEHorizonPortal; class GLPortal : public IPortal { - static TArray portals; - static int recursion; - static unsigned int QueryObject; -protected: - static TArray planestack; - static int MirrorFlag; - static int PlaneMirrorFlag; - static int renderdepth; - -public: - static int PlaneMirrorMode; - static int inupperstack; - static bool inskybox; private: void DrawPortalStencil(); - AActor * savedviewactor; ActorRenderFlags savedvisibility; GLPortal *PrevPortal; TArray mPrimIndices; protected: - TArray lines; int level; - GLPortal(bool local = false) { if (!local) portals.Push(this); } - virtual ~GLPortal() { } + GLPortal(FPortalSceneState *state, bool local = false) : IPortal(state, local) { } - bool Start(bool usestencil, bool doquery, FDrawInfo *outer_di, FDrawInfo **pDi); - void End(FDrawInfo *di, bool usestencil); - virtual void DrawContents(FDrawInfo *di)=0; - virtual void * GetSource() const =0; // GetSource MUST be implemented! - void ClearClipper(FDrawInfo *di); - virtual bool IsSky() { return false; } - virtual bool NeedCap() { return true; } - virtual bool NeedDepthBuffer() { return true; } - void ClearScreen(FDrawInfo *di); - virtual const char *GetName() = 0; - virtual void PushState() {} - virtual void PopState() {} - -public: - - void RenderPortal(bool usestencil, bool doquery, FDrawInfo *outer_di) - { - // Start may perform an occlusion query. If that returns 0 there - // is no need to draw the stencil's contents and there's also no - // need to restore the affected area becasue there is none! - FDrawInfo *di; - if (Start(usestencil, doquery, outer_di, &di)) - { - DrawContents(di); - End(di, usestencil); - } - } - - void AddLine(GLWall * l) - { - lines.Push(*l); - } - - static int GetRecursion() - { - return recursion; - } - - static bool isMirrored() { return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); } - - virtual void RenderAttached(FDrawInfo *di) {} - - static void BeginScene(); - static void StartFrame(); - static bool RenderFirstSkyPortal(int recursion, FDrawInfo *outer_di); - static void EndFrame(FDrawInfo *outer_di); - static GLPortal * FindPortal(const void * src); - - static void Initialize(); - static void Shutdown(); + bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) override; + void End(HWDrawInfo *di, bool usestencil) override; + void ClearClipper(HWDrawInfo *di); + void ClearScreen(HWDrawInfo *di); }; struct GLLinePortal : public GLPortal @@ -140,14 +75,14 @@ struct GLLinePortal : public GLPortal angle_t angv1, angv2; // for quick comparisons with a line or subsector - GLLinePortal(line_t *line) + GLLinePortal(FPortalSceneState *state, line_t *line) : GLPortal(state) { v1 = line->v1; v2 = line->v2; CalcDelta(); } - GLLinePortal(FLinePortalSpan *line) + GLLinePortal(FPortalSceneState *state, FLinePortalSpan *line) : GLPortal(state) { if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr) { @@ -189,14 +124,14 @@ struct GLMirrorPortal : public GLLinePortal line_t * linedef; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return linedef; } virtual const char *GetName(); public: - GLMirrorPortal(line_t * line) - : GLLinePortal(line) + GLMirrorPortal(FPortalSceneState *state, line_t * line) + : GLLinePortal(state, line) { linedef=line; } @@ -207,16 +142,16 @@ struct GLLineToLinePortal : public GLLinePortal { FLinePortalSpan *glport; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return glport; } virtual const char *GetName(); virtual line_t *ClipLine() { return line(); } - virtual void RenderAttached(FDrawInfo *di); + virtual void RenderAttached(HWDrawInfo *di); public: - GLLineToLinePortal(FLinePortalSpan *ll) - : GLLinePortal(ll) + GLLineToLinePortal(FPortalSceneState *state, FLinePortalSpan *ll) + : GLLinePortal(state, ll) { glport = ll; } @@ -228,7 +163,7 @@ struct GLSkyboxPortal : public GLPortal FSectorPortal * portal; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return portal; } virtual bool IsSky() { return true; } virtual const char *GetName(); @@ -236,7 +171,7 @@ protected: public: - GLSkyboxPortal(FSectorPortal * pt) + GLSkyboxPortal(FPortalSceneState *state, FSectorPortal * pt) : GLPortal(state) { portal=pt; } @@ -250,7 +185,7 @@ struct GLSkyPortal : public GLPortal friend struct GLEEHorizonPortal; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return origin; } virtual bool IsSky() { return true; } virtual bool NeedDepthBuffer() { return false; } @@ -259,8 +194,8 @@ protected: public: - GLSkyPortal(GLSkyInfo * pt, bool local = false) - : GLPortal(local) + GLSkyPortal(FPortalSceneState *state, GLSkyInfo * pt, bool local = false) + : GLPortal(state, local) { origin=pt; } @@ -274,7 +209,7 @@ struct GLSectorStackPortal : public GLPortal TArray subsectors; protected: virtual ~GLSectorStackPortal(); - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return origin; } virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. virtual const char *GetName(); @@ -282,11 +217,11 @@ protected: public: - GLSectorStackPortal(FSectorPortalGroup *pt) + GLSectorStackPortal(FPortalSceneState *state, FSectorPortalGroup *pt) : GLPortal(state) { origin=pt; } - void SetupCoverage(FDrawInfo *di); + void SetupCoverage(HWDrawInfo *di); void AddSubsector(subsector_t *sub) { subsectors.Push(sub); @@ -297,14 +232,14 @@ public: struct GLPlaneMirrorPortal : public GLPortal { protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return origin; } virtual const char *GetName(); secplane_t * origin; public: - GLPlaneMirrorPortal(secplane_t * pt) + GLPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : GLPortal(state) { origin=pt; } @@ -320,7 +255,7 @@ struct GLHorizonPortal : public GLPortal friend struct GLEEHorizonPortal; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return origin; } virtual bool NeedDepthBuffer() { return false; } virtual bool NeedCap() { return false; } @@ -328,7 +263,7 @@ protected: public: - GLHorizonPortal(GLHorizonInfo * pt, FRenderViewpoint &vp, bool local = false); + GLHorizonPortal(FPortalSceneState *state, GLHorizonInfo * pt, FRenderViewpoint &vp, bool local = false); }; struct GLEEHorizonPortal : public GLPortal @@ -336,7 +271,7 @@ struct GLEEHorizonPortal : public GLPortal FSectorPortal * portal; protected: - virtual void DrawContents(FDrawInfo *di); + virtual void DrawContents(HWDrawInfo *di); virtual void * GetSource() const { return portal; } virtual bool NeedDepthBuffer() { return false; } virtual bool NeedCap() { return false; } @@ -344,7 +279,7 @@ protected: public: - GLEEHorizonPortal(FSectorPortal *pt) + GLEEHorizonPortal(FPortalSceneState *state, FSectorPortal *pt) : GLPortal(state) { portal=pt; } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 057683caf..123fd47be 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -50,6 +50,7 @@ #include "gl/renderer/gl_renderbuffers.h" #include "gl/data/gl_vertexbuffer.h" #include "hwrenderer/scene/hw_clipper.h" +#include "hwrenderer/scene/hw_portal.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" #include "gl/stereo3d/gl_stereo3d.h" @@ -91,41 +92,6 @@ void FDrawInfo::ApplyVPUniforms() } -//----------------------------------------------------------------------------- -// -// Setup the modelview matrix -// -//----------------------------------------------------------------------------- - -void FDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) -{ - float mult = mirror? -1:1; - float planemult = planemirror? -level.info->pixelstretch : level.info->pixelstretch; - - VPUniforms.mViewMatrix.loadIdentity(); - VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); - VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); - VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); - VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult , -vy); - VPUniforms.mViewMatrix.scale(-mult, planemult, 1); -} - - -//----------------------------------------------------------------------------- -// -// SetupView -// Setup the view rotation matrix for the given viewpoint -// -//----------------------------------------------------------------------------- -void FDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool planemirror) -{ - auto &vp = Viewpoint; - vp.SetViewAngle(r_viewwindow); - SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); - SetCameraPos(vp.Pos); - ApplyVPUniforms(); -} - //----------------------------------------------------------------------------- // // CreateScene @@ -141,7 +107,7 @@ void FDrawInfo::CreateScene() mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); // reset the portal manager - GLPortal::StartFrame(); + GLRenderer->mPortalState.StartFrame(); PO_LinkToSubsectors(); ProcessAll.Clock(); @@ -195,7 +161,7 @@ void FDrawInfo::RenderScene(int recursion) RenderAll.Clock(); glDepthMask(true); - if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion, this); + if (!gl_no_skyclear) GLRenderer->mPortalState.RenderFirstSkyPortal(recursion, this); gl_RenderState.EnableFog(true); gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); @@ -377,7 +343,7 @@ void FDrawInfo::DrawScene(int drawmode) // Handle all portals after rendering the opaque objects but before // doing all translucent stuff recursion++; - GLPortal::EndFrame(this); + GLRenderer->mPortalState.EndFrame(this); recursion--; RenderTranslucent(); } @@ -444,7 +410,7 @@ void FDrawInfo::DrawEndScene2D(sector_t * viewsector) void FDrawInfo::ProcessScene(bool toscreen) { iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; - GLPortal::BeginScene(); + GLRenderer->mPortalState.BeginScene(); int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; CurrentMapSections.Set(mapsection); diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 9f3ef2bd7..9feaad5b0 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -210,7 +210,7 @@ static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool // // //----------------------------------------------------------------------------- -void GLSkyPortal::DrawContents(FDrawInfo *di) +void GLSkyPortal::DrawContents(HWDrawInfo *di) { bool drawBoth = false; auto &vp = di->Viewpoint; @@ -230,7 +230,7 @@ void GLSkyPortal::DrawContents(FDrawInfo *di) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bool oldClamp = gl_RenderState.SetDepthClamp(true); - di->SetupView(0, 0, 0, !!(MirrorFlag & 1), !!(PlaneMirrorFlag & 1)); + di->SetupView(0, 0, 0, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); if (origin->texture[0] && origin->texture[0]->tex->bSkybox) diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 8c989313b..5a2a84155 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -337,7 +337,8 @@ void FDrawInfo::AddMirrorSurface(GLWall *w) void FDrawInfo::AddPortal(GLWall *wall, int ptype) { - GLPortal * portal; + auto &pstate = GLRenderer->mPortalState; + IPortal * portal; wall->MakeVertices(this, false); switch (ptype) @@ -345,19 +346,19 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) // portals don't go into the draw list. // Instead they are added to the portal manager case PORTALTYPE_HORIZON: - wall->horizon = UniqueHorizons.Get(wall->horizon); - portal = GLPortal::FindPortal(wall->horizon); - if (!portal) portal = new GLHorizonPortal(wall->horizon, Viewpoint); + wall->horizon = pstate.UniqueHorizons.Get(wall->horizon); + portal = pstate.FindPortal(wall->horizon); + if (!portal) portal = new GLHorizonPortal(&pstate, wall->horizon, Viewpoint); portal->AddLine(wall); break; case PORTALTYPE_SKYBOX: - portal = GLPortal::FindPortal(wall->secportal); + portal = pstate.FindPortal(wall->secportal); if (!portal) { // either a regular skybox or an Eternity-style horizon - if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(wall->secportal); - else portal = new GLSkyboxPortal(wall->secportal); + if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(&pstate, wall->secportal); + else portal = new GLSkyboxPortal(&pstate, wall->secportal); } portal->AddLine(wall); break; @@ -368,19 +369,19 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) break; case PORTALTYPE_PLANEMIRROR: - if (GLPortal::PlaneMirrorMode * wall->planemirror->fC() <= 0) + if (pstate.PlaneMirrorMode * wall->planemirror->fC() <= 0) { //@sync-portal - wall->planemirror = UniquePlaneMirrors.Get(wall->planemirror); - portal = GLPortal::FindPortal(wall->planemirror); - if (!portal) portal = new GLPlaneMirrorPortal(wall->planemirror); + wall->planemirror = pstate.UniquePlaneMirrors.Get(wall->planemirror); + portal = pstate.FindPortal(wall->planemirror); + if (!portal) portal = new GLPlaneMirrorPortal(&pstate, wall->planemirror); portal->AddLine(wall); } break; case PORTALTYPE_MIRROR: - portal = GLPortal::FindPortal(wall->seg->linedef); - if (!portal) portal = new GLMirrorPortal(wall->seg->linedef); + portal = pstate.FindPortal(wall->seg->linedef); + if (!portal) portal = new GLMirrorPortal(&pstate, wall->seg->linedef); portal->AddLine(wall); if (gl_mirror_envmap) { @@ -390,7 +391,7 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) break; case PORTALTYPE_LINETOLINE: - portal = GLPortal::FindPortal(wall->lineportal); + portal = pstate.FindPortal(wall->lineportal); if (!portal) { line_t *otherside = wall->lineportal->lines[0]->mDestination; @@ -398,15 +399,15 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { ProcessActorsInPortal(otherside->getPortal()->mGroup, in_area); } - portal = new GLLineToLinePortal(wall->lineportal); + portal = new GLLineToLinePortal(&pstate, wall->lineportal); } portal->AddLine(wall); break; case PORTALTYPE_SKY: - wall->sky = UniqueSkies.Get(wall->sky); - portal = GLPortal::FindPortal(wall->sky); - if (!portal) portal = new GLSkyPortal(wall->sky); + wall->sky = pstate.UniqueSkies.Get(wall->sky); + portal = pstate.FindPortal(wall->sky); + if (!portal) portal = new GLSkyPortal(&pstate, wall->sky); portal->AddLine(wall); break; } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 3b73d72c6..994bfc7c2 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -214,6 +214,42 @@ angle_t HWDrawInfo::FrustumAngle() return a1; } +//----------------------------------------------------------------------------- +// +// Setup the modelview matrix +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + float mult = mirror ? -1 : 1; + float planemult = planemirror ? -level.info->pixelstretch : level.info->pixelstretch; + + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); + VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy); + VPUniforms.mViewMatrix.scale(-mult, planemult, 1); +} + + +//----------------------------------------------------------------------------- +// +// SetupView +// Setup the view rotation matrix for the given viewpoint +// +//----------------------------------------------------------------------------- +void HWDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool planemirror) +{ + auto &vp = Viewpoint; + vp.SetViewAngle(r_viewwindow); + SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); + SetCameraPos(vp.Pos); + ApplyVPUniforms(); +} + + //----------------------------------------------------------------------------- // // diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 2c55145c9..f52ae4580 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -217,6 +217,8 @@ public: void PrepareTargeterSprites(); void UpdateCurrentMapSection(); + void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); + void SetupView(float vx, float vy, float vz, bool mirror, bool planemirror); angle_t FrustumAngle(); virtual void DrawWall(GLWall *wall, int pass) = 0; diff --git a/src/hwrenderer/scene/hw_portal.cpp b/src/hwrenderer/scene/hw_portal.cpp new file mode 100644 index 000000000..9c6ca5caa --- /dev/null +++ b/src/hwrenderer/scene/hw_portal.cpp @@ -0,0 +1,157 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2004-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** hw_portal.cpp +** portal maintenance classes for skyboxes, horizons etc. (API independent parts) +** +*/ + +#include "c_dispatch.h" +#include "hw_portal.h" + +IPortal * FPortalSceneState::FindPortal(const void * src) +{ + int i = portals.Size() - 1; + + while (i >= 0 && portals[i] && portals[i]->GetSource() != src) i--; + return i >= 0 ? portals[i] : nullptr; +} + +//----------------------------------------------------------------------------- +// +// StartFrame +// +//----------------------------------------------------------------------------- + +void FPortalSceneState::StartFrame() +{ + IPortal * p = nullptr; + portals.Push(p); + if (renderdepth == 0) + { + inskybox = false; + screen->instack[sector_t::floor] = screen->instack[sector_t::ceiling] = 0; + } + renderdepth++; +} + +//----------------------------------------------------------------------------- +// +// printing portal info +// +//----------------------------------------------------------------------------- + +static bool gl_portalinfo; + +CCMD(gl_portalinfo) +{ + gl_portalinfo = true; +} + +static FString indent; + +//----------------------------------------------------------------------------- +// +// EndFrame +// +//----------------------------------------------------------------------------- + +void FPortalSceneState::EndFrame(HWDrawInfo *outer_di) +{ + IPortal * p; + + if (gl_portalinfo) + { + Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), portals.Size(), renderdepth, indent.GetChars()); + indent += " "; + } + + // Only use occlusion query if there are more than 2 portals. + // Otherwise there's too much overhead. + // (And don't forget to consider the separating null pointers!) + bool usequery = portals.Size() > 2 + (unsigned)renderdepth; + + while (portals.Pop(p) && p) + { + if (gl_portalinfo) + { + Printf("%sProcessing %s, depth = %d, query = %d\n", indent.GetChars(), p->GetName(), renderdepth, usequery); + } + if (p->lines.Size() > 0) + { + p->RenderPortal(true, usequery, outer_di); + } + delete p; + } + renderdepth--; + + if (gl_portalinfo) + { + indent.Truncate(long(indent.Len()-2)); + Printf("%s}\n", indent.GetChars()); + if (portals.Size() == 0) gl_portalinfo = false; + } +} + + +//----------------------------------------------------------------------------- +// +// Renders one sky portal without a stencil. +// In more complex scenes using a stencil for skies can severely stall +// the GPU and there's rarely more than one sky visible at a time. +// +//----------------------------------------------------------------------------- +bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di) +{ + IPortal * p; + IPortal * best = nullptr; + unsigned bestindex=0; + + // Find the one with the highest amount of lines. + // Normally this is also the one that saves the largest amount + // of time by drawing it before the scene itself. + for(int i = portals.Size()-1; i >= 0 && portals[i] != nullptr; --i) + { + p=portals[i]; + if (p->lines.Size() > 0 && p->IsSky()) + { + // Cannot clear the depth buffer inside a portal recursion + if (recursion && p->NeedDepthBuffer()) continue; + + if (!best || p->lines.Size()>best->lines.Size()) + { + best=p; + bestindex=i; + } + } + } + + if (best) + { + portals.Delete(bestindex); + best->RenderPortal(false, false, outer_di); + delete best; + return true; + } + return false; +} + diff --git a/src/hwrenderer/scene/hw_portal.h b/src/hwrenderer/scene/hw_portal.h index b4a313c94..da23db552 100644 --- a/src/hwrenderer/scene/hw_portal.h +++ b/src/hwrenderer/scene/hw_portal.h @@ -1,6 +1,7 @@ #pragma once #include "hw_drawinfo.h" +#include "hw_drawstructs.h" #include "hwrenderer/textures/hw_material.h" struct GLSkyInfo @@ -33,12 +34,96 @@ struct GLHorizonInfo PalEntry specialcolor; }; +struct FPortalSceneState; + class IPortal { + friend struct FPortalSceneState; +protected: + FPortalSceneState * mState; + TArray lines; public: + IPortal(FPortalSceneState *s, bool local); virtual ~IPortal() {} virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } virtual line_t *ClipLine() { return nullptr; } + virtual void * GetSource() const = 0; // GetSource MUST be implemented! + virtual const char *GetName() = 0; + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di) = 0; + virtual void RenderAttached(HWDrawInfo *di) {} + virtual bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) = 0; + virtual void End(HWDrawInfo *di, bool usestencil) = 0; + + void AddLine(GLWall * l) + { + lines.Push(*l); + } + + void RenderPortal(bool usestencil, bool doquery, HWDrawInfo *outer_di) + { + // Start may perform an occlusion query. If that returns 0 there + // is no need to draw the stencil's contents and there's also no + // need to restore the affected area becasue there is none! + HWDrawInfo *di; + if (Start(usestencil, doquery, outer_di, &di)) + { + DrawContents(di); + End(di, usestencil); + } + } + + }; + +struct FPortalSceneState +{ + TArray portals; + int recursion = 0; + + int MirrorFlag = 0; + int PlaneMirrorFlag = 0; + int renderdepth = 0; + + int PlaneMirrorMode = 0; + bool inskybox = 0; + + UniqueList UniqueSkies; + UniqueList UniqueHorizons; + UniqueList UniquePlaneMirrors; + + int skyboxrecursion = 0; + + void BeginScene() + { + UniqueSkies.Clear(); + UniqueHorizons.Clear(); + UniquePlaneMirrors.Clear(); + } + + int GetRecursion() const + { + return recursion; + } + + bool isMirrored() const + { + return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); + } + + IPortal * FindPortal(const void * src); + void StartFrame(); + bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di); + void EndFrame(HWDrawInfo *outer_di); + + +}; + +inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s) +{ + if (!local) s->portals.Push(this); +} From 1cf3af9c418d9dad616b69347a68c2c2ba401358 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Jun 2018 13:51:19 +0200 Subject: [PATCH 47/58] - moved the current portal pointer to HWDrawInfo so that the portal managed does not need to save and restore a global variable for each portal. --- src/gl/renderer/gl_renderer.cpp | 1 - src/gl/renderer/gl_renderer.h | 2 -- src/gl/scene/gl_portal.cpp | 6 +----- src/gl/scene/gl_portal.h | 1 - src/gl/scene/gl_scene.cpp | 3 +-- src/hwrenderer/scene/hw_drawinfo.cpp | 1 + src/hwrenderer/scene/hw_drawinfo.h | 1 + 7 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index a605025d2..b70c7ed61 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -86,7 +86,6 @@ extern bool NoInterpolateView; FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) { framebuffer = fb; - mCurrentPortal = nullptr; mMirrorCount = 0; mPlaneMirrorCount = 0; mVBO = nullptr; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 2aebd0169..44ccfe1a5 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -66,8 +66,6 @@ class FGLRenderer public: OpenGLFrameBuffer *framebuffer; - //GLPortal *mClipPortal; - GLPortal *mCurrentPortal; int mMirrorCount; int mPlaneMirrorCount; FShaderManager *mShaderManager; diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index f8d560f27..201d7bf2f 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -213,14 +213,11 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw } } *pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms); + (*pDi)->mCurrentPortal = this; // save viewpoint savedvisibility = outer_di->Viewpoint.camera ? outer_di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0); - - PrevPortal = GLRenderer->mCurrentPortal; - GLRenderer->mCurrentPortal = this; - return true; } @@ -263,7 +260,6 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil) bool needdepth = NeedDepthBuffer(); Clocker c(PortalAll); - GLRenderer->mCurrentPortal = PrevPortal; di = static_cast(di)->EndDrawInfo(); di->ApplyVPUniforms(); diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index d94461765..cda52886e 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -53,7 +53,6 @@ private: void DrawPortalStencil(); ActorRenderFlags savedvisibility; - GLPortal *PrevPortal; TArray mPrimIndices; protected: diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 123fd47be..5d0bd7253 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -130,7 +130,7 @@ void FDrawInfo::CreateScene() PreparePlayerSprites(vp.sector, in_area); // Process all the sprites on the current portal's back side which touch the portal. - if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached(this); + if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this); Bsp.Unclock(); // And now the crappy hacks that have to be done to avoid rendering anomalies. @@ -414,7 +414,6 @@ void FDrawInfo::ProcessScene(bool toscreen) int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; CurrentMapSections.Set(mapsection); - GLRenderer->mCurrentPortal = nullptr; DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 994bfc7c2..69fa2ab98 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -95,6 +95,7 @@ void HWDrawInfo::ClearBuffers() memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0])); mClipPortal = nullptr; + mCurrentPortal = nullptr; } //========================================================================== diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index f52ae4580..cf34a4778 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -99,6 +99,7 @@ struct HWDrawInfo int FullbrightFlags; std::atomic spriteindex; IPortal *mClipPortal; + IPortal *mCurrentPortal; //FRotator mAngles; IShadowMap *mShadowMap; Clipper *mClipper; From 663ac919bd2e97150aa5d561aa17d2b5408e2496 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 23 Jun 2018 16:21:35 +0300 Subject: [PATCH 48/58] - removed unused header file --- src/posix/i_input.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/posix/i_input.h diff --git a/src/posix/i_input.h b/src/posix/i_input.h deleted file mode 100644 index e69de29bb..000000000 From 3936e3018dd2b0948a1be69ac748abb798f9f208 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 23 Jun 2018 16:22:29 +0300 Subject: [PATCH 49/58] - added window position and size restoration to SDL backend --- src/posix/sdl/i_input.cpp | 18 ++---------- src/posix/sdl/sdlglvideo.cpp | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/posix/sdl/i_input.cpp b/src/posix/sdl/i_input.cpp index 88c43050c..2c8160e68 100644 --- a/src/posix/sdl/i_input.cpp +++ b/src/posix/sdl/i_input.cpp @@ -43,7 +43,6 @@ #include "c_console.h" #include "c_dispatch.h" #include "dikeys.h" -#include "s_sound.h" #include "events.h" static void I_CheckGUICapture (); @@ -57,7 +56,6 @@ extern int paused; CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) EXTERN_CVAR (Bool, fullscreen) @@ -305,20 +303,8 @@ void MessagePump (const SDL_Event &sev) exit (0); case SDL_WINDOWEVENT: - switch (sev.window.event) - { - extern bool AppActive; - - case SDL_WINDOWEVENT_FOCUS_GAINED: - S_SetSoundPaused(1); - AppActive = true; - break; - - case SDL_WINDOWEVENT_FOCUS_LOST: - S_SetSoundPaused(i_soundinbackground); - AppActive = false; - break; - } + extern void ProcessSDLWindowEvent(const SDL_WindowEvent &); + ProcessSDLWindowEvent(sev.window); break; case SDL_MOUSEBUTTONDOWN: diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index 4e7e0fbec..d6f495c6d 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -41,6 +41,7 @@ #include "v_video.h" #include "version.h" #include "c_console.h" +#include "s_sound.h" #include "videomodes.h" #include "hardware.h" @@ -84,6 +85,13 @@ CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCA Printf("This won't take effect until " GAMENAME " is restarted.\n"); } +CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_w, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, win_h, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CVAR(Bool, i_soundinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -226,6 +234,20 @@ SystemFrameBuffer::SystemFrameBuffer (void *, bool fullscreen) m_supportsGamma = -1 != SDL_GetWindowGammaRamp(Screen, m_origGamma[0], m_origGamma[1], m_origGamma[2] ); + + if (!fullscreen) + { + if (win_x >= 0 && win_y >= 0) + { + SDL_SetWindowPosition(Screen, win_x, win_y); + } + + if (win_h > 320 && win_w > 200) + { + SDL_SetWindowSize(Screen, win_w, win_h); + } + } + return; } @@ -328,6 +350,41 @@ int SystemFrameBuffer::GetClientHeight() } +void ProcessSDLWindowEvent(const SDL_WindowEvent &event) +{ + switch (event.event) + { + extern bool AppActive; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + S_SetSoundPaused(1); + AppActive = true; + break; + + case SDL_WINDOWEVENT_FOCUS_LOST: + S_SetSoundPaused(i_soundinbackground); + AppActive = false; + break; + + case SDL_WINDOWEVENT_MOVED: + if (!fullscreen) + { + win_x = event.data1; + win_y = event.data2; + } + break; + + case SDL_WINDOWEVENT_RESIZED: + if (!fullscreen) + { + win_w = event.data1; + win_h = event.data2; + } + break; + } +} + + // each platform has its own specific version of this function. void I_SetWindowTitle(const char* caption) { From 6ebec37baf316369706aee0ac456aabbb702feb4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Jun 2018 20:57:02 +0200 Subject: [PATCH 50/58] - moved all portal code that sets up a scene to be rendered into API independent code and let it be handled by a common wrapper class. --- src/gl/scene/gl_drawinfo.cpp | 38 +-- src/gl/scene/gl_drawinfo.h | 16 +- src/gl/scene/gl_portal.cpp | 481 --------------------------- src/gl/scene/gl_portal.h | 166 +-------- src/gl/scene/gl_scene.cpp | 1 - src/gl/scene/gl_walls_draw.cpp | 14 +- src/hwrenderer/scene/hw_bsp.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.h | 10 +- src/hwrenderer/scene/hw_portal.cpp | 480 ++++++++++++++++++++++++++ src/hwrenderer/scene/hw_portal.h | 197 ++++++++++- src/portal.h | 5 - src/r_data/portalgroups.cpp | 1 - 13 files changed, 728 insertions(+), 689 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index de3cd91ee..1aa3eac57 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -175,23 +175,6 @@ void FDrawInfoList::Release(FDrawInfo * di) mList.Push(di); } -//========================================================================== -// -// -// -//========================================================================== - -FDrawInfo::FDrawInfo() -{ - next = NULL; -} - -FDrawInfo::~FDrawInfo() -{ - ClearBuffers(); -} - - //========================================================================== // // Sets up a new drawinfo struct @@ -225,7 +208,7 @@ void FDrawInfo::StartScene() { ClearBuffers(); - next = gl_drawinfo; + outer = gl_drawinfo; gl_drawinfo = this; for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); decals[0].Clear(); @@ -233,7 +216,7 @@ void FDrawInfo::StartScene() hudsprites.Clear(); // Fullbright information needs to be propagated from the main view. - if (next != nullptr) FullbrightFlags = next->FullbrightFlags; + if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags; else FullbrightFlags = 0; } @@ -247,7 +230,7 @@ FDrawInfo *FDrawInfo::EndDrawInfo() { assert(this == gl_drawinfo); for(int i=0;i(outer); di_list.Release(this); if (gl_drawinfo == nullptr) ResetRenderDataAllocator(); @@ -499,9 +482,15 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) } // Same here for the dependency on the portal. -void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) +void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) { - portal->GetRenderState()->AddSubsector(sub); + auto portal = GLRenderer->mPortalState.FindPortal(ptg); + if (!portal) + { + portal = new GLScenePortal(&GLRenderer->mPortalState, new HWSectorStackPortal(ptg)); + } + auto ptl = static_cast(static_cast(portal)->mScene); + ptl->AddSubsector(sub); } std::pair FDrawInfo::AllocVertices(unsigned int count) @@ -523,5 +512,10 @@ int FDrawInfo::UploadLights(FDynLightData &data) return GLRenderer->mLights->UploadLights(data); } +bool FDrawInfo::SetDepthClamp(bool on) +{ + return gl_RenderState.SetDepthClamp(on); +} + diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index ec0a6ed85..e41e718a2 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -35,14 +35,10 @@ enum Drawpasses struct FDrawInfo : public HWDrawInfo { - FDrawInfo * next; HWDrawList drawlists[GLDL_TYPES]; TArray hudsprites; // These may just be stored by value. TArray decals[2]; // the second slot is for mirrors which get rendered in a separate pass. - FDrawInfo(); - ~FDrawInfo(); - void ApplyVPUniforms() override; void AddWall(GLWall *wall) override; @@ -56,15 +52,6 @@ struct FDrawInfo : public HWDrawInfo std::pair AllocVertices(unsigned int count) override; int UploadLights(FDynLightData &data) override; - // Legacy GL only. - bool PutWallCompat(GLWall *wall, int passflag); - bool PutFlatCompat(GLFlat *flat, bool fog); - void RenderFogBoundaryCompat(GLWall *wall); - void RenderLightsCompat(GLWall *wall, int pass); - void DrawSubsectorLights(GLFlat *flat, subsector_t * sub, int pass); - void DrawLightsCompat(GLFlat *flat, int pass); - - void DrawDecal(GLDecal *gldecal); void DrawDecals(); void DrawDecalsForMirror(GLWall *wall); @@ -111,7 +98,8 @@ struct FDrawInfo : public HWDrawInfo void ProcessScene(bool toscreen = false); void EndDrawScene(sector_t * viewsector); void DrawEndScene2D(sector_t * viewsector); - + bool SetDepthClamp(bool on) override; + static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); FDrawInfo *EndDrawInfo(); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 201d7bf2f..a4bb2b16b 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -222,34 +222,6 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw } -inline void GLPortal::ClearClipper(HWDrawInfo *di) -{ - auto outer_di = static_cast(di)->next; - DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); - - di->mClipper->Clear(); - - // Set the clipper to the minimal visible area - di->mClipper->SafeAddClipRange(0,0xffffffff); - for (unsigned int i = 0; i < lines.Size(); i++) - { - DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; - DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset; - - if (deltaangle(endAngle, startAngle) < 0) - { - di->mClipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); - } - } - - // and finally clip it to the visible area - angle_t a1 = di->FrustumAngle(); - if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); - - // lock the parts that have just been clipped out. - di->mClipper->SetSilhouette(); -} - //----------------------------------------------------------------------------- // // End @@ -346,454 +318,12 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil) } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Skybox Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// GLSkyboxPortal::DrawContents -// -//----------------------------------------------------------------------------- - -void GLSkyboxPortal::DrawContents(HWDrawInfo *di) -{ - int old_pm = mState->PlaneMirrorMode; - - if (mState->skyboxrecursion >= 3) - { - ClearScreen(di); - return; - } - auto &vp = di->Viewpoint; - - mState->skyboxrecursion++; - AActor *origin = portal->mSkybox; - portal->mFlags |= PORTSF_INSKYBOX; - vp.extralight = 0; - - mState->PlaneMirrorMode = 0; - - bool oldclamp = gl_RenderState.SetDepthClamp(false); - vp.Pos = origin->InterpolatedPosition(vp.TicFrac); - vp.ActorPos = origin->Pos(); - vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); - - // Don't let the viewpoint be too close to a floor or ceiling - double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); - double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); - if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; - if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; - - vp.ViewActor = origin; - - mState->inskybox = true; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); - di->SetViewArea(); - ClearClipper(di); - - di->UpdateCurrentMapSection(); - - static_cast(di)->DrawScene(DM_PORTAL); - portal->mFlags &= ~PORTSF_INSKYBOX; - mState->inskybox = false; - gl_RenderState.SetDepthClamp(oldclamp); - mState->skyboxrecursion--; - - mState->PlaneMirrorMode = old_pm; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Sector stack Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -//========================================================================== -// -// Fixme: This needs abstraction. -// -//========================================================================== - -GLSectorStackPortal *FSectorPortalGroup::GetRenderState() -{ - if (glportal == nullptr) glportal = new GLSectorStackPortal(&GLRenderer->mPortalState, this); - return glportal; -} - - - -GLSectorStackPortal::~GLSectorStackPortal() -{ - if (origin != nullptr && origin->glportal == this) - { - origin->glportal = nullptr; - } -} - -//----------------------------------------------------------------------------- -// -// GLSectorStackPortal::SetupCoverage -// -//----------------------------------------------------------------------------- - -static uint8_t SetCoverage(HWDrawInfo *di, void *node) -{ - if (level.nodes.Size() == 0) - { - return 0; - } - if (!((size_t)node & 1)) // Keep going until found a subsector - { - node_t *bsp = (node_t *)node; - uint8_t coverage = SetCoverage(di, bsp->children[0]) | SetCoverage(di, bsp->children[1]); - di->no_renderflags[bsp->Index()] = coverage; - return coverage; - } - else - { - subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); - return di->ss_renderflags[sub->Index()] & SSRF_SEEN; - } -} - -void GLSectorStackPortal::SetupCoverage(HWDrawInfo *di) -{ - for(unsigned i=0; iplane; - for(int j=0;jportalcoverage[plane].sscount; j++) - { - subsector_t *dsub = &::level.subsectors[sub->portalcoverage[plane].subsectors[j]]; - di->CurrentMapSections.Set(dsub->mapsection); - di->ss_renderflags[dsub->Index()] |= SSRF_SEEN; - } - } - SetCoverage(di, ::level.HeadNode()); -} - -//----------------------------------------------------------------------------- -// -// GLSectorStackPortal::DrawContents -// -//----------------------------------------------------------------------------- -void GLSectorStackPortal::DrawContents(HWDrawInfo *di) -{ - FSectorPortalGroup *portal = origin; - auto &vp = di->Viewpoint; - - vp.Pos += origin->mDisplacement; - vp.ActorPos += origin->mDisplacement; - vp.ViewActor = nullptr; - - // avoid recursions! - if (origin->plane != -1) screen->instack[origin->plane]++; - - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - SetupCoverage(di); - ClearClipper(di); - - // If the viewpoint is not within the portal, we need to invalidate the entire clip area. - // The portal will re-validate the necessary parts when its subsectors get traversed. - subsector_t *sub = R_PointInSubsector(vp.Pos); - if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) - { - di->mClipper->SafeAddClipRange(0, ANGLE_MAX); - di->mClipper->SetBlocked(true); - } - - static_cast(di)->DrawScene(DM_PORTAL); - - if (origin->plane != -1) screen->instack[origin->plane]--; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Plane Mirror Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// GLPlaneMirrorPortal::DrawContents -// -//----------------------------------------------------------------------------- - -void GLPlaneMirrorPortal::DrawContents(HWDrawInfo *di) -{ - if (mState->renderdepth > r_mirror_recursions) - { - ClearScreen(di); - return; - } - // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. - std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); - - auto &vp = di->Viewpoint; - int old_pm = mState->PlaneMirrorMode; - - // the player is always visible in a mirror. - vp.showviewer = true; - - double planez = origin->ZatPoint(vp.Pos); - vp.Pos.Z = 2 * planez - vp.Pos.Z; - vp.ViewActor = nullptr; - mState->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; - - mState->PlaneMirrorFlag++; - di->SetClipHeight(planez, mState->PlaneMirrorMode < 0 ? -1.f : 1.f); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); - ClearClipper(di); - - di->UpdateCurrentMapSection(); - - static_cast(di)->DrawScene(DM_PORTAL); - mState->PlaneMirrorFlag--; - mState->PlaneMirrorMode = old_pm; - std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); -} - -//----------------------------------------------------------------------------- -// -// Common code for line to line and mirror portals -// -//----------------------------------------------------------------------------- - -int GLLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) -{ - line_t *linedef = seg->linedef; - if (!linedef) - { - return PClip_Inside; // should be handled properly. - } - return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside; -} - -int GLLinePortal::ClipSubsector(subsector_t *sub) -{ - // this seg is completely behind the mirror - for(unsigned int i=0;inumlines;i++) - { - if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; - } - return PClip_InFront; -} - -int GLLinePortal::ClipPoint(const DVector2 &pos) -{ - if (P_PointOnLineSidePrecise(pos, line())) - { - return PClip_InFront; - } - return PClip_Inside; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Mirror Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// R_EnterMirror -// -//----------------------------------------------------------------------------- -void GLMirrorPortal::DrawContents(HWDrawInfo *di) -{ - if (mState->renderdepth>r_mirror_recursions) - { - ClearScreen(di); - return; - } - - auto &vp = di->Viewpoint; - di->UpdateCurrentMapSection(); - - di->mClipPortal = this; - DAngle StartAngle = vp.Angles.Yaw; - DVector3 StartPos = vp.Pos; - - vertex_t *v1 = linedef->v1; - vertex_t *v2 = linedef->v2; - - // the player is always visible in a mirror. - vp.showviewer = true; - // Reflect the current view behind the mirror. - if (linedef->Delta().X == 0) - { - // vertical mirror - vp.Pos.X = 2 * v1->fX() - StartPos.X; - - // Compensation for reendering inaccuracies - if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1; - else vp.Pos.X += 0.1; - } - else if (linedef->Delta().Y == 0) - { - // horizontal mirror - vp.Pos.Y = 2*v1->fY() - StartPos.Y; - - // Compensation for reendering inaccuracies - if (StartPos.YfY()) vp.Pos.Y -= 0.1; - else vp.Pos.Y += 0.1; - } - else - { - // any mirror--use floats to avoid integer overflow. - // Use doubles to avoid losing precision which is very important here. - - double dx = v2->fX() - v1->fX(); - double dy = v2->fY() - v1->fY(); - double x1 = v1->fX(); - double y1 = v1->fY(); - double x = StartPos.X; - double y = StartPos.Y; - - // the above two cases catch len == 0 - double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - - vp.Pos.X = (x1 + r * dx)*2 - x; - vp.Pos.Y = (y1 + r * dy)*2 - y; - - // Compensation for reendering inaccuracies - FVector2 v(-dx, dy); - v.MakeUnit(); - - vp.Pos.X+= v[1] * mState->renderdepth / 2; - vp.Pos.Y+= v[0] * mState->renderdepth / 2; - } - vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; - - vp.ViewActor = nullptr; - - mState->MirrorFlag++; - di->SetClipLine(linedef); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - - di->mClipper->Clear(); - - angle_t af = di->FrustumAngle(); - if (afmClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs()+af, vp.Angles.Yaw.BAMs()-af); - - di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); - - static_cast(di)->DrawScene(DM_PORTAL); - - mState->MirrorFlag--; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Line to line Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// -// -//----------------------------------------------------------------------------- -void GLLineToLinePortal::DrawContents(HWDrawInfo *di) -{ - // TODO: Handle recursion more intelligently - if (mState->renderdepth>r_mirror_recursions) - { - ClearScreen(di); - return; - } - auto &vp = di->Viewpoint; - di->mClipPortal = this; - - line_t *origin = glport->lines[0]->mOrigin; - P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); - P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); - P_TranslatePortalAngle(origin, vp.Angles.Yaw); - P_TranslatePortalZ(origin, vp.Pos.Z); - P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); - P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); - if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) - { - double distp = (vp.Path[0] - vp.Path[1]).Length(); - if (distp > EQUAL_EPSILON) - { - double dist1 = (vp.Pos - vp.Path[0]).Length(); - double dist2 = (vp.Pos - vp.Path[1]).Length(); - - if (dist1 + dist2 < distp + 1) - { - vp.camera->renderflags |= RF_MAYBEINVISIBLE; - } - } - } - - - for (unsigned i = 0; i < lines.Size(); i++) - { - line_t *line = lines[i].seg->linedef->getPortalDestination(); - subsector_t *sub; - if (line->sidedef[0]->Flags & WALLF_POLYOBJ) - sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY()); - else sub = line->frontsector->subsectors[0]; - di->CurrentMapSections.Set(sub->mapsection); - } - - vp.ViewActor = nullptr; - di->SetClipLine(glport->lines[0]->mDestination); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - - ClearClipper(di); - static_cast(di)->DrawScene(DM_PORTAL); -} - -void GLLineToLinePortal::RenderAttached(HWDrawInfo *di) -{ - di->ProcessActorsInPortal(glport, di->in_area); -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // // // Horizon Portal // -// This simply draws the area in medium sized squares. Drawing it as a whole -// polygon creates visible inaccuracies. -// -// Originally I tried to minimize the amount of data to be drawn but there -// are 2 problems with it: -// -// 1. Setting this up completely negates any performance gains. -// 2. It doesn't work with a 360° field of view (as when you are looking up.) -// -// -// So the brute force mechanism is just as good. -// // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -978,17 +508,6 @@ void GLEEHorizonPortal::DrawContents(HWDrawInfo *di) } const char *GLSkyPortal::GetName() { return "Sky"; } -const char *GLSkyboxPortal::GetName() { return "Skybox"; } -const char *GLSectorStackPortal::GetName() { return "Sectorstack"; } -const char *GLPlaneMirrorPortal::GetName() { return "Planemirror"; } -const char *GLMirrorPortal::GetName() { return "Mirror"; } -const char *GLLineToLinePortal::GetName() { return "LineToLine"; } const char *GLHorizonPortal::GetName() { return "Horizon"; } const char *GLEEHorizonPortal::GetName() { return "EEHorizon"; } -// This needs to remain on the renderer side until the portal interface can be abstracted. -void FSectorPortalGroup::AddSubsector(subsector_t *sub) -{ - GLSectorStackPortal *glportal = GetRenderState(); - glportal->AddSubsector(sub); -} diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index cda52886e..890eb7909 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -66,115 +66,30 @@ protected: void ClearScreen(HWDrawInfo *di); }; -struct GLLinePortal : public GLPortal +class GLScenePortal : public GLPortal { - // this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal. - vertex_t *v1, *v2; // vertices, from v1 to v2 - DVector2 delta; // precalculated v2 - v1 for side checking - - angle_t angv1, angv2; // for quick comparisons with a line or subsector - - GLLinePortal(FPortalSceneState *state, line_t *line) : GLPortal(state) +public: + HWScenePortalBase *mScene; + GLScenePortal(FPortalSceneState *state, HWScenePortalBase *handler) : GLPortal(state) { - v1 = line->v1; - v2 = line->v2; - CalcDelta(); + mScene = handler; + handler->SetOwner(this); } - - GLLinePortal(FPortalSceneState *state, FLinePortalSpan *line) : GLPortal(state) - { - if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr) + ~GLScenePortal() { delete mScene; } + virtual void * GetSource() const { return mScene->GetSource(); } + virtual const char *GetName() { return mScene->GetName(); } + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di) + { + if (mScene->Setup(di, di->mClipper)) { - // For non-linked portals we must check the actual linedef. - line_t *lline = line->lines[0]->mDestination; - v1 = lline->v1; - v2 = lline->v2; + static_cast(di)->DrawScene(DM_PORTAL); + mScene->Shutdown(di); } - else - { - // For linked portals we can check the merged span. - v1 = line->v1; - v2 = line->v2; - } - CalcDelta(); } - - void CalcDelta() - { - delta = v2->fPos() - v1->fPos(); - } - - line_t *line() - { - vertex_t **pv = &v1; - return reinterpret_cast(pv); - } - - virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos); - virtual int ClipSubsector(subsector_t *sub); - virtual int ClipPoint(const DVector2 &pos); - virtual bool NeedCap() { return false; } -}; - - -struct GLMirrorPortal : public GLLinePortal -{ - // mirror portals always consist of single linedefs! - line_t * linedef; - -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return linedef; } - virtual const char *GetName(); - -public: - - GLMirrorPortal(FPortalSceneState *state, line_t * line) - : GLLinePortal(state, line) - { - linedef=line; - } -}; - - -struct GLLineToLinePortal : public GLLinePortal -{ - FLinePortalSpan *glport; -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return glport; } - virtual const char *GetName(); - virtual line_t *ClipLine() { return line(); } - virtual void RenderAttached(HWDrawInfo *di); - -public: - - GLLineToLinePortal(FPortalSceneState *state, FLinePortalSpan *ll) - : GLLinePortal(state, ll) - { - glport = ll; - } -}; - - -struct GLSkyboxPortal : public GLPortal -{ - FSectorPortal * portal; - -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return portal; } - virtual bool IsSky() { return true; } - virtual const char *GetName(); - -public: - - - GLSkyboxPortal(FPortalSceneState *state, FSectorPortal * pt) : GLPortal(state) - { - portal=pt; - } - + virtual void RenderAttached(HWDrawInfo *di) { return mScene->RenderAttached(di); } }; @@ -201,51 +116,6 @@ public: }; - - -struct GLSectorStackPortal : public GLPortal -{ - TArray subsectors; -protected: - virtual ~GLSectorStackPortal(); - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return origin; } - virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. - virtual const char *GetName(); - FSectorPortalGroup *origin; - -public: - - GLSectorStackPortal(FPortalSceneState *state, FSectorPortalGroup *pt) : GLPortal(state) - { - origin=pt; - } - void SetupCoverage(HWDrawInfo *di); - void AddSubsector(subsector_t *sub) - { - subsectors.Push(sub); - } - -}; - -struct GLPlaneMirrorPortal : public GLPortal -{ -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return origin; } - virtual const char *GetName(); - secplane_t * origin; - -public: - - GLPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : GLPortal(state) - { - origin=pt; - } - -}; - - struct GLHorizonPortal : public GLPortal { GLHorizonInfo * origin; diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 5d0bd7253..c5a500922 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -113,7 +113,6 @@ void FDrawInfo::CreateScene() ProcessAll.Clock(); // clip the scene and fill the drawlists - for(auto p : level.portalGroups) p->glportal = nullptr; Bsp.Clock(); GLRenderer->mVBO->Map(); GLRenderer->mLights->Begin(); diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 5a2a84155..cf48f1348 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -358,13 +358,17 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { // either a regular skybox or an Eternity-style horizon if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(&pstate, wall->secportal); - else portal = new GLSkyboxPortal(&pstate, wall->secportal); + else portal = new GLScenePortal(&pstate, new HWSkyboxPortal(wall->secportal)); } portal->AddLine(wall); break; case PORTALTYPE_SECTORSTACK: - portal = wall->portal->GetRenderState(); + portal = pstate.FindPortal(wall->portal); + if (!portal) + { + portal = new GLScenePortal(&pstate, new HWSectorStackPortal(wall->portal)); + } portal->AddLine(wall); break; @@ -374,14 +378,14 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) //@sync-portal wall->planemirror = pstate.UniquePlaneMirrors.Get(wall->planemirror); portal = pstate.FindPortal(wall->planemirror); - if (!portal) portal = new GLPlaneMirrorPortal(&pstate, wall->planemirror); + if (!portal) portal = new GLScenePortal(&pstate, new HWPlaneMirrorPortal(wall->planemirror)); portal->AddLine(wall); } break; case PORTALTYPE_MIRROR: portal = pstate.FindPortal(wall->seg->linedef); - if (!portal) portal = new GLMirrorPortal(&pstate, wall->seg->linedef); + if (!portal) portal = new GLScenePortal(&pstate, new HWMirrorPortal(wall->seg->linedef)); portal->AddLine(wall); if (gl_mirror_envmap) { @@ -399,7 +403,7 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { ProcessActorsInPortal(otherside->getPortal()->mGroup, in_area); } - portal = new GLLineToLinePortal(&pstate, wall->lineportal); + portal = new GLScenePortal(&pstate, new HWLineToLinePortal(wall->lineportal)); } portal->AddLine(wall); break; diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 18884dab0..edbe04dc3 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -538,13 +538,13 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) portal = fakesector->GetPortalGroup(sector_t::ceiling); if (portal != nullptr) { - portal->AddSubsector(sub); + AddSubsectorToPortal(portal, sub); } portal = fakesector->GetPortalGroup(sector_t::floor); if (portal != nullptr) { - portal->AddSubsector(sub); + AddSubsectorToPortal(portal, sub); } } } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 69fa2ab98..697715f61 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -223,7 +223,7 @@ angle_t HWDrawInfo::FrustumAngle() void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) { - float mult = mirror ? -1 : 1; + float mult = mirror ? -1.f : 1.f; float planemult = planemirror ? -level.info->pixelstretch : level.info->pixelstretch; VPUniforms.mViewMatrix.loadIdentity(); @@ -250,7 +250,6 @@ void HWDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool plane ApplyVPUniforms(); } - //----------------------------------------------------------------------------- // // @@ -268,3 +267,4 @@ void HWViewpointUniforms::SetDefaults() mClipLine.X = -10000000.0f; } + diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index cf34a4778..4c8a303c5 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -23,6 +23,7 @@ class Clipper; class IPortal; class FFlatVertexGenerator; class IRenderQueue; +class HWScenePortalBase; //========================================================================== // @@ -58,7 +59,10 @@ enum EPortalClip struct HWDrawInfo { - virtual ~HWDrawInfo() {} + virtual ~HWDrawInfo() + { + ClearBuffers(); + } struct wallseg { @@ -96,9 +100,10 @@ struct HWDrawInfo bool isNightvision() const { return !!(FullbrightFlags & Nightvision); } bool isStealthVision() const { return !!(FullbrightFlags & StealthVision); } + HWDrawInfo * outer = nullptr; int FullbrightFlags; std::atomic spriteindex; - IPortal *mClipPortal; + HWScenePortalBase *mClipPortal; IPortal *mCurrentPortal; //FRotator mAngles; IShadowMap *mShadowMap; @@ -240,6 +245,7 @@ public: virtual int UploadLights(FDynLightData &data) = 0; virtual void ApplyVPUniforms() = 0; + virtual bool SetDepthClamp(bool on) = 0; virtual GLDecal *AddDecal(bool onmirror) = 0; virtual std::pair AllocVertices(unsigned int count) = 0; diff --git a/src/hwrenderer/scene/hw_portal.cpp b/src/hwrenderer/scene/hw_portal.cpp index 9c6ca5caa..248a9c94c 100644 --- a/src/hwrenderer/scene/hw_portal.cpp +++ b/src/hwrenderer/scene/hw_portal.cpp @@ -26,7 +26,14 @@ */ #include "c_dispatch.h" +#include "portal.h" +#include "p_maputl.h" #include "hw_portal.h" +#include "hw_clipper.h" +#include "actor.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Int, r_mirror_recursions) IPortal * FPortalSceneState::FindPortal(const void * src) { @@ -155,3 +162,476 @@ bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di return false; } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWScenePortalBase::ClearClipper(HWDrawInfo *di) +{ + auto outer_di = di->outer; + DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); + + di->mClipper->Clear(); + + auto &lines = mOwner->lines; + // Set the clipper to the minimal visible area + di->mClipper->SafeAddClipRange(0, 0xffffffff); + for (unsigned int i = 0; i < lines.Size(); i++) + { + DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; + DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset; + + if (deltaangle(endAngle, startAngle) < 0) + { + di->mClipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); + } + } + + // and finally clip it to the visible area + angle_t a1 = di->FrustumAngle(); + if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); + + // lock the parts that have just been clipped out. + di->mClipper->SetSilhouette(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Common code for line to line and mirror portals +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +int HWLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) +{ + line_t *linedef = seg->linedef; + if (!linedef) + { + return PClip_Inside; // should be handled properly. + } + return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside; +} + +int HWLinePortal::ClipSubsector(subsector_t *sub) +{ + // this seg is completely behind the mirror + for (unsigned int i = 0; inumlines; i++) + { + if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; + } + return PClip_InFront; +} + +int HWLinePortal::ClipPoint(const DVector2 &pos) +{ + if (P_PointOnLineSidePrecise(pos, line())) + { + return PClip_InFront; + } + return PClip_Inside; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool HWMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + + auto &vp = di->Viewpoint; + di->UpdateCurrentMapSection(); + + di->mClipPortal = this; + DAngle StartAngle = vp.Angles.Yaw; + DVector3 StartPos = vp.Pos; + + vertex_t *v1 = linedef->v1; + vertex_t *v2 = linedef->v2; + + // the player is always visible in a mirror. + vp.showviewer = true; + // Reflect the current view behind the mirror. + if (linedef->Delta().X == 0) + { + // vertical mirror + vp.Pos.X = 2 * v1->fX() - StartPos.X; + + // Compensation for reendering inaccuracies + if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1; + else vp.Pos.X += 0.1; + } + else if (linedef->Delta().Y == 0) + { + // horizontal mirror + vp.Pos.Y = 2 * v1->fY() - StartPos.Y; + + // Compensation for reendering inaccuracies + if (StartPos.Y < v1->fY()) vp.Pos.Y -= 0.1; + else vp.Pos.Y += 0.1; + } + else + { + // any mirror--use floats to avoid integer overflow. + // Use doubles to avoid losing precision which is very important here. + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = StartPos.X; + double y = StartPos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy * dy); + + vp.Pos.X = (x1 + r * dx) * 2 - x; + vp.Pos.Y = (y1 + r * dy) * 2 - y; + + // Compensation for reendering inaccuracies + FVector2 v(-dx, dy); + v.MakeUnit(); + + vp.Pos.X += v[1] * state->renderdepth / 2; + vp.Pos.Y += v[0] * state->renderdepth / 2; + } + vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; + + vp.ViewActor = nullptr; + + state->MirrorFlag++; + di->SetClipLine(linedef); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + clipper->Clear(); + + angle_t af = di->FrustumAngle(); + if (af < ANGLE_180) clipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + af, vp.Angles.Yaw.BAMs() - af); + + clipper->SafeAddClipRange(linedef->v1, linedef->v2); + return true; +} + +void HWMirrorPortal::Shutdown(HWDrawInfo *di) +{ + mOwner->mState->MirrorFlag--; +} + +const char *HWMirrorPortal::GetName() { return "Mirror"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Line to line Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +bool HWLineToLinePortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + // TODO: Handle recursion more intelligently + auto &state = mOwner->mState; + if (state->renderdepth>r_mirror_recursions) + { + return false; + } + auto &vp = di->Viewpoint; + di->mClipPortal = this; + + line_t *origin = glport->lines[0]->mOrigin; + P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); + P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); + P_TranslatePortalAngle(origin, vp.Angles.Yaw); + P_TranslatePortalZ(origin, vp.Pos.Z); + P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); + P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); + if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) + { + double distp = (vp.Path[0] - vp.Path[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (vp.Pos - vp.Path[0]).Length(); + double dist2 = (vp.Pos - vp.Path[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + vp.camera->renderflags |= RF_MAYBEINVISIBLE; + } + } + } + + auto &lines = mOwner->lines; + + for (unsigned i = 0; i < lines.Size(); i++) + { + line_t *line = lines[i].seg->linedef->getPortalDestination(); + subsector_t *sub; + if (line->sidedef[0]->Flags & WALLF_POLYOBJ) + sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY()); + else sub = line->frontsector->subsectors[0]; + di->CurrentMapSections.Set(sub->mapsection); + } + + vp.ViewActor = nullptr; + di->SetClipLine(glport->lines[0]->mDestination); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + ClearClipper(di); + return true; +} + +void HWLineToLinePortal::RenderAttached(HWDrawInfo *di) +{ + di->ProcessActorsInPortal(glport, di->in_area); +} + +const char *HWLineToLinePortal::GetName() { return "LineToLine"; } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Skybox Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSkyboxPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWSkyboxPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + old_pm = state->PlaneMirrorMode; + + if (mOwner->mState->skyboxrecursion >= 3) + { + return false; + } + auto &vp = di->Viewpoint; + + state->skyboxrecursion++; + state->PlaneMirrorMode = 0; + state->inskybox = true; + + AActor *origin = portal->mSkybox; + portal->mFlags |= PORTSF_INSKYBOX; + vp.extralight = 0; + + + oldclamp = di->SetDepthClamp(false); + vp.Pos = origin->InterpolatedPosition(vp.TicFrac); + vp.ActorPos = origin->Pos(); + vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); + + // Don't let the viewpoint be too close to a floor or ceiling + double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); + double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); + if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; + if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; + + vp.ViewActor = origin; + + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + di->SetViewArea(); + ClearClipper(di); + di->UpdateCurrentMapSection(); + return true; +} + + +void HWSkyboxPortal::Shutdown(HWDrawInfo *di) +{ + auto state = mOwner->mState; + portal->mFlags &= ~PORTSF_INSKYBOX; + di->SetDepthClamp(oldclamp); + state->inskybox = false; + state->skyboxrecursion--; + state->PlaneMirrorMode = old_pm; +} + +const char *HWSkyboxPortal::GetName() { return "Skybox"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Sector stack Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::SetupCoverage +// +//----------------------------------------------------------------------------- + +static uint8_t SetCoverage(HWDrawInfo *di, void *node) +{ + if (level.nodes.Size() == 0) + { + return 0; + } + if (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + uint8_t coverage = SetCoverage(di, bsp->children[0]) | SetCoverage(di, bsp->children[1]); + di->no_renderflags[bsp->Index()] = coverage; + return coverage; + } + else + { + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); + return di->ss_renderflags[sub->Index()] & SSRF_SEEN; + } +} + +void HWSectorStackPortal::SetupCoverage(HWDrawInfo *di) +{ + for (unsigned i = 0; iplane; + for (int j = 0; jportalcoverage[plane].sscount; j++) + { + subsector_t *dsub = &::level.subsectors[sub->portalcoverage[plane].subsectors[j]]; + di->CurrentMapSections.Set(dsub->mapsection); + di->ss_renderflags[dsub->Index()] |= SSRF_SEEN; + } + } + SetCoverage(di, ::level.HeadNode()); +} + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::DrawContents +// +//----------------------------------------------------------------------------- +bool HWSectorStackPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + FSectorPortalGroup *portal = origin; + auto &vp = di->Viewpoint; + + vp.Pos += origin->mDisplacement; + vp.ActorPos += origin->mDisplacement; + vp.ViewActor = nullptr; + + // avoid recursions! + if (origin->plane != -1) screen->instack[origin->plane]++; + + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + SetupCoverage(di); + ClearClipper(di); + + // If the viewpoint is not within the portal, we need to invalidate the entire clip area. + // The portal will re-validate the necessary parts when its subsectors get traversed. + subsector_t *sub = R_PointInSubsector(vp.Pos); + if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) + { + di->mClipper->SafeAddClipRange(0, ANGLE_MAX); + di->mClipper->SetBlocked(true); + } + return true; +} + + +void HWSectorStackPortal::Shutdown(HWDrawInfo *di) +{ + if (origin->plane != -1) screen->instack[origin->plane]--; +} + +const char *HWSectorStackPortal::GetName() { return "Sectorstack"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Plane Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLPlaneMirrorPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. + std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); + + auto &vp = di->Viewpoint; + int old_pm = state->PlaneMirrorMode; + + // the player is always visible in a mirror. + vp.showviewer = true; + + double planez = origin->ZatPoint(vp.Pos); + vp.Pos.Z = 2 * planez - vp.Pos.Z; + vp.ViewActor = nullptr; + state->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; + + state->PlaneMirrorFlag++; + di->SetClipHeight(planez, state->PlaneMirrorMode < 0 ? -1.f : 1.f); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + ClearClipper(di); + + di->UpdateCurrentMapSection(); + return true; +} + +void HWPlaneMirrorPortal::Shutdown(HWDrawInfo *di) +{ + auto state = mOwner->mState; + state->PlaneMirrorFlag--; + state->PlaneMirrorMode = old_pm; + std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); +} + +const char *HWPlaneMirrorPortal::GetName() { return "Planemirror"; } diff --git a/src/hwrenderer/scene/hw_portal.h b/src/hwrenderer/scene/hw_portal.h index da23db552..39b451530 100644 --- a/src/hwrenderer/scene/hw_portal.h +++ b/src/hwrenderer/scene/hw_portal.h @@ -1,5 +1,6 @@ #pragma once +#include "portal.h" #include "hw_drawinfo.h" #include "hw_drawstructs.h" #include "hwrenderer/textures/hw_material.h" @@ -39,16 +40,12 @@ struct FPortalSceneState; class IPortal { friend struct FPortalSceneState; -protected: +public: FPortalSceneState * mState; TArray lines; -public: + IPortal(FPortalSceneState *s, bool local); virtual ~IPortal() {} - virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } - virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } - virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } - virtual line_t *ClipLine() { return nullptr; } virtual void * GetSource() const = 0; // GetSource MUST be implemented! virtual const char *GetName() = 0; virtual bool IsSky() { return false; } @@ -127,3 +124,191 @@ inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s) { if (!local) s->portals.Push(this); } + + +class HWScenePortalBase +{ +protected: + IPortal *mOwner; +public: + HWScenePortalBase() {} + virtual ~HWScenePortalBase() {} + void SetOwner(IPortal *p) { mOwner = p; } + void ClearClipper(HWDrawInfo *di); + + virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } + virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } + virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } + virtual line_t *ClipLine() { return nullptr; } + + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return false; } + virtual bool NeedDepthBuffer() { return true; } + virtual void * GetSource() const = 0; // GetSource MUST be implemented! + virtual const char *GetName() = 0; + + virtual bool Setup(HWDrawInfo *di, Clipper *clipper) = 0; + virtual void Shutdown(HWDrawInfo *di) {} + virtual void RenderAttached(HWDrawInfo *di) {} + +}; + +struct HWLinePortal : public HWScenePortalBase +{ + // this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal. + vertex_t *v1, *v2; // vertices, from v1 to v2 + DVector2 delta; // precalculated v2 - v1 for side checking + + angle_t angv1, angv2; // for quick comparisons with a line or subsector + + HWLinePortal(line_t *line) + { + v1 = line->v1; + v2 = line->v2; + CalcDelta(); + } + + HWLinePortal(FLinePortalSpan *line) + { + if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr) + { + // For non-linked portals we must check the actual linedef. + line_t *lline = line->lines[0]->mDestination; + v1 = lline->v1; + v2 = lline->v2; + } + else + { + // For linked portals we can check the merged span. + v1 = line->v1; + v2 = line->v2; + } + CalcDelta(); + } + + void CalcDelta() + { + delta = v2->fPos() - v1->fPos(); + } + + line_t *line() + { + vertex_t **pv = &v1; + return reinterpret_cast(pv); + } + + int ClipSeg(seg_t *seg, const DVector3 &viewpos) override; + int ClipSubsector(subsector_t *sub) override; + int ClipPoint(const DVector2 &pos); + bool NeedCap() override { return false; } +}; + +struct HWMirrorPortal : public HWLinePortal +{ + // mirror portals always consist of single linedefs! + line_t * linedef; + +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + void * GetSource() const override { return linedef; } + const char *GetName() override; + +public: + + HWMirrorPortal(line_t * line) + : HWLinePortal(line) + { + linedef = line; + } +}; + + +struct HWLineToLinePortal : public HWLinePortal +{ + FLinePortalSpan *glport; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + virtual void * GetSource() const override { return glport; } + virtual const char *GetName() override; + virtual line_t *ClipLine() override { return line(); } + virtual void RenderAttached(HWDrawInfo *di) override; + +public: + + HWLineToLinePortal(FLinePortalSpan *ll) + : HWLinePortal(ll) + { + glport = ll; + } +}; + + +struct HWSkyboxPortal : public HWScenePortalBase +{ + bool oldclamp; + int old_pm; + FSectorPortal * portal; + +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return portal; } + virtual bool IsSky() { return true; } + virtual const char *GetName(); + +public: + + + HWSkyboxPortal(FSectorPortal * pt) + { + portal = pt; + } + +}; + + +struct HWSectorStackPortal : public HWScenePortalBase +{ + TArray subsectors; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. + virtual const char *GetName(); + FSectorPortalGroup *origin; + +public: + + HWSectorStackPortal(FSectorPortalGroup *pt) + { + origin = pt; + } + void SetupCoverage(HWDrawInfo *di); + void AddSubsector(subsector_t *sub) + { + subsectors.Push(sub); + } + +}; + +struct HWPlaneMirrorPortal : public HWScenePortalBase +{ + int old_pm; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + secplane_t * origin; + +public: + + HWPlaneMirrorPortal(secplane_t * pt) + { + origin = pt; + } + +}; + diff --git a/src/portal.h b/src/portal.h index 343e09b29..01b17fc8b 100644 --- a/src/portal.h +++ b/src/portal.h @@ -254,11 +254,6 @@ struct FSectorPortalGroup { DVector2 mDisplacement; int plane; - GLSectorStackPortal *glportal; // for quick access to the render data. This is only valid during BSP traversal! - - GLSectorStackPortal *GetRenderState(); - - void AddSubsector(subsector_t *sub); }; diff --git a/src/r_data/portalgroups.cpp b/src/r_data/portalgroups.cpp index a65f8b5e0..f1baac476 100644 --- a/src/r_data/portalgroups.cpp +++ b/src/r_data/portalgroups.cpp @@ -363,7 +363,6 @@ static void GroupSectorPortals() FSectorPortalGroup *portal = new FSectorPortalGroup; portal->mDisplacement = pair->Key.mDisplacement; portal->plane = (i == 1 ? sector_t::floor : sector_t::ceiling); /**/ - portal->glportal = NULL; level.portalGroups.Push(portal); for (unsigned j = 0; j < pair->Value.Size(); j++) { From e7a0ccf879ad7442d08f2ed539016b826d04b64b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Jun 2018 23:02:56 +0200 Subject: [PATCH 51/58] - fixed remaining issues with portal refactoring. --- src/gl/scene/gl_drawinfo.cpp | 3 +- src/gl/scene/gl_portal.cpp | 8 ++-- src/gl/scene/gl_portal.h | 2 +- src/gl/scene/gl_walls_draw.cpp | 48 +++++++++++++++------ src/hwrenderer/scene/hw_drawinfo.cpp | 16 ++++++- src/hwrenderer/scene/hw_drawinfo.h | 3 ++ src/hwrenderer/scene/hw_portal.cpp | 63 ++++++++++++---------------- src/hwrenderer/scene/hw_portal.h | 6 +-- src/hwrenderer/scene/hw_walls.cpp | 2 +- 9 files changed, 90 insertions(+), 61 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 1aa3eac57..f143c6795 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -484,10 +484,11 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) // Same here for the dependency on the portal. void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) { - auto portal = GLRenderer->mPortalState.FindPortal(ptg); + auto portal = FindPortal(ptg); if (!portal) { portal = new GLScenePortal(&GLRenderer->mPortalState, new HWSectorStackPortal(ptg)); + Portals.Push(portal); } auto ptl = static_cast(static_cast(portal)->mScene); ptl->AddSubsector(sub); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index a4bb2b16b..2aa853d12 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -149,11 +149,11 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw else if (gl_noquery) doquery = false; // Use occlusion query to avoid rendering portals that aren't visible - glBeginQuery(GL_SAMPLES_PASSED, GLRenderer->PortalQueryObject); + if (doquery) glBeginQuery(GL_SAMPLES_PASSED, GLRenderer->PortalQueryObject); DrawPortalStencil(); - glEndQuery(GL_SAMPLES_PASSED); + if (doquery) glEndQuery(GL_SAMPLES_PASSED); // Clear Z-buffer glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil @@ -170,9 +170,9 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw gl_RenderState.SetEffect(EFF_NONE); glDepthRange(0, 1); - GLuint sampleCount; + GLuint sampleCount = 1; - glGetQueryObjectuiv(GLRenderer->PortalQueryObject, GL_QUERY_RESULT, &sampleCount); + if (doquery) glGetQueryObjectuiv(GLRenderer->PortalQueryObject, GL_QUERY_RESULT, &sampleCount); if (sampleCount == 0) // not visible { diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index 890eb7909..4282f3d0b 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -62,7 +62,6 @@ protected: bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) override; void End(HWDrawInfo *di, bool usestencil) override; - void ClearClipper(HWDrawInfo *di); void ClearScreen(HWDrawInfo *di); }; @@ -88,6 +87,7 @@ public: static_cast(di)->DrawScene(DM_PORTAL); mScene->Shutdown(di); } + else ClearScreen(di); } virtual void RenderAttached(HWDrawInfo *di) { return mScene->RenderAttached(di); } }; diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index cf48f1348..61f51a1d5 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -347,27 +347,36 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) // Instead they are added to the portal manager case PORTALTYPE_HORIZON: wall->horizon = pstate.UniqueHorizons.Get(wall->horizon); - portal = pstate.FindPortal(wall->horizon); - if (!portal) portal = new GLHorizonPortal(&pstate, wall->horizon, Viewpoint); + portal = FindPortal(wall->horizon); + if (!portal) + { + portal = new GLHorizonPortal(&pstate, wall->horizon, Viewpoint); + Portals.Push(portal); + } portal->AddLine(wall); break; case PORTALTYPE_SKYBOX: - portal = pstate.FindPortal(wall->secportal); + portal = FindPortal(wall->secportal); if (!portal) { // either a regular skybox or an Eternity-style horizon if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(&pstate, wall->secportal); - else portal = new GLScenePortal(&pstate, new HWSkyboxPortal(wall->secportal)); + else + { + portal = new GLScenePortal(&pstate, new HWSkyboxPortal(wall->secportal)); + Portals.Push(portal); + } } portal->AddLine(wall); break; case PORTALTYPE_SECTORSTACK: - portal = pstate.FindPortal(wall->portal); + portal = FindPortal(wall->portal); if (!portal) { portal = new GLScenePortal(&pstate, new HWSectorStackPortal(wall->portal)); + Portals.Push(portal); } portal->AddLine(wall); break; @@ -377,15 +386,23 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { //@sync-portal wall->planemirror = pstate.UniquePlaneMirrors.Get(wall->planemirror); - portal = pstate.FindPortal(wall->planemirror); - if (!portal) portal = new GLScenePortal(&pstate, new HWPlaneMirrorPortal(wall->planemirror)); + portal = FindPortal(wall->planemirror); + if (!portal) + { + portal = new GLScenePortal(&pstate, new HWPlaneMirrorPortal(wall->planemirror)); + Portals.Push(portal); + } portal->AddLine(wall); } break; case PORTALTYPE_MIRROR: - portal = pstate.FindPortal(wall->seg->linedef); - if (!portal) portal = new GLScenePortal(&pstate, new HWMirrorPortal(wall->seg->linedef)); + portal = FindPortal(wall->seg->linedef); + if (!portal) + { + portal = new GLScenePortal(&pstate, new HWMirrorPortal(wall->seg->linedef)); + Portals.Push(portal); + } portal->AddLine(wall); if (gl_mirror_envmap) { @@ -395,23 +412,28 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) break; case PORTALTYPE_LINETOLINE: - portal = pstate.FindPortal(wall->lineportal); + portal = FindPortal(wall->lineportal); if (!portal) { line_t *otherside = wall->lineportal->lines[0]->mDestination; - if (otherside != NULL && otherside->portalindex < level.linePortals.Size()) + if (otherside != nullptr && otherside->portalindex < level.linePortals.Size()) { ProcessActorsInPortal(otherside->getPortal()->mGroup, in_area); } portal = new GLScenePortal(&pstate, new HWLineToLinePortal(wall->lineportal)); + Portals.Push(portal); } portal->AddLine(wall); break; case PORTALTYPE_SKY: wall->sky = pstate.UniqueSkies.Get(wall->sky); - portal = pstate.FindPortal(wall->sky); - if (!portal) portal = new GLSkyPortal(&pstate, wall->sky); + portal = FindPortal(wall->sky); + if (!portal) + { + portal = new GLSkyPortal(&pstate, wall->sky); + Portals.Push(portal); + } portal->AddLine(wall); break; } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 697715f61..b5dce2247 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -32,6 +32,7 @@ #include "g_levellocals.h" #include "hw_fakeflat.h" #include "hw_drawinfo.h" +#include "hw_portal.h" #include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/utility/hw_cvars.h" @@ -256,6 +257,20 @@ void HWDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool plane // //----------------------------------------------------------------------------- +IPortal * HWDrawInfo::FindPortal(const void * src) +{ + int i = Portals.Size() - 1; + + while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--; + return i >= 0 ? Portals[i] : nullptr; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + void HWViewpointUniforms::SetDefaults() { mProjectionMatrix.loadIdentity(); @@ -267,4 +282,3 @@ void HWViewpointUniforms::SetDefaults() mClipLine.X = -10000000.0f; } - diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 4c8a303c5..7f403e54d 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -110,6 +110,7 @@ struct HWDrawInfo Clipper *mClipper; FRenderViewpoint Viewpoint; HWViewpointUniforms VPUniforms; // per-viewpoint uniform state + TArray Portals; TArray MissingUpperTextures; TArray MissingLowerTextures; @@ -149,6 +150,7 @@ private: sector_t fakesec; // this is a struct member because it gets used in recursively called functions so it cannot be put on the stack. + void UnclipSubsector(subsector_t *sub); void AddLine(seg_t *seg, bool portalclip); void PolySubsector(subsector_t * sub); @@ -183,6 +185,7 @@ public: return (screen->hwcaps & RFL_NO_CLIP_PLANES) && VPUniforms.mClipLine.X > -1000000.f; } + IPortal * FindPortal(const void * src); void RenderBSPNode(void *node); void ClearBuffers(); diff --git a/src/hwrenderer/scene/hw_portal.cpp b/src/hwrenderer/scene/hw_portal.cpp index 248a9c94c..84cca96d7 100644 --- a/src/hwrenderer/scene/hw_portal.cpp +++ b/src/hwrenderer/scene/hw_portal.cpp @@ -35,14 +35,6 @@ EXTERN_CVAR(Int, r_mirror_recursions) -IPortal * FPortalSceneState::FindPortal(const void * src) -{ - int i = portals.Size() - 1; - - while (i >= 0 && portals[i] && portals[i]->GetSource() != src) i--; - return i >= 0 ? portals[i] : nullptr; -} - //----------------------------------------------------------------------------- // // StartFrame @@ -51,8 +43,6 @@ IPortal * FPortalSceneState::FindPortal(const void * src) void FPortalSceneState::StartFrame() { - IPortal * p = nullptr; - portals.Push(p); if (renderdepth == 0) { inskybox = false; @@ -82,22 +72,22 @@ static FString indent; // //----------------------------------------------------------------------------- -void FPortalSceneState::EndFrame(HWDrawInfo *outer_di) +void FPortalSceneState::EndFrame(HWDrawInfo *di) { IPortal * p; if (gl_portalinfo) { - Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), portals.Size(), renderdepth, indent.GetChars()); + Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), di->Portals.Size(), renderdepth, indent.GetChars()); indent += " "; } // Only use occlusion query if there are more than 2 portals. // Otherwise there's too much overhead. // (And don't forget to consider the separating null pointers!) - bool usequery = portals.Size() > 2 + (unsigned)renderdepth; + bool usequery = di->Portals.Size() > 2 + (unsigned)renderdepth; - while (portals.Pop(p) && p) + while (di->Portals.Pop(p) && p) { if (gl_portalinfo) { @@ -105,7 +95,7 @@ void FPortalSceneState::EndFrame(HWDrawInfo *outer_di) } if (p->lines.Size() > 0) { - p->RenderPortal(true, usequery, outer_di); + p->RenderPortal(true, usequery, di); } delete p; } @@ -115,7 +105,7 @@ void FPortalSceneState::EndFrame(HWDrawInfo *outer_di) { indent.Truncate(long(indent.Len()-2)); Printf("%s}\n", indent.GetChars()); - if (portals.Size() == 0) gl_portalinfo = false; + if (indent.Len() == 0) gl_portalinfo = false; } } @@ -131,23 +121,24 @@ bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di { IPortal * p; IPortal * best = nullptr; - unsigned bestindex=0; + unsigned bestindex = 0; // Find the one with the highest amount of lines. // Normally this is also the one that saves the largest amount // of time by drawing it before the scene itself. - for(int i = portals.Size()-1; i >= 0 && portals[i] != nullptr; --i) + auto &portals = outer_di->Portals; + for (int i = portals.Size() - 1; i >= 0; --i) { - p=portals[i]; + p = portals[i]; if (p->lines.Size() > 0 && p->IsSky()) { // Cannot clear the depth buffer inside a portal recursion if (recursion && p->NeedDepthBuffer()) continue; - if (!best || p->lines.Size()>best->lines.Size()) + if (!best || p->lines.Size() > best->lines.Size()) { - best=p; - bestindex=i; + best = p; + bestindex = i; } } } @@ -168,16 +159,16 @@ bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di // //----------------------------------------------------------------------------- -void HWScenePortalBase::ClearClipper(HWDrawInfo *di) +void HWScenePortalBase::ClearClipper(HWDrawInfo *di, Clipper *clipper) { auto outer_di = di->outer; DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); - di->mClipper->Clear(); + clipper->Clear(); auto &lines = mOwner->lines; // Set the clipper to the minimal visible area - di->mClipper->SafeAddClipRange(0, 0xffffffff); + clipper->SafeAddClipRange(0, 0xffffffff); for (unsigned int i = 0; i < lines.Size(); i++) { DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; @@ -185,16 +176,16 @@ void HWScenePortalBase::ClearClipper(HWDrawInfo *di) if (deltaangle(endAngle, startAngle) < 0) { - di->mClipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); + clipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); } } // and finally clip it to the visible area angle_t a1 = di->FrustumAngle(); - if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); + if (a1 < ANGLE_180) clipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); // lock the parts that have just been clipped out. - di->mClipper->SetSilhouette(); + clipper->SetSilhouette(); } @@ -406,7 +397,7 @@ bool HWLineToLinePortal::Setup(HWDrawInfo *di, Clipper *clipper) di->SetClipLine(glport->lines[0]->mDestination); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); - ClearClipper(di); + ClearClipper(di, clipper); return true; } @@ -469,7 +460,7 @@ bool HWSkyboxPortal::Setup(HWDrawInfo *di, Clipper *clipper) di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); di->SetViewArea(); - ClearClipper(di); + ClearClipper(di, clipper); di->UpdateCurrentMapSection(); return true; } @@ -559,15 +550,15 @@ bool HWSectorStackPortal::Setup(HWDrawInfo *di, Clipper *clipper) di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); SetupCoverage(di); - ClearClipper(di); + ClearClipper(di, clipper); // If the viewpoint is not within the portal, we need to invalidate the entire clip area. // The portal will re-validate the necessary parts when its subsectors get traversed. subsector_t *sub = R_PointInSubsector(vp.Pos); if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) { - di->mClipper->SafeAddClipRange(0, ANGLE_MAX); - di->mClipper->SetBlocked(true); + clipper->SafeAddClipRange(0, ANGLE_MAX); + clipper->SetBlocked(true); } return true; } @@ -607,7 +598,7 @@ bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); auto &vp = di->Viewpoint; - int old_pm = state->PlaneMirrorMode; + old_pm = state->PlaneMirrorMode; // the player is always visible in a mirror. vp.showviewer = true; @@ -620,7 +611,7 @@ bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) state->PlaneMirrorFlag++; di->SetClipHeight(planez, state->PlaneMirrorMode < 0 ? -1.f : 1.f); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); - ClearClipper(di); + ClearClipper(di, clipper); di->UpdateCurrentMapSection(); return true; @@ -634,4 +625,4 @@ void HWPlaneMirrorPortal::Shutdown(HWDrawInfo *di) std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); } -const char *HWPlaneMirrorPortal::GetName() { return "Planemirror"; } +const char *HWPlaneMirrorPortal::GetName() { return origin->fC() < 0? "Planemirror ceiling" : "Planemirror floor"; } diff --git a/src/hwrenderer/scene/hw_portal.h b/src/hwrenderer/scene/hw_portal.h index 39b451530..d3228ab1d 100644 --- a/src/hwrenderer/scene/hw_portal.h +++ b/src/hwrenderer/scene/hw_portal.h @@ -79,7 +79,6 @@ public: struct FPortalSceneState { - TArray portals; int recursion = 0; int MirrorFlag = 0; @@ -112,7 +111,6 @@ struct FPortalSceneState return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); } - IPortal * FindPortal(const void * src); void StartFrame(); bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di); void EndFrame(HWDrawInfo *outer_di); @@ -122,7 +120,7 @@ struct FPortalSceneState inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s) { - if (!local) s->portals.Push(this); + //if (!local) s->portals.Push(this); } @@ -134,7 +132,7 @@ public: HWScenePortalBase() {} virtual ~HWScenePortalBase() {} void SetOwner(IPortal *p) { mOwner = p; } - void ClearClipper(HWDrawInfo *di); + void ClearClipper(HWDrawInfo *di, Clipper *clipper); virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index 6c22e560a..1cd924aaf 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -1458,7 +1458,7 @@ void GLWall::Process(HWDrawInfo *di, seg_t *seg, sector_t * frontsector, sector_ sector_t * segback; #ifdef _DEBUG - if (seg->linedef->Index() == 10) + if (seg->linedef->Index() == 3407) { int a = 0; } From 65849441e52c6abb6e85663fd473b8a4ff6534c4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 09:32:46 +0200 Subject: [PATCH 52/58] - disable the wipe code entirely when a stereo3D mode is active. This simply cannot work right so let's better outright disable it. --- src/d_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index b3556c1db..66c0e3c33 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -104,6 +104,7 @@ EXTERN_CVAR(Bool, hud_althud) EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Int, vr_mode) void DrawHUD(); void D_DoAnonStats(); @@ -706,7 +707,7 @@ void D_Display () wipe = false; wipegamestate = gamestate; } - else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL) + else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL && (vr_mode == 0 || vid_rendermode != 4)) { // save the current screen if about to wipe switch (wipegamestate) { From 532fba5c26c3bb732df9a3af8ebb66dda04d4805 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 10:47:42 +0200 Subject: [PATCH 53/58] - moved the Stereo3D EyePose to their own file in hwrenderer/. --- src/CMakeLists.txt | 2 + src/d_main.cpp | 1 + src/gl/renderer/gl_postprocess.cpp | 4 +- src/gl/renderer/gl_renderer.h | 3 - src/gl/scene/gl_scene.cpp | 2 +- src/gl/stereo3d/gl_sidebyside3d.h | 16 ---- src/gl/stereo3d/gl_stereo3d.cpp | 28 ------- src/gl/stereo3d/gl_stereo3d.h | 25 +------ src/gl/stereo3d/gl_stereo_leftright.cpp | 43 ----------- src/gl/stereo3d/gl_stereo_leftright.h | 36 --------- src/hwrenderer/stereo3d/hw_eyepose.cpp | 97 +++++++++++++++++++++++++ src/hwrenderer/stereo3d/hw_eyepose.h | 63 ++++++++++++++++ src/v_video.h | 3 + 13 files changed, 170 insertions(+), 153 deletions(-) create mode 100644 src/hwrenderer/stereo3d/hw_eyepose.cpp create mode 100644 src/hwrenderer/stereo3d/hw_eyepose.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69b010dcf..40a3925c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -697,6 +697,7 @@ file( GLOB HEADER_FILES hwrenderer/dynlights/*.h hwrenderer/postprocessing/*.h hwrenderer/scene/*.h + hwrenderer/stereo3d/*.h hwrenderer/textures/*.h hwrenderer/utility/*.h gl/*.h @@ -1073,6 +1074,7 @@ set (PCH_SOURCES hwrenderer/data/flatvertices.cpp hwrenderer/dynlights/hw_aabbtree.cpp hwrenderer/dynlights/hw_shadowmap.cpp + hwrenderer/stereo3d/hw_eyepose.cpp hwrenderer/scene/hw_skydome.cpp hwrenderer/postprocessing/hw_postprocess_cvars.cpp hwrenderer/postprocessing/hw_postprocessshader.cpp diff --git a/src/d_main.cpp b/src/d_main.cpp index 66c0e3c33..9a2089954 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -707,6 +707,7 @@ void D_Display () wipe = false; wipegamestate = gamestate; } + // No wipes when in a stereo3D VR mode else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL && (vr_mode == 0 || vid_rendermode != 4)) { // save the current screen if about to wipe switch (wipegamestate) diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 61e69f2d7..e371699d7 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -114,8 +114,8 @@ void FGLRenderer::AmbientOccludeScene(float m5) mBuffers->BindSceneColorTexture(1); mLinearDepthShader->Bind(NOQUEUE); if (gl_multisample > 1) mLinearDepthShader->Uniforms->SampleIndex = 0; - mLinearDepthShader->Uniforms->LinearizeDepthA = 1.0f / GetZFar() - 1.0f / GetZNear(); - mLinearDepthShader->Uniforms->LinearizeDepthB = MAX(1.0f / GetZNear(), 1.e-8f); + mLinearDepthShader->Uniforms->LinearizeDepthA = 1.0f / screen->GetZFar() - 1.0f / screen->GetZNear(); + mLinearDepthShader->Uniforms->LinearizeDepthB = MAX(1.0f / screen->GetZNear(), 1.e-8f); mLinearDepthShader->Uniforms->InverseDepthRangeA = 1.0f; mLinearDepthShader->Uniforms->InverseDepthRangeB = 0.0f; mLinearDepthShader->Uniforms->Scale = sceneScale; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 44ccfe1a5..c89d85705 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -158,9 +158,6 @@ public: void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel, int bottomclip); - - static float GetZNear() { return 5.f; } - static float GetZFar() { return 65536.f; } }; #include "hwrenderer/scene/hw_fakeflat.h" diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index c5a500922..2e1b004aa 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -470,7 +470,7 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came stereo3dMode.SetUp(); for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) { - const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix); + const auto eye = stereo3dMode.getEyePose(eye_ix); eye->SetUp(); screen->SetViewportRects(bounds); Set3DViewport(mainview); diff --git a/src/gl/stereo3d/gl_sidebyside3d.h b/src/gl/stereo3d/gl_sidebyside3d.h index 071474c46..d3817c43d 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.h +++ b/src/gl/stereo3d/gl_sidebyside3d.h @@ -61,22 +61,6 @@ private: RightEyePose rightEye; }; -class SBSFLeftEyePose : public LeftEyePose { -public: - SBSFLeftEyePose(double ipdMeters) : LeftEyePose(ipdMeters) {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { - return LeftEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); - } -}; - -class SBSFRightEyePose : public RightEyePose { -public: - SBSFRightEyePose(double ipdMeters) : RightEyePose(ipdMeters) {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { - return RightEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); - } -}; - class SideBySideFull : public SideBySideBase { public: diff --git a/src/gl/stereo3d/gl_stereo3d.cpp b/src/gl/stereo3d/gl_stereo3d.cpp index 5a5a115d9..609e037db 100644 --- a/src/gl/stereo3d/gl_stereo3d.cpp +++ b/src/gl/stereo3d/gl_stereo3d.cpp @@ -31,34 +31,6 @@ namespace s3d { -/* virtual */ -VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const -{ - VSMatrix result; - - float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); - result.perspective(fovy, aspectRatio, FGLRenderer::GetZNear(), FGLRenderer::GetZFar()); - - return result; -} - -/* virtual */ -Viewport EyePose::GetViewport(const Viewport& fullViewport) const -{ - return fullViewport; -} - - -/* virtual */ -void EyePose::GetViewShift(float yaw, float outViewShift[3]) const -{ - // pass-through for Mono view - outViewShift[0] = 0; - outViewShift[1] = 0; - outViewShift[2] = 0; -} - - Stereo3DMode::Stereo3DMode() { } diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h index 542d45551..8468d5ad9 100644 --- a/src/gl/stereo3d/gl_stereo3d.h +++ b/src/gl/stereo3d/gl_stereo3d.h @@ -32,34 +32,11 @@ #include "tarray.h" #include "r_data/matrix.h" #include "gl/renderer/gl_renderer.h" - +#include "hwrenderer/stereo3d/hw_eyepose.h" /* stereoscopic 3D API */ namespace s3d { - -/* Subregion of current display window */ -class Viewport -{ -public: - int x, y; - int width, height; -}; - - -/* Viewpoint of one eye */ -class EyePose -{ -public: - EyePose() {} - virtual ~EyePose() {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; - virtual Viewport GetViewport(const Viewport& fullViewport) const; - virtual void GetViewShift(float yaw, float outViewShift[3]) const; - virtual void SetUp() const {}; -}; - - /* Base class for stereoscopic 3D rendering modes */ class Stereo3DMode { diff --git a/src/gl/stereo3d/gl_stereo_leftright.cpp b/src/gl/stereo3d/gl_stereo_leftright.cpp index f6eebc509..be1d0a3d9 100644 --- a/src/gl/stereo3d/gl_stereo_leftright.cpp +++ b/src/gl/stereo3d/gl_stereo_leftright.cpp @@ -34,52 +34,9 @@ #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_renderbuffers.h" -EXTERN_CVAR(Float, vr_screendist) -EXTERN_CVAR(Float, vr_hunits_per_meter) -EXTERN_CVAR(Bool, vr_swap_eyes) - namespace s3d { -/* virtual */ -VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const -{ - double zNear = 5.0; - double zFar = 65536.0; - - // For stereo 3D, use asymmetric frustum shift in projection matrix - // Q: shouldn't shift vary with roll angle, at least for desktop display? - // A: No. (lab) roll is not measured on desktop display (yet) - double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units - // double frustumShift = 0; // Turning off shift for debugging - double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; - double fW = fH * aspectRatio; - double left = -fW - frustumShift; - double right = fW - frustumShift; - double bottom = -fH; - double top = fH; - - VSMatrix result(1); - result.frustum(left, right, bottom, top, zNear, zFar); - return result; -} - - -/* virtual */ -void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const -{ - float dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); - float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); - outViewShift[0] = dx; - outViewShift[1] = dy; - outViewShift[2] = 0; -} - -float ShiftedEyePose::getShift() const -{ - return vr_swap_eyes ? -shift : shift; -} - /* static */ const LeftEyeView& LeftEyeView::getInstance(float ipd) { diff --git a/src/gl/stereo3d/gl_stereo_leftright.h b/src/gl/stereo3d/gl_stereo_leftright.h index 9fa7b40bc..ad4ccb769 100644 --- a/src/gl/stereo3d/gl_stereo_leftright.h +++ b/src/gl/stereo3d/gl_stereo_leftright.h @@ -33,40 +33,6 @@ namespace s3d { -class ShiftedEyePose : public EyePose -{ -public: - ShiftedEyePose(float shift) : shift(shift) {}; - float getShift() const; - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; - virtual void GetViewShift(float yaw, float outViewShift[3]) const; - -protected: - void setShift(float shift) { this->shift = shift; } - -private: - float shift; -}; - - -class LeftEyePose : public ShiftedEyePose -{ -public: - LeftEyePose(float ipd) : ShiftedEyePose( float(-0.5) * ipd) {} - float getIpd() const { return float(fabs(2.0f*getShift())); } - void setIpd(float ipd) { setShift(float(-0.5)*ipd); } -}; - - -class RightEyePose : public ShiftedEyePose -{ -public: - RightEyePose(float ipd) : ShiftedEyePose(float(+0.5)*ipd) {} - float getIpd() const { return float(fabs(2.0f*getShift())); } - void setIpd(float ipd) { setShift(float(+0.5)*ipd); } -}; - - /** * As if viewed through the left eye only */ @@ -76,7 +42,6 @@ public: static const LeftEyeView& getInstance(float ipd); LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } - float getIpd() const { return eye.getIpd(); } void setIpd(float ipd) { eye.setIpd(ipd); } void Present() const override; protected: @@ -90,7 +55,6 @@ public: static const RightEyeView& getInstance(float ipd); RightEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } - float getIpd() const { return eye.getIpd(); } void setIpd(float ipd) { eye.setIpd(ipd); } void Present() const override; protected: diff --git a/src/hwrenderer/stereo3d/hw_eyepose.cpp b/src/hwrenderer/stereo3d/hw_eyepose.cpp new file mode 100644 index 000000000..e8ac56d6b --- /dev/null +++ b/src/hwrenderer/stereo3d/hw_eyepose.cpp @@ -0,0 +1,97 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_stereo_leftright.cpp +** Offsets for left and right eye views +** +*/ + +#include "vectors.h" // RAD2DEG +#include "doomtype.h" // M_PI +#include "hwrenderer/utility/hw_cvars.h" +#include "hw_eyepose.h" +#include "v_video.h" + +EXTERN_CVAR(Float, vr_screendist) +EXTERN_CVAR(Float, vr_hunits_per_meter) +EXTERN_CVAR(Bool, vr_swap_eyes) + + +/* virtual */ +VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const +{ + VSMatrix result; + + float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); + result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar()); + + return result; +} + +/* virtual */ +void EyePose::GetViewShift(float yaw, float outViewShift[3]) const +{ + // pass-through for Mono view + outViewShift[0] = 0; + outViewShift[1] = 0; + outViewShift[2] = 0; +} + +/* virtual */ +VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const +{ + double zNear = screen->GetZNear(); + double zFar = screen->GetZFar(); + + // For stereo 3D, use asymmetric frustum shift in projection matrix + // Q: shouldn't shift vary with roll angle, at least for desktop display? + // A: No. (lab) roll is not measured on desktop display (yet) + double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units + // double frustumShift = 0; // Turning off shift for debugging + double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; + double fW = fH * aspectRatio; + double left = -fW - frustumShift; + double right = fW - frustumShift; + double bottom = -fH; + double top = fH; + + VSMatrix result(1); + result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar); + return result; +} + + +/* virtual */ +void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const +{ + float dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + outViewShift[0] = dx; + outViewShift[1] = dy; + outViewShift[2] = 0; +} + +float ShiftedEyePose::getShift() const +{ + return vr_swap_eyes ? -shift : shift; +} + diff --git a/src/hwrenderer/stereo3d/hw_eyepose.h b/src/hwrenderer/stereo3d/hw_eyepose.h new file mode 100644 index 000000000..64ff9c27c --- /dev/null +++ b/src/hwrenderer/stereo3d/hw_eyepose.h @@ -0,0 +1,63 @@ +#pragma once + +#include "r_data/matrix.h" + + +/* Viewpoint of one eye */ +class EyePose +{ +public: + EyePose() {} + virtual ~EyePose() {} + virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; + virtual void GetViewShift(float yaw, float outViewShift[3]) const; + virtual void SetUp() const {} +}; + +class ShiftedEyePose : public EyePose +{ +public: + ShiftedEyePose(float shift) : shift(shift) {}; + float getShift() const; + virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; + virtual void GetViewShift(float yaw, float outViewShift[3]) const; + +protected: + void setShift(float shift) { this->shift = shift; } + +private: + float shift; +}; + + +class LeftEyePose : public ShiftedEyePose +{ +public: + LeftEyePose(float ipd) : ShiftedEyePose( -0.5f * ipd) {} + void setIpd(float ipd) { setShift(-0.5f * ipd); } +}; + + +class RightEyePose : public ShiftedEyePose +{ +public: + RightEyePose(float ipd) : ShiftedEyePose(0.5f * ipd) {} + void setIpd(float ipd) { setShift(0.5f * ipd); } +}; + + +class SBSFLeftEyePose : public LeftEyePose { +public: + SBSFLeftEyePose(float ipdMeters) : LeftEyePose(ipdMeters) {} + virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { + return LeftEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); + } +}; + +class SBSFRightEyePose : public RightEyePose { +public: + SBSFRightEyePose(float ipdMeters) : RightEyePose(ipdMeters) {} + virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { + return RightEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); + } +}; diff --git a/src/v_video.h b/src/v_video.h index de16b85eb..ec17b86fa 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -527,6 +527,9 @@ public: // points to the last row in the buffer, which will be the first row output. virtual void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type, float &gamma) {} + static float GetZNear() { return 5.f; } + static float GetZFar() { return 65536.f; } + // The original size of the framebuffer as selected in the video menu. uint64_t FrameTime = 0; From 840c2c8958ca4361255fd6c6a1d60f3652477092 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 10:48:04 +0200 Subject: [PATCH 54/58] - fixed warning in ADLMidi. --- src/sound/adlmidi/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/adlmidi/adlmidi_midiplay.cpp b/src/sound/adlmidi/adlmidi_midiplay.cpp index 89c1a73ad..8996d67d0 100644 --- a/src/sound/adlmidi/adlmidi_midiplay.cpp +++ b/src/sound/adlmidi/adlmidi_midiplay.cpp @@ -2686,7 +2686,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) std::fflush(stderr); #endif - for(unsigned a = 0, n = P->adl_ins_list.size(); a < n; ++a) + for(unsigned a = 0, n = (unsigned)P->adl_ins_list.size(); a < n; ++a) { const unsigned i = P->adl_ins_list[a]; const adlinsdata2 ains(adlins[i]); From 4ef7b66c4b69f6ef8f6f4e61ad3263d53019f2b1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 11:45:40 +0200 Subject: [PATCH 55/58] - partial consolidation of the EyePose classes. The SBS versions can just as easily be handled with an additional parameter. --- src/gl/stereo3d/gl_sidebyside3d.cpp | 2 +- src/gl/stereo3d/gl_sidebyside3d.h | 4 ++-- src/hwrenderer/stereo3d/hw_eyepose.cpp | 2 +- src/hwrenderer/stereo3d/hw_eyepose.h | 24 ++++-------------------- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/gl/stereo3d/gl_sidebyside3d.cpp b/src/gl/stereo3d/gl_sidebyside3d.cpp index 4f18033e1..12d7cf41f 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.cpp +++ b/src/gl/stereo3d/gl_sidebyside3d.cpp @@ -92,7 +92,7 @@ const SideBySideFull& SideBySideFull::getInstance(float ipd) } SideBySideFull::SideBySideFull(double ipdMeters) - : leftEye(ipdMeters), rightEye(ipdMeters) + : leftEye(ipdMeters, 0.5f), rightEye(ipdMeters, 0.5f) { eye_ptrs.Push(&leftEye); eye_ptrs.Push(&rightEye); diff --git a/src/gl/stereo3d/gl_sidebyside3d.h b/src/gl/stereo3d/gl_sidebyside3d.h index d3817c43d..ca0a22aa4 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.h +++ b/src/gl/stereo3d/gl_sidebyside3d.h @@ -68,8 +68,8 @@ public: SideBySideFull(double ipdMeters); virtual void AdjustPlayerSprites(FDrawInfo *di) const override; private: - SBSFLeftEyePose leftEye; - SBSFRightEyePose rightEye; + LeftEyePose leftEye; + RightEyePose rightEye; }; class TopBottom3D : public SideBySideSquished diff --git a/src/hwrenderer/stereo3d/hw_eyepose.cpp b/src/hwrenderer/stereo3d/hw_eyepose.cpp index e8ac56d6b..43e6d9bd4 100644 --- a/src/hwrenderer/stereo3d/hw_eyepose.cpp +++ b/src/hwrenderer/stereo3d/hw_eyepose.cpp @@ -68,7 +68,7 @@ VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRa double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units // double frustumShift = 0; // Turning off shift for debugging double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; - double fW = fH * aspectRatio; + double fW = fH * aspectRatio * squish; double left = -fW - frustumShift; double right = fW - frustumShift; double bottom = -fH; diff --git a/src/hwrenderer/stereo3d/hw_eyepose.h b/src/hwrenderer/stereo3d/hw_eyepose.h index 64ff9c27c..7531f29ba 100644 --- a/src/hwrenderer/stereo3d/hw_eyepose.h +++ b/src/hwrenderer/stereo3d/hw_eyepose.h @@ -17,7 +17,7 @@ public: class ShiftedEyePose : public EyePose { public: - ShiftedEyePose(float shift) : shift(shift) {}; + ShiftedEyePose(float shift, float squish) : shift(shift), squish(squish) {}; float getShift() const; virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; virtual void GetViewShift(float yaw, float outViewShift[3]) const; @@ -27,13 +27,14 @@ protected: private: float shift; + float squish; }; class LeftEyePose : public ShiftedEyePose { public: - LeftEyePose(float ipd) : ShiftedEyePose( -0.5f * ipd) {} + LeftEyePose(float ipd, float squish = 1.f) : ShiftedEyePose( -0.5f * ipd, squish) {} void setIpd(float ipd) { setShift(-0.5f * ipd); } }; @@ -41,23 +42,6 @@ public: class RightEyePose : public ShiftedEyePose { public: - RightEyePose(float ipd) : ShiftedEyePose(0.5f * ipd) {} + RightEyePose(float ipd, float squish = 1.f) : ShiftedEyePose(0.5f * ipd, squish) {} void setIpd(float ipd) { setShift(0.5f * ipd); } }; - - -class SBSFLeftEyePose : public LeftEyePose { -public: - SBSFLeftEyePose(float ipdMeters) : LeftEyePose(ipdMeters) {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { - return LeftEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); - } -}; - -class SBSFRightEyePose : public RightEyePose { -public: - SBSFRightEyePose(float ipdMeters) : RightEyePose(ipdMeters) {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const override { - return RightEyePose::GetProjection(fov, 0.5f * aspectRatio, fovRatio); - } -}; From 036307927a655e7549f0a938c6867344e25f0821 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 24 Jun 2018 13:35:45 +0300 Subject: [PATCH 56/58] - refactored video part of Cocoa backend Window related functionality is moved to framebuffer class Video settings are now applied properly on startup --- src/posix/cocoa/gl_sysfb.h | 25 ++- src/posix/cocoa/i_video.mm | 357 ++++++++++++++++--------------------- 2 files changed, 177 insertions(+), 205 deletions(-) diff --git a/src/posix/cocoa/gl_sysfb.h b/src/posix/cocoa/gl_sysfb.h index ddd000e5c..7eefa0fd9 100644 --- a/src/posix/cocoa/gl_sysfb.h +++ b/src/posix/cocoa/gl_sysfb.h @@ -36,6 +36,14 @@ #include "v_video.h" +#ifdef __OBJC__ +@class NSCursor; +@class CocoaWindow; +#else +typedef struct objc_object NSCursor; +typedef struct objc_object CocoaWindow; +#endif + class SystemFrameBuffer : public DFrameBuffer { public: @@ -50,8 +58,18 @@ public: int GetClientHeight() override; void ToggleFullscreen(bool yes) override; + void SetMode(bool fullscreen, bool hiDPI); + + static void UseHiDPI(bool hiDPI); + static void SetCursor(NSCursor* cursor); + static void SetWindowVisible(bool visible); + static void SetWindowTitle(const char* title); + protected: - bool UpdatePending; + CocoaWindow* m_window; + + bool m_fullscreen; + bool m_hiDPI; static const uint32_t GAMMA_CHANNEL_SIZE = 256; static const uint32_t GAMMA_CHANNEL_COUNT = 3; @@ -60,7 +78,10 @@ protected: bool m_supportsGamma; uint16_t m_originalGamma[GAMMA_TABLE_SIZE]; - SystemFrameBuffer(); + SystemFrameBuffer() {} + + void SetFullscreenMode(); + void SetWindowedMode(); void InitializeState(); diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 869dc4e3b..d44be138e 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -2,7 +2,7 @@ ** i_video.mm ** **--------------------------------------------------------------------------- - ** Copyright 2012-2015 Alexey Lysiuk + ** Copyright 2012-2018 Alexey Lysiuk ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -213,28 +213,15 @@ namespace class CocoaVideo : public IVideo { public: - CocoaVideo(); + virtual DFrameBuffer* CreateFrameBuffer() override + { + auto fb = new OpenGLFrameBuffer(nullptr, fullscreen); - virtual DFrameBuffer* CreateFrameBuffer(); + fb->SetMode(fullscreen, vid_hidpi); + fb->SetSize(fb->GetClientWidth(), fb->GetClientHeight()); - static void ToggleFullscreen(bool yes); - static bool IsFullscreen(); - static void UseHiDPI(bool hiDPI); - static void SetCursor(NSCursor* cursor); - static void SetWindowVisible(bool visible); - static void SetWindowTitle(const char* title); - -private: - CocoaWindow* m_window; - - bool m_fullscreen; - bool m_hiDPI; - - void SetFullscreenMode(); - void SetWindowedMode(); - void SetMode(bool fullscreen, bool hiDPI); - - static CocoaVideo* GetInstance(); + return fb; + } }; @@ -277,7 +264,7 @@ extern id appCtrl; namespace { -CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) +CocoaWindow* CreateWindow(const NSUInteger styleMask) { CocoaWindow* const window = [CocoaWindow alloc]; [window initWithContentRect:NSMakeRect(0, 0, vid_defwidth, vid_defheight) @@ -332,11 +319,14 @@ NSOpenGLPixelFormat* CreatePixelFormat(const NSOpenGLPixelFormatAttribute profil // --------------------------------------------------------------------------- -CocoaVideo::CocoaVideo() -: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen) +: DFrameBuffer(vid_defwidth, vid_defheight) +, m_window(CreateWindow(STYLE_MASK_WINDOWED)) , m_fullscreen(false) , m_hiDPI(false) { + SetFlash(0, 0); + // Create OpenGL pixel format NSOpenGLPixelFormatAttribute defaultProfile = NSOpenGLProfileVersion3_2Core; @@ -376,170 +366,8 @@ CocoaVideo::CocoaVideo() [m_window setContentView:glView]; - FConsoleWindow::GetInstance().Show(false); -} + // Create table for system-wide gamma correction -DFrameBuffer* CocoaVideo::CreateFrameBuffer() -{ - PalEntry flashColor = 0; - int flashAmount = 0; - - DFrameBuffer* fb = NULL; - - fb = new OpenGLFrameBuffer(NULL, fullscreen); - - fb->SetFlash(flashColor, flashAmount); - - SetMode(fullscreen, vid_hidpi); - - return fb; -} - -void CocoaVideo::ToggleFullscreen(bool yes) -{ - if (CocoaVideo* const video = GetInstance()) - { - video->SetMode(yes, video->m_hiDPI); - } -} - -bool CocoaVideo::IsFullscreen() -{ - CocoaVideo* const video = GetInstance(); - return NULL == video - ? false - : video->m_fullscreen; -} - -void CocoaVideo::UseHiDPI(const bool hiDPI) -{ - if (CocoaVideo* const video = GetInstance()) - { - video->SetMode(video->m_fullscreen, hiDPI); - } -} - -void CocoaVideo::SetCursor(NSCursor* cursor) -{ - if (CocoaVideo* const video = GetInstance()) - { - NSWindow* const window = video->m_window; - CocoaView* const view = [window contentView]; - - [view setCursor:cursor]; - [window invalidateCursorRectsForView:view]; - } -} - -void CocoaVideo::SetWindowVisible(bool visible) -{ - if (CocoaVideo* const video = GetInstance()) - { - if (visible) - { - [video->m_window orderFront:nil]; - } - else - { - [video->m_window orderOut:nil]; - } - - I_SetNativeMouse(!visible); - } -} - -void CocoaVideo::SetWindowTitle(const char* title) -{ - if (CocoaVideo* const video = GetInstance()) - { - NSString* const nsTitle = nullptr == title ? nil : - [NSString stringWithCString:title encoding:NSISOLatin1StringEncoding]; - [video->m_window setTitle:nsTitle]; - } -} - - -void CocoaVideo::SetFullscreenMode() -{ - if (!m_fullscreen) - { - [m_window setLevel:LEVEL_FULLSCREEN]; - [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; - - [m_window setHidesOnDeactivate:YES]; - } - - const NSRect screenFrame = [[m_window screen] frame]; - [m_window setFrame:screenFrame display:YES]; -} - -void CocoaVideo::SetWindowedMode() -{ - if (m_fullscreen) - { - [m_window setLevel:LEVEL_WINDOWED]; - [m_window setStyleMask:STYLE_MASK_WINDOWED]; - - [m_window setHidesOnDeactivate:NO]; - } - - const bool isFrameValid = win_x >= 0 && win_y >= 0 && win_w > 320 && win_h > 200; - const NSRect frameSize = isFrameValid - ? NSMakeRect(win_x, win_y, win_w, win_h) - : NSMakeRect(0, 0, vid_defwidth, vid_defheight); - - [m_window setFrame:frameSize display:YES]; - [m_window enterFullscreenOnZoom]; - [m_window exitAppOnClose]; -} - -void CocoaVideo::SetMode(const bool fullscreen, const bool hiDPI) -{ - NSOpenGLView* const glView = [m_window contentView]; - [glView setWantsBestResolutionOpenGLSurface:hiDPI]; - - if (fullscreen) - { - SetFullscreenMode(); - } - else - { - SetWindowedMode(); - } - - const NSSize viewSize = I_GetContentViewSize(m_window); - - glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - [[NSOpenGLContext currentContext] flushBuffer]; - - [m_window updateTitle]; - - if (![m_window isKeyWindow]) - { - [m_window makeKeyAndOrderFront:nil]; - } - - m_fullscreen = fullscreen; - m_hiDPI = hiDPI; -} - - -CocoaVideo* CocoaVideo::GetInstance() -{ - return static_cast(Video); -} - - -// --------------------------------------------------------------------------- - - -SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen) -: DFrameBuffer(vid_defwidth, vid_defheight) -, UpdatePending(false) -{ CGGammaValue gammaTable[GAMMA_TABLE_SIZE]; uint32_t actualChannelSize; @@ -554,24 +382,29 @@ SystemFrameBuffer::SystemFrameBuffer(void*, const bool fullscreen) m_originalGamma[i] = static_cast(gammaTable[i] * 65535.0f); } } -} -SystemFrameBuffer::SystemFrameBuffer() -{ + FConsoleWindow::GetInstance().Show(false); } SystemFrameBuffer::~SystemFrameBuffer() { + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:m_window + name:NSWindowDidMoveNotification + object:nil]; + [nc removeObserver:m_window + name:NSWindowDidEndLiveResizeNotification + object:nil]; } bool SystemFrameBuffer::IsFullscreen() { - return CocoaVideo::IsFullscreen(); + return m_fullscreen; } void SystemFrameBuffer::ToggleFullscreen(bool yes) { - CocoaVideo::ToggleFullscreen(yes); + SetMode(yes, m_hiDPI); } @@ -617,23 +450,141 @@ void SystemFrameBuffer::ResetGammaTable() } } + int SystemFrameBuffer::GetClientWidth() { - NSView *view = [[NSOpenGLContext currentContext] view]; - NSRect backingBounds = [view convertRectToBacking: [view bounds]]; - int clientWidth = (int)backingBounds.size.width; + const int clientWidth = I_GetContentViewSize(m_window).width; return clientWidth > 0 ? clientWidth : GetWidth(); } int SystemFrameBuffer::GetClientHeight() { - NSView *view = [[NSOpenGLContext currentContext] view]; - NSRect backingBounds = [view convertRectToBacking: [view bounds]]; - int clientHeight = (int)backingBounds.size.height; + const int clientHeight = I_GetContentViewSize(m_window).height; return clientHeight > 0 ? clientHeight : GetHeight(); } +void SystemFrameBuffer::SetFullscreenMode() +{ + if (!m_fullscreen) + { + [m_window setLevel:LEVEL_FULLSCREEN]; + [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; + + [m_window setHidesOnDeactivate:YES]; + } + + const NSRect screenFrame = [[m_window screen] frame]; + [m_window setFrame:screenFrame display:YES]; +} + +void SystemFrameBuffer::SetWindowedMode() +{ + if (m_fullscreen) + { + [m_window setLevel:LEVEL_WINDOWED]; + [m_window setStyleMask:STYLE_MASK_WINDOWED]; + + [m_window setHidesOnDeactivate:NO]; + } + + const bool isFrameValid = win_x >= 0 && win_y >= 0 && win_w > 320 && win_h > 200; + const NSRect frameSize = isFrameValid + ? NSMakeRect(win_x, win_y, win_w, win_h) + : NSMakeRect(0, 0, vid_defwidth, vid_defheight); + + [m_window setFrame:frameSize display:YES]; + [m_window enterFullscreenOnZoom]; + [m_window exitAppOnClose]; +} + +void SystemFrameBuffer::SetMode(const bool fullscreen, const bool hiDPI) +{ + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; + + if (fullscreen) + { + SetFullscreenMode(); + } + else + { + SetWindowedMode(); + } + + const NSSize viewSize = I_GetContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + [[NSOpenGLContext currentContext] flushBuffer]; + + [m_window updateTitle]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } + + m_fullscreen = fullscreen; + m_hiDPI = hiDPI; +} + + +static SystemFrameBuffer* GetSystemFrameBuffer() +{ + return static_cast(screen); +} + +void SystemFrameBuffer::UseHiDPI(const bool hiDPI) +{ + if (auto fb = GetSystemFrameBuffer()) + { + fb->SetMode(fb->m_fullscreen, hiDPI); + } +} + +void SystemFrameBuffer::SetCursor(NSCursor* cursor) +{ + if (auto fb = GetSystemFrameBuffer()) + { + NSWindow* const window = fb->m_window; + CocoaView* const view = [window contentView]; + + [view setCursor:cursor]; + [window invalidateCursorRectsForView:view]; + } +} + +void SystemFrameBuffer::SetWindowVisible(bool visible) +{ + if (auto fb = GetSystemFrameBuffer()) + { + if (visible) + { + [fb->m_window orderFront:nil]; + } + else + { + [fb->m_window orderOut:nil]; + } + + I_SetNativeMouse(!visible); + } +} + +void SystemFrameBuffer::SetWindowTitle(const char* title) +{ + if (auto fb = GetSystemFrameBuffer()) + { + NSString* const nsTitle = nullptr == title ? nil : + [NSString stringWithCString:title encoding:NSISOLatin1StringEncoding]; + [fb->m_window setTitle:nsTitle]; + } +} + + // --------------------------------------------------------------------------- @@ -674,9 +625,9 @@ void I_SetFPSLimit(int limit) { } -CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { - CocoaVideo::UseHiDPI(self); + SystemFrameBuffer::UseHiDPI(self); } @@ -737,7 +688,7 @@ bool I_SetCursor(FTexture* cursorpic) hotSpot:NSMakePoint(0.0f, 0.0f)]; } - CocoaVideo::SetCursor(cursor); + SystemFrameBuffer::SetCursor(cursor); [pool release]; @@ -757,11 +708,11 @@ NSSize I_GetContentViewSize(const NSWindow* const window) void I_SetMainWindowVisible(bool visible) { - CocoaVideo::SetWindowVisible(visible); + SystemFrameBuffer::SetWindowVisible(visible); } // each platform has its own specific version of this function. void I_SetWindowTitle(const char* title) { - CocoaVideo::SetWindowTitle(title); + SystemFrameBuffer::SetWindowTitle(title); } From c3d5b960ee9e276aaa9a5ec15fb7b89d3f031794 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 13:39:14 +0200 Subject: [PATCH 57/58] - refactored the entire Stereo3D system to get rid of the class hierarchy and all its ugly implications. Ultimately all this needs is a small data table describing the geometric properties of each mode and a single Present function that calls the mode specific variants. Code size got reduced from 50kb to less than 20kb with proper separation of the generic parts from the OpenGL parts. --- src/CMakeLists.txt | 14 +- src/gl/renderer/gl_postprocess.cpp | 12 +- src/gl/renderer/gl_renderer.cpp | 1 - src/gl/renderer/gl_renderer.h | 1 + src/gl/renderer/gl_stereo3d.cpp | 364 +++++++++++++++++++ src/gl/scene/gl_drawinfo.cpp | 65 ++-- src/gl/scene/gl_portal.cpp | 88 +++-- src/gl/scene/gl_scene.cpp | 28 +- src/gl/scene/gl_weapon.cpp | 3 - src/gl/stereo3d/gl_anaglyph.cpp | 84 ----- src/gl/stereo3d/gl_anaglyph.h | 113 ------ src/gl/stereo3d/gl_interleaved3d.cpp | 232 ------------ src/gl/stereo3d/gl_interleaved3d.h | 75 ---- src/gl/stereo3d/gl_quadstereo.cpp | 115 ------ src/gl/stereo3d/gl_quadstereo.h | 77 ---- src/gl/stereo3d/gl_sidebyside3d.cpp | 150 -------- src/gl/stereo3d/gl_sidebyside3d.h | 87 ----- src/gl/stereo3d/gl_stereo3d.cpp | 53 --- src/gl/stereo3d/gl_stereo3d.h | 90 ----- src/gl/stereo3d/gl_stereo_cvars.cpp | 127 ------- src/gl/stereo3d/gl_stereo_leftright.cpp | 73 ---- src/gl/stereo3d/gl_stereo_leftright.h | 67 ---- src/gl/stereo3d/scoped_color_mask.h | 56 --- src/gl/system/gl_framebuffer.cpp | 7 +- src/hwrenderer/stereo3d/hw_eyepose.cpp | 97 ----- src/hwrenderer/stereo3d/hw_eyepose.h | 47 --- src/hwrenderer/utility/hw_vrmodes.cpp | 176 +++++++++ src/hwrenderer/utility/hw_vrmodes.h | 46 +++ src/hwrenderer/utility/scoped_view_shifter.h | 62 ---- 29 files changed, 687 insertions(+), 1723 deletions(-) create mode 100644 src/gl/renderer/gl_stereo3d.cpp delete mode 100644 src/gl/stereo3d/gl_anaglyph.cpp delete mode 100644 src/gl/stereo3d/gl_anaglyph.h delete mode 100644 src/gl/stereo3d/gl_interleaved3d.cpp delete mode 100644 src/gl/stereo3d/gl_interleaved3d.h delete mode 100644 src/gl/stereo3d/gl_quadstereo.cpp delete mode 100644 src/gl/stereo3d/gl_quadstereo.h delete mode 100644 src/gl/stereo3d/gl_sidebyside3d.cpp delete mode 100644 src/gl/stereo3d/gl_sidebyside3d.h delete mode 100644 src/gl/stereo3d/gl_stereo3d.cpp delete mode 100644 src/gl/stereo3d/gl_stereo3d.h delete mode 100644 src/gl/stereo3d/gl_stereo_cvars.cpp delete mode 100644 src/gl/stereo3d/gl_stereo_leftright.cpp delete mode 100644 src/gl/stereo3d/gl_stereo_leftright.h delete mode 100644 src/gl/stereo3d/scoped_color_mask.h delete mode 100644 src/hwrenderer/stereo3d/hw_eyepose.cpp delete mode 100644 src/hwrenderer/stereo3d/hw_eyepose.h create mode 100644 src/hwrenderer/utility/hw_vrmodes.cpp create mode 100644 src/hwrenderer/utility/hw_vrmodes.h delete mode 100644 src/hwrenderer/utility/scoped_view_shifter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40a3925c5..d77e733e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -697,7 +697,6 @@ file( GLOB HEADER_FILES hwrenderer/dynlights/*.h hwrenderer/postprocessing/*.h hwrenderer/scene/*.h - hwrenderer/stereo3d/*.h hwrenderer/textures/*.h hwrenderer/utility/*.h gl/*.h @@ -706,7 +705,6 @@ file( GLOB HEADER_FILES gl/models/*.h gl/renderer/*.h gl/scene/*.h - gl/stereo3d/*.h gl/shaders/*.h gl/system/*.h gl/textures/*.h @@ -1055,16 +1053,10 @@ set (PCH_SOURCES gl/renderer/gl_lightdata.cpp gl/renderer/gl_postprocess.cpp gl/renderer/gl_postprocessstate.cpp + gl/renderer/gl_stereo3d.cpp gl/shaders/gl_shader.cpp gl/shaders/gl_shaderprogram.cpp gl/shaders/gl_postprocessshader.cpp - gl/stereo3d/gl_stereo3d.cpp - gl/stereo3d/gl_stereo_cvars.cpp - gl/stereo3d/gl_stereo_leftright.cpp - gl/stereo3d/gl_anaglyph.cpp - gl/stereo3d/gl_quadstereo.cpp - gl/stereo3d/gl_sidebyside3d.cpp - gl/stereo3d/gl_interleaved3d.cpp gl_load/gl_interface.cpp gl/system/gl_framebuffer.cpp gl/system/gl_debug.cpp @@ -1074,7 +1066,6 @@ set (PCH_SOURCES hwrenderer/data/flatvertices.cpp hwrenderer/dynlights/hw_aabbtree.cpp hwrenderer/dynlights/hw_shadowmap.cpp - hwrenderer/stereo3d/hw_eyepose.cpp hwrenderer/scene/hw_skydome.cpp hwrenderer/postprocessing/hw_postprocess_cvars.cpp hwrenderer/postprocessing/hw_postprocessshader.cpp @@ -1094,6 +1085,7 @@ set (PCH_SOURCES hwrenderer/utility/hw_cvars.cpp hwrenderer/utility/hw_lighting.cpp hwrenderer/utility/hw_shaderpatcher.cpp + hwrenderer/utility/hw_vrmodes.cpp menu/joystickmenu.cpp menu/loadsavemenu.cpp @@ -1424,7 +1416,6 @@ source_group("Hardware Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SO source_group("Hardware Renderer\\Postprocessing" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/postprocessing/.+") source_group("Hardware Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/renderer/.+") source_group("Hardware Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/scene/.+") -source_group("Hardware Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/stereo3d/.+") source_group("Hardware Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/shaders/.+") source_group("Hardware Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/system/.+") source_group("Hardware Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/hwrenderer/textures/.+") @@ -1436,7 +1427,6 @@ source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURR source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+") source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+") source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+") -source_group("OpenGL Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/stereo3d/.+") source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/shaders/.+") source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+") source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+") diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index e371699d7..1789876d7 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -47,8 +47,8 @@ #include "hwrenderer/postprocessing/hw_lensshader.h" #include "hwrenderer/postprocessing/hw_fxaashader.h" #include "hwrenderer/postprocessing/hw_presentshader.h" +#include "hwrenderer/utility/hw_vrmodes.h" #include "gl/shaders/gl_postprocessshaderinstance.h" -#include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_hwtexture.h" #include "r_videoscale.h" @@ -654,18 +654,18 @@ void FGLRenderer::ApplyFXAA() void FGLRenderer::Flush() { - const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode(); + auto vrmode = VRMode::GetVRMode(true); const auto &mSceneViewport = screen->mSceneViewport; const auto &mScreenViewport = screen->mScreenViewport; - if (stereo3dMode.IsMono()) + if (vrmode->mEyeCount == 1) { CopyToBackbuffer(nullptr, true); } else { // Render 2D to eye textures - for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) + for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix) { FGLDebug::PushGroup("Eye2D"); mBuffers->BindEyeFB(eye_ix); @@ -678,7 +678,9 @@ void FGLRenderer::Flush() FGLPostProcessState savedState; FGLDebug::PushGroup("PresentEyes"); - stereo3dMode.Present(); + // Note: This here is the ONLY place in the entire engine where the OpenGL dependent parts of the Stereo3D code need to be dealt with. + // There's absolutely no need to create a overly complex class hierarchy for just this. + GLRenderer->PresentStereo(); FGLDebug::PopGroup(); } } diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index b70c7ed61..25e423489 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -61,7 +61,6 @@ #include "hwrenderer/postprocessing/hw_present3dRowshader.h" #include "hwrenderer/postprocessing/hw_shadowmapshader.h" #include "gl/shaders/gl_postprocessshaderinstance.h" -#include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_samplers.h" #include "gl/dynlights/gl_lightbuffer.h" #include "r_videoscale.h" diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index c89d85705..0d2bd43b1 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -127,6 +127,7 @@ public: void SetupLevel(); void ResetSWScene(); + void PresentStereo(); void RenderScreenQuad(); void PostProcessScene(int fixedcm, const std::function &afterBloomDrawEndScene2D); void AmbientOccludeScene(float m5); diff --git a/src/gl/renderer/gl_stereo3d.cpp b/src/gl/renderer/gl_stereo3d.cpp new file mode 100644 index 000000000..6035a67bf --- /dev/null +++ b/src/gl/renderer/gl_stereo3d.cpp @@ -0,0 +1,364 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_stereo3d.cpp +** Stereoscopic 3D API +** +*/ + +#include "gl_load/gl_system.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderbuffers.h" +#include "hwrenderer/utility/hw_vrmodes.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/renderer/gl_postprocessstate.h" +#include "hwrenderer/postprocessing/hw_presentshader.h" +#include "hwrenderer/postprocessing/hw_present3dRowshader.h" + +EXTERN_CVAR(Int, vr_mode) +EXTERN_CVAR(Float, vid_saturation) +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Int, gl_satformula) + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentAnaglyph(bool r, bool g, bool b) +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + glColorMask(r, g, b, 1); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); + + glColorMask(!r, !g, !b, 1); + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); + + glColorMask(1, 1, 1, 1); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentSideBySide() +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + // Compute screen regions to use for left and right eye views + int leftWidth = screen->mOutputLetterbox.width / 2; + int rightWidth = screen->mOutputLetterbox.width - leftWidth; + IntRect leftHalfScreen = screen->mOutputLetterbox; + leftHalfScreen.width = leftWidth; + IntRect rightHalfScreen = screen->mOutputLetterbox; + rightHalfScreen.width = rightWidth; + rightHalfScreen.left += leftWidth; + + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(leftHalfScreen, true); + + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(rightHalfScreen, true); +} + + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentTopBottom() +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + // Compute screen regions to use for left and right eye views + int topHeight = screen->mOutputLetterbox.height / 2; + int bottomHeight = screen->mOutputLetterbox.height - topHeight; + IntRect topHalfScreen = screen->mOutputLetterbox; + topHalfScreen.height = topHeight; + topHalfScreen.top = topHeight; + IntRect bottomHalfScreen = screen->mOutputLetterbox; + bottomHalfScreen.height = bottomHeight; + bottomHalfScreen.top = 0; + + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(topHalfScreen, true); + + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(bottomHalfScreen, true); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void prepareInterleavedPresent(FPresentShaderBase& shader) +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + + // Bind each eye texture, for composition in the shader + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->mBuffers->BindEyeTexture(1, 1); + + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + const IntRect& box = screen->mOutputLetterbox; + glViewport(box.left, box.top, box.width, box.height); + + shader.Bind(NOQUEUE); + + if (GLRenderer->framebuffer->IsHWGammaActive()) + { + shader.Uniforms->InvGamma = 1.0f; + shader.Uniforms->Contrast = 1.0f; + shader.Uniforms->Brightness = 0.0f; + shader.Uniforms->Saturation = 1.0f; + } + else + { + shader.Uniforms->InvGamma = 1.0f / clamp(Gamma, 0.1f, 4.f); + shader.Uniforms->Contrast = clamp(vid_contrast, 0.1f, 3.f); + shader.Uniforms->Brightness = clamp(vid_brightness, -0.8f, 0.8f); + shader.Uniforms->Saturation = clamp(vid_saturation, -15.0f, 15.0f); + shader.Uniforms->GrayFormula = static_cast(gl_satformula); + } + shader.Uniforms->Scale = { + screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(), + screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight() + }; + shader.Uniforms.Set(); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentColumnInterleaved() +{ + FGLPostProcessState savedState; + savedState.SaveTextureBindings(2); + prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader); + + // Compute absolute offset from top of screen to top of current display window + // because we need screen-relative, not window-relative, scan line parity + + // Todo: + //auto clientoffset = screen->GetClientOffset(); + //auto windowHOffset = clientoffset.X % 2; + int windowHOffset = 0; + + GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset; + GLRenderer->mPresent3dColumnShader->Uniforms.Set(); + + GLRenderer->RenderScreenQuad(); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentRowInterleaved() +{ + FGLPostProcessState savedState; + savedState.SaveTextureBindings(2); + prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader); + + // Todo: + //auto clientoffset = screen->GetClientOffset(); + //auto windowVOffset = clientoffset.Y % 2; + int windowVOffset = 0; + + GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity = + (windowVOffset + + screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom + ) % 2; + + GLRenderer->mPresent3dRowShader->Uniforms.Set(); + GLRenderer->RenderScreenQuad(); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentCheckerInterleaved() +{ + FGLPostProcessState savedState; + savedState.SaveTextureBindings(2); + prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader); + + // Compute absolute offset from top of screen to top of current display window + // because we need screen-relative, not window-relative, scan line parity + + //auto clientoffset = screen->GetClientOffset(); + //auto windowHOffset = clientoffset.X % 2; + //auto windowVOffset = clientoffset.Y % 2; + int windowHOffset = 0; + int windowVOffset = 0; + + GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity = + (windowVOffset + + windowHOffset + + screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom + ) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset + + GLRenderer->mPresent3dCheckerShader->Uniforms.Set(); + GLRenderer->RenderScreenQuad(); +} + +//========================================================================== +// +// Sometimes the stereo render context is not ready immediately at start up +// +//========================================================================== + +bool QuadStereoCheckInitialRenderContextState() +{ + // Keep trying until we see at least one good OpenGL context to render to + static bool bQuadStereoSupported = false; + static bool bDecentContextWasFound = false; + static int contextCheckCount = 0; + if ((!bDecentContextWasFound) && (contextCheckCount < 200)) + { + contextCheckCount += 1; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context + GLboolean supportsStereo, supportsBuffered; + glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered); + if (supportsBuffered) // Finally, a useful OpenGL context + { + // This block will be executed exactly ONCE during a game run + bDecentContextWasFound = true; // now we can stop checking every frame... + // Now check whether this context supports hardware stereo + glGetBooleanv(GL_STEREO, &supportsStereo); + bQuadStereoSupported = supportsStereo && supportsBuffered; + } + } + return bQuadStereoSupported; +} + +//========================================================================== +// +// +// +//========================================================================== + +static void PresentQuadStereo() +{ + if (QuadStereoCheckInitialRenderContextState()) + { + GLRenderer->mBuffers->BindOutputFB(); + + glDrawBuffer(GL_BACK_LEFT); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); + + glDrawBuffer(GL_BACK_RIGHT); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); + + glDrawBuffer(GL_BACK); + } + else + { + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); + } +} + + +void FGLRenderer::PresentStereo() +{ + switch (vr_mode) + { + default: + return; + + case VR_GREENMAGENTA: + PresentAnaglyph(false, true, true); + break; + + case VR_REDCYAN: + PresentAnaglyph(true, false, false); + break; + + case VR_AMBERBLUE: + PresentAnaglyph(true, true, false); + break; + + case VR_SIDEBYSIDEFULL: + case VR_SIDEBYSIDESQUISHED: + PresentSideBySide(); + break; + + case VR_TOPBOTTOM: + PresentTopBottom(); + break; + + case VR_ROWINTERLEAVED: + PresentRowInterleaved(); + break; + + case VR_COLUMNINTERLEAVED: + PresentColumnInterleaved(); + break; + + case VR_CHECKERINTERLEAVED: + PresentCheckerInterleaved(); + break; + + case VR_QUADSTEREO: + PresentQuadStereo(); + break; + } +} + diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index f143c6795..fbc5aa60c 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -40,7 +40,6 @@ #include "hwrenderer/scene/hw_clipper.h" #include "gl/scene/gl_portal.h" #include "gl/renderer/gl_renderstate.h" -#include "gl/stereo3d/scoped_color_mask.h" #include "gl/renderer/gl_quaddrawer.h" #include "gl/dynlights/gl_lightbuffer.h" @@ -253,26 +252,25 @@ void FDrawInfo::SetupFloodStencil(wallseg * ws) // Create stencil glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels - { // Use revertible color mask, to avoid stomping on anaglyph 3D state - ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // don't write to the graphics buffer - gl_RenderState.EnableTexture(false); - gl_RenderState.ResetColor(); - glEnable(GL_DEPTH_TEST); - glDepthMask(true); + glColorMask(0, 0, 0, 0); // don't write to the graphics buffer + gl_RenderState.EnableTexture(false); + gl_RenderState.ResetColor(); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); - gl_RenderState.Apply(); - FQuadDrawer qd; - qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0); - qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0); - qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0); - qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0); - qd.Render(GL_TRIANGLE_FAN); + gl_RenderState.Apply(); + FQuadDrawer qd; + qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0); + qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0); + qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0); + qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0); + qd.Render(GL_TRIANGLE_FAN); - glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil + glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil - } // glColorMask(1, 1, 1, 1); // don't write to the graphics buffer + glColorMask(1, 1, 1, 1); // don't write to the graphics buffer gl_RenderState.EnableTexture(true); glDisable(GL_DEPTH_TEST); glDepthMask(false); @@ -282,26 +280,25 @@ void FDrawInfo::ClearFloodStencil(wallseg * ws) { int recursion = GLRenderer->mPortalState.GetRecursion(); - glStencilOp(GL_KEEP,GL_KEEP,GL_DECR); + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); gl_RenderState.EnableTexture(false); - { - // Use revertible color mask, to avoid stomping on anaglyph 3D state - ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer - gl_RenderState.ResetColor(); + // Use revertible color mask, to avoid stomping on anaglyph 3D state + glColorMask(0, 0, 0, 0); // don't write to the graphics buffer + gl_RenderState.ResetColor(); - gl_RenderState.Apply(); - FQuadDrawer qd; - qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0); - qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0); - qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0); - qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0); - qd.Render(GL_TRIANGLE_FAN); + gl_RenderState.Apply(); + FQuadDrawer qd; + qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0); + qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0); + qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0); + qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0); + qd.Render(GL_TRIANGLE_FAN); - // restore old stencil op. - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_EQUAL, recursion, ~0); - gl_RenderState.EnableTexture(true); - } // glColorMask(1, 1, 1, 1); + // restore old stencil op. + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, recursion, ~0); + gl_RenderState.EnableTexture(true); + glColorMask(1, 1, 1, 1); glEnable(GL_DEPTH_TEST); glDepthMask(true); } diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 2aa853d12..9d1a85ae7 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -42,7 +42,6 @@ #include "gl/data/gl_vertexbuffer.h" #include "hwrenderer/scene/hw_clipper.h" #include "gl/scene/gl_portal.h" -#include "gl/stereo3d/scoped_color_mask.h" //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -135,7 +134,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw glStencilFunc(GL_EQUAL, mState->recursion, ~0); // create stencil glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels { - ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer + glColorMask(0,0,0,0); // don't write to the graphics buffer gl_RenderState.SetEffect(EFF_STENCIL); gl_RenderState.EnableTexture(false); gl_RenderState.ResetColor(); @@ -166,7 +165,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw // set normal drawing mode gl_RenderState.EnableTexture(true); glDepthFunc(GL_LESS); - // glColorMask(1, 1, 1, 1); + glColorMask(1, 1, 1, 1); gl_RenderState.SetEffect(EFF_NONE); glDepthRange(0, 1); @@ -194,7 +193,7 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil gl_RenderState.EnableTexture(true); - // glColorMask(1,1,1,1); + glColorMask(1,1,1,1); gl_RenderState.SetEffect(EFF_NONE); glDisable(GL_DEPTH_TEST); glDepthMask(false); // don't write to Z-buffer! @@ -242,46 +241,45 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil) // Restore the old view if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; + glColorMask(0, 0, 0, 0); // no graphics + gl_RenderState.SetEffect(EFF_NONE); + gl_RenderState.ResetColor(); + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(); + + if (needdepth) { - ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // no graphics - gl_RenderState.SetEffect(EFF_NONE); - gl_RenderState.ResetColor(); - gl_RenderState.EnableTexture(false); - gl_RenderState.Apply(); - - if (needdepth) - { - // first step: reset the depth buffer to max. depth - glDepthRange(1, 1); // always - glDepthFunc(GL_ALWAYS); // write the farthest depth value - DrawPortalStencil(); - } - else - { - glEnable(GL_DEPTH_TEST); - } - - // second step: restore the depth buffer to the previous values and reset the stencil - glDepthFunc(GL_LEQUAL); - glDepthRange(0, 1); - glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); - glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil + // first step: reset the depth buffer to max. depth + glDepthRange(1, 1); // always + glDepthFunc(GL_ALWAYS); // write the farthest depth value DrawPortalStencil(); - glDepthFunc(GL_LESS); + } + else + { + glEnable(GL_DEPTH_TEST); + } + + // second step: restore the depth buffer to the previous values and reset the stencil + glDepthFunc(GL_LEQUAL); + glDepthRange(0, 1); + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil + DrawPortalStencil(); + glDepthFunc(GL_LESS); - gl_RenderState.EnableTexture(true); - gl_RenderState.SetEffect(EFF_NONE); - } // glColorMask(1, 1, 1, 1); + gl_RenderState.EnableTexture(true); + gl_RenderState.SetEffect(EFF_NONE); + glColorMask(1, 1, 1, 1); mState->recursion--; // restore old stencil op. - glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); - glStencilFunc(GL_EQUAL, mState->recursion,~0); // draw sky into stencil + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil } else { - if (needdepth) + if (needdepth) { glClear(GL_DEPTH_BUFFER_BIT); } @@ -302,17 +300,17 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil) gl_RenderState.ResetColor(); glDepthFunc(GL_LEQUAL); glDepthRange(0, 1); - { - ScopedColorMask colorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color - gl_RenderState.SetEffect(EFF_STENCIL); - gl_RenderState.EnableTexture(false); - gl_RenderState.BlendFunc(GL_ONE, GL_ZERO); - gl_RenderState.BlendEquation(GL_FUNC_ADD); - gl_RenderState.Apply(); - DrawPortalStencil(); - gl_RenderState.SetEffect(EFF_NONE); - gl_RenderState.EnableTexture(true); - } + glColorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color + gl_RenderState.SetEffect(EFF_STENCIL); + gl_RenderState.EnableTexture(false); + gl_RenderState.BlendFunc(GL_ONE, GL_ZERO); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl_RenderState.Apply(); + DrawPortalStencil(); + gl_RenderState.SetEffect(EFF_NONE); + gl_RenderState.EnableTexture(true); + glColorMask(1, 1, 1, 1); // mark portal in alpha channel but don't touch color + glDepthFunc(GL_LESS); } } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 2e1b004aa..942e646ae 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -53,8 +53,7 @@ #include "hwrenderer/scene/hw_portal.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" -#include "gl/stereo3d/gl_stereo3d.h" -#include "hwrenderer/utility/scoped_view_shifter.h" +#include "hwrenderer/utility/hw_vrmodes.h" //========================================================================== // @@ -380,10 +379,10 @@ void FDrawInfo::EndDrawScene(sector_t * viewsector) void FDrawInfo::DrawEndScene2D(sector_t * viewsector) { const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); + auto vrmode = VRMode::GetVRMode(true); - // This should be removed once all 2D stuff is really done through the 2D interface. VPUniforms.mViewMatrix.loadIdentity(); - VPUniforms.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f); + VPUniforms.mProjectionMatrix = vrmode->GetHUDSpriteProjection(); ApplyVPUniforms(); glDisable(GL_DEPTH_TEST); glDisable(GL_MULTISAMPLE); @@ -465,27 +464,24 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came R_SetupFrame (mainvp, r_viewwindow, camera); // Render (potentially) multiple views for stereo 3d - float viewShift[3]; - const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode(); - stereo3dMode.SetUp(); - for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) + // Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode. + auto vrmode = VRMode::GetVRMode(mainview && toscreen); + for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix) { - const auto eye = stereo3dMode.getEyePose(eye_ix); - eye->SetUp(); + const auto &eye = vrmode->mEyes[eye_ix]; screen->SetViewportRects(bounds); Set3DViewport(mainview); FDrawInfo *di = FDrawInfo::StartDrawInfo(mainvp, nullptr); - auto vp = di->Viewpoint; + auto &vp = di->Viewpoint; di->SetViewArea(); auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr); di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) // Stereo mode specific perspective projection - di->VPUniforms.mProjectionMatrix = eye->GetProjection(fov, ratio, fovratio); - // Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos - eye->GetViewShift(vp.HWAngles.Yaw.Degrees, viewShift); - ScopedViewShifter viewShifter(vp.Pos, viewShift); + di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio); + // Stereo mode specific viewpoint adjustment + vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees); di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); @@ -497,7 +493,7 @@ sector_t * FGLRenderer::RenderViewpoint (FRenderViewpoint &mainvp, AActor * came PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector); }); } di->EndDrawInfo(); - if (!stereo3dMode.IsMono()) + if (vrmode->mEyeCount > 1) mBuffers->BlitToEyeTexture(eye_ix); } diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index fdc10ef19..fe5119639 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -38,7 +38,6 @@ #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" #include "gl/models/gl_models.h" -#include "gl/stereo3d/gl_stereo3d.h" #include "gl/dynlights/gl_lightbuffer.h" //========================================================================== @@ -91,8 +90,6 @@ void FDrawInfo::DrawPSprite (HUDSprite *huds) void FDrawInfo::DrawPlayerSprites(bool hudModelStep) { - s3d::Stereo3DMode::getCurrentMode().AdjustPlayerSprites(this); - int oldlightmode = level.lightmode; if (!hudModelStep && level.lightmode == 8) level.lightmode = 2; // Software lighting cannot handle 2D content so revert to lightmode 2 for that. for(auto &hudsprite : hudsprites) diff --git a/src/gl/stereo3d/gl_anaglyph.cpp b/src/gl/stereo3d/gl_anaglyph.cpp deleted file mode 100644 index 791c49982..000000000 --- a/src/gl/stereo3d/gl_anaglyph.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_anaglyph.cpp -** Color mask based stereoscopic 3D modes for GZDoom -** -*/ - -#include "gl_anaglyph.h" -#include "gl/renderer/gl_renderbuffers.h" - -namespace s3d { - -MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters) - : leftEye(leftColorMask, ipdMeters), rightEye(leftColorMask.inverse(), ipdMeters) -{ - eye_ptrs.Push(&leftEye); - eye_ptrs.Push(&rightEye); -} - -void MaskAnaglyph::Present() const -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - - gl_RenderState.SetColorMask(leftEye.GetColorMask().r, leftEye.GetColorMask().g, leftEye.GetColorMask().b, true); - gl_RenderState.ApplyColorMask(); - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); - - gl_RenderState.SetColorMask(rightEye.GetColorMask().r, rightEye.GetColorMask().g, rightEye.GetColorMask().b, true); - gl_RenderState.ApplyColorMask(); - GLRenderer->mBuffers->BindEyeTexture(1, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); - - gl_RenderState.ResetColorMask(); - gl_RenderState.ApplyColorMask(); -} - - -/* static */ -const GreenMagenta& GreenMagenta::getInstance(float ipd) -{ - static GreenMagenta instance(ipd); - return instance; -} - - -/* static */ -const RedCyan& RedCyan::getInstance(float ipd) -{ - static RedCyan instance(ipd); - return instance; -} - - -/* static */ -const AmberBlue& AmberBlue::getInstance(float ipd) -{ - static AmberBlue instance(ipd); - return instance; -} - - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_anaglyph.h b/src/gl/stereo3d/gl_anaglyph.h deleted file mode 100644 index 110493de9..000000000 --- a/src/gl/stereo3d/gl_anaglyph.h +++ /dev/null @@ -1,113 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_anaglyph.h -** Color mask based stereoscopic 3D modes for GZDoom -** -*/ - -#ifndef GL_ANAGLYPH_H_ -#define GL_ANAGLYPH_H_ - -#include "gl_stereo3d.h" -#include "gl_stereo_leftright.h" -#include "gl_load/gl_system.h" -#include "gl/renderer/gl_renderstate.h" - - -namespace s3d { - - -class ColorMask -{ -public: - ColorMask(bool r, bool g, bool b) : r(r), g(g), b(b) {} - ColorMask inverse() const { return ColorMask(!r, !g, !b); } - - bool r; - bool g; - bool b; -}; - - -class AnaglyphLeftPose : public LeftEyePose -{ -public: - AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {} - ColorMask GetColorMask() const { return colorMask; } - -private: - ColorMask colorMask; -}; - -class AnaglyphRightPose : public RightEyePose -{ -public: - AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {} - ColorMask GetColorMask() const { return colorMask; } - -private: - ColorMask colorMask; -}; - -class MaskAnaglyph : public Stereo3DMode -{ -public: - MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters); - void Present() const override; -private: - AnaglyphLeftPose leftEye; - AnaglyphRightPose rightEye; -}; - - -class RedCyan : public MaskAnaglyph -{ -public: - static const RedCyan& getInstance(float ipd); - - RedCyan(float ipd) : MaskAnaglyph(ColorMask(true, false, false), ipd) {} -}; - -class GreenMagenta : public MaskAnaglyph -{ -public: - static const GreenMagenta& getInstance(float ipd); - - GreenMagenta(float ipd) : MaskAnaglyph(ColorMask(false, true, false), ipd) {} -}; - -class AmberBlue : public MaskAnaglyph -{ -public: - static const AmberBlue& getInstance(float ipd); - - AmberBlue(float ipd) : MaskAnaglyph(ColorMask(true, true, false), ipd) {} -}; - -// TODO matrix anaglyph - - -} /* namespace s3d */ - - -#endif /* GL_ANAGLYPH_H_ */ diff --git a/src/gl/stereo3d/gl_interleaved3d.cpp b/src/gl/stereo3d/gl_interleaved3d.cpp deleted file mode 100644 index 3ab0b309d..000000000 --- a/src/gl/stereo3d/gl_interleaved3d.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* -** gl_interleaved3d.cpp -** Interleaved image stereoscopic 3D modes for GZDoom -** -**--------------------------------------------------------------------------- -** Copyright 2016 Christopher Bruns -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#include "gl_interleaved3d.h" -#include "gl/renderer/gl_renderbuffers.h" -#include "gl/renderer/gl_postprocessstate.h" -#include "gl/system/gl_framebuffer.h" -#include "hwrenderer/postprocessing/hw_present3dRowshader.h" - -#ifdef _WIN32 -#include "hardware.h" -#endif // _WIN32 - -EXTERN_CVAR(Float, vid_saturation) -EXTERN_CVAR(Float, vid_brightness) -EXTERN_CVAR(Float, vid_contrast) -EXTERN_CVAR(Int, gl_satformula) -EXTERN_CVAR(Bool, fullscreen) -EXTERN_CVAR(Int, win_x) // screen pixel position of left of display window -EXTERN_CVAR(Int, win_y) // screen pixel position of top of display window - -namespace s3d { - -/* static */ -const CheckerInterleaved3D& CheckerInterleaved3D::getInstance(float ipd) -{ - static CheckerInterleaved3D instance(ipd); - return instance; -} - -/* static */ -const ColumnInterleaved3D& ColumnInterleaved3D::getInstance(float ipd) -{ - static ColumnInterleaved3D instance(ipd); - return instance; -} - -/* static */ -const RowInterleaved3D& RowInterleaved3D::getInstance(float ipd) -{ - static RowInterleaved3D instance(ipd); - return instance; -} - -static void prepareInterleavedPresent(FPresentShaderBase& shader) -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - - - // Bind each eye texture, for composition in the shader - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->mBuffers->BindEyeTexture(1, 1); - - glActiveTexture(GL_TEXTURE0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glActiveTexture(GL_TEXTURE1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - const IntRect& box = screen->mOutputLetterbox; - glViewport(box.left, box.top, box.width, box.height); - - shader.Bind(NOQUEUE); - - if ( GLRenderer->framebuffer->IsHWGammaActive() ) - { - shader.Uniforms->InvGamma = 1.0f; - shader.Uniforms->Contrast = 1.0f; - shader.Uniforms->Brightness = 0.0f; - shader.Uniforms->Saturation = 1.0f; - } - else - { - shader.Uniforms->InvGamma = 1.0f / clamp(Gamma, 0.1f, 4.f); - shader.Uniforms->Contrast = clamp(vid_contrast, 0.1f, 3.f); - shader.Uniforms->Brightness = clamp(vid_brightness, -0.8f, 0.8f); - shader.Uniforms->Saturation = clamp(vid_saturation, -15.0f, 15.0f); - shader.Uniforms->GrayFormula = static_cast(gl_satformula); - } - shader.Uniforms->Scale = { - screen->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(), - screen->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight() - }; - shader.Uniforms.Set(); -} - -// fixme: I don't know how to get absolute window position on Mac and Linux -// fixme: I don't know how to get window border decoration size anywhere -// So for now I'll hard code the border effect on my test machine. -// Workaround for others is to fuss with vr_swap_eyes CVAR until it looks right. -// Presumably the top/left window border on my test machine has an odd number of pixels -// in the horizontal direction, and an even number in the vertical direction. -#define WINDOW_BORDER_HORIZONTAL_PARITY 1 -#define WINDOW_BORDER_VERTICAL_PARITY 0 - -void CheckerInterleaved3D::Present() const -{ - FGLPostProcessState savedState; - savedState.SaveTextureBindings(2); - prepareInterleavedPresent(*GLRenderer->mPresent3dCheckerShader); - - // Compute absolute offset from top of screen to top of current display window - // because we need screen-relative, not window-relative, scan line parity - int windowVOffset = 0; - int windowHOffset = 0; - -#ifdef _WIN32 - /* this needs to be done differently! - if (!fullscreen) { - I_SaveWindowedPos(); // update win_y CVAR - windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2; - windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2; - } - */ -#endif // _WIN32 - - GLRenderer->mPresent3dCheckerShader->Uniforms->WindowPositionParity = - (windowVOffset - + windowHOffset - + screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom - ) % 2; // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset - - GLRenderer->mPresent3dCheckerShader->Uniforms.Set(); - GLRenderer->RenderScreenQuad(); -} - -void s3d::CheckerInterleaved3D::AdjustViewports() const -{ - // decrease the total pixel count by 2, but keep the same aspect ratio - const float sqrt2 = 1.41421356237f; - // Change size of renderbuffer, and align to screen - screen->mSceneViewport.height /= sqrt2; - screen->mSceneViewport.top /= sqrt2; - screen->mSceneViewport.width /= sqrt2; - screen->mSceneViewport.left /= sqrt2; - - screen->mScreenViewport.height /= sqrt2; - screen->mScreenViewport.top /= sqrt2; - screen->mScreenViewport.width /= sqrt2; - screen->mScreenViewport.left /= sqrt2; -} - -void ColumnInterleaved3D::Present() const -{ - FGLPostProcessState savedState; - savedState.SaveTextureBindings(2); - prepareInterleavedPresent(*GLRenderer->mPresent3dColumnShader); - - // Compute absolute offset from top of screen to top of current display window - // because we need screen-relative, not window-relative, scan line parity - int windowHOffset = 0; - -#ifdef _WIN32 - /* this needs to be done differently! - if (!fullscreen) { - I_SaveWindowedPos(); // update win_y CVAR - windowHOffset = (win_x + WINDOW_BORDER_HORIZONTAL_PARITY) % 2; - } - */ -#endif // _WIN32 - - GLRenderer->mPresent3dColumnShader->Uniforms->WindowPositionParity = windowHOffset; - GLRenderer->mPresent3dColumnShader->Uniforms.Set(); - - GLRenderer->RenderScreenQuad(); -} - -void RowInterleaved3D::Present() const -{ - FGLPostProcessState savedState; - savedState.SaveTextureBindings(2); - prepareInterleavedPresent(*GLRenderer->mPresent3dRowShader); - - // Compute absolute offset from top of screen to top of current display window - // because we need screen-relative, not window-relative, scan line parity - int windowVOffset = 0; - -#ifdef _WIN32 - /* this needs to be done differently! - if (! fullscreen) { - I_SaveWindowedPos(); // update win_y CVAR - windowVOffset = (win_y + WINDOW_BORDER_VERTICAL_PARITY) % 2; - } - */ -#endif // _WIN32 - - GLRenderer->mPresent3dRowShader->Uniforms->WindowPositionParity = - (windowVOffset - + screen->mOutputLetterbox.height + 1 // +1 because of origin at bottom - ) % 2; - - GLRenderer->mPresent3dRowShader->Uniforms.Set(); - GLRenderer->RenderScreenQuad(); -} - - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_interleaved3d.h b/src/gl/stereo3d/gl_interleaved3d.h deleted file mode 100644 index 68125cccd..000000000 --- a/src/gl/stereo3d/gl_interleaved3d.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -** gl_interleaved3d.h -** Interleaved stereoscopic 3D modes for GZDoom -** -**--------------------------------------------------------------------------- -** Copyright 2016 Christopher Bruns -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#ifndef GL_INTERLEAVED3D_H_ -#define GL_INTERLEAVED3D_H_ - -#include "gl_stereo3d.h" -#include "gl_stereo_leftright.h" -#include "gl_sidebyside3d.h" -#include "gl_load/gl_system.h" -#include "gl/renderer/gl_renderstate.h" - -namespace s3d { - -class CheckerInterleaved3D : public SideBySideSquished -{ -public: - static const CheckerInterleaved3D& getInstance(float ipd); - CheckerInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {} - void Present() const override; - void AdjustViewports() const override; -}; - -class ColumnInterleaved3D : public SideBySideSquished -{ -public: - static const ColumnInterleaved3D& getInstance(float ipd); - ColumnInterleaved3D(double ipdMeters) : SideBySideSquished(ipdMeters) {} - void Present() const override; -}; - -class RowInterleaved3D : public TopBottom3D -{ -public: - static const RowInterleaved3D& getInstance(float ipd); - RowInterleaved3D(double ipdMeters) : TopBottom3D(ipdMeters) {} - void Present() const override; -}; - -} /* namespace s3d */ - - -#endif /* GL_INTERLEAVED3D_H_ */ diff --git a/src/gl/stereo3d/gl_quadstereo.cpp b/src/gl/stereo3d/gl_quadstereo.cpp deleted file mode 100644 index dce114caf..000000000 --- a/src/gl/stereo3d/gl_quadstereo.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_quadstereo.cpp -** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom -** -*/ - -#include "gl_quadstereo.h" -#include "gl/renderer/gl_renderbuffers.h" - -namespace s3d { - -QuadStereo::QuadStereo(double ipdMeters) - : leftEye(ipdMeters), rightEye(ipdMeters) -{ - // Check whether quad-buffered stereo is supported in the current context - // We are assuming the OpenGL context is already current at this point, - // i.e. this constructor is called "just in time". - - // First initialize to mono-ish initial state - bQuadStereoSupported = leftEye.bQuadStereoSupported = rightEye.bQuadStereoSupported = false; - eye_ptrs.Push(&leftEye); // We ALWAYS want to show at least this one view... - // We will possibly advance to true stereo mode in the Setup() method... -} - -// Sometimes the stereo render context is not ready immediately at start up -/* private */ -void QuadStereo::checkInitialRenderContextState() -{ - // Keep trying until we see at least one good OpenGL context to render to - static bool bDecentContextWasFound = false; - static int contextCheckCount = 0; - if ( (! bDecentContextWasFound) && (contextCheckCount < 200) ) - { - contextCheckCount += 1; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // This question is about the main screen display context - GLboolean supportsStereo, supportsBuffered; - glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered); - if (supportsBuffered) // Finally, a useful OpenGL context - { - // This block will be executed exactly ONCE during a game run - bDecentContextWasFound = true; // now we can stop checking every frame... - // Now check whether this context supports hardware stereo - glGetBooleanv(GL_STEREO, &supportsStereo); - bQuadStereoSupported = supportsStereo && supportsBuffered; - leftEye.bQuadStereoSupported = bQuadStereoSupported; - rightEye.bQuadStereoSupported = bQuadStereoSupported; - if (bQuadStereoSupported) - eye_ptrs.Push(&rightEye); // Use the other eye too, if we can do stereo - } - } -} - -void QuadStereo::Present() const -{ - if (bQuadStereoSupported) - { - GLRenderer->mBuffers->BindOutputFB(); - - glDrawBuffer(GL_BACK_LEFT); - GLRenderer->ClearBorders(); - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); - - glDrawBuffer(GL_BACK_RIGHT); - GLRenderer->ClearBorders(); - GLRenderer->mBuffers->BindEyeTexture(1, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); - - glDrawBuffer(GL_BACK); - } - else - { - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); - } -} - -void QuadStereo::SetUp() const -{ - Stereo3DMode::SetUp(); - // Maybe advance to true stereo mode (ONCE), after the stereo context is finally ready - const_cast(this)->checkInitialRenderContextState(); -} - -/* static */ -const QuadStereo& QuadStereo::getInstance(float ipd) -{ - static QuadStereo instance(ipd); - return instance; -} - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_quadstereo.h b/src/gl/stereo3d/gl_quadstereo.h deleted file mode 100644 index 014a91991..000000000 --- a/src/gl/stereo3d/gl_quadstereo.h +++ /dev/null @@ -1,77 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_quadstereo.h -** Quad-buffered OpenGL stereoscopic 3D mode for GZDoom -** -*/ - -#ifndef GL_QUADSTEREO_H_ -#define GL_QUADSTEREO_H_ - -#include "gl_stereo3d.h" -#include "gl_stereo_leftright.h" -#include "gl_load/gl_system.h" - -namespace s3d { - - -class QuadStereoLeftPose : public LeftEyePose -{ -public: - QuadStereoLeftPose(float ipd) : LeftEyePose(ipd), bQuadStereoSupported(false) {} - bool bQuadStereoSupported; -}; - -class QuadStereoRightPose : public RightEyePose -{ -public: - QuadStereoRightPose(float ipd) : RightEyePose(ipd), bQuadStereoSupported(false){} - bool bQuadStereoSupported; -}; - -// To use Quad-buffered stereo mode with nvidia 3d vision glasses, -// you must either: -// A) be using a Quadro series video card, OR -// -// B) be using nvidia driver version 314.07 or later -// AND have your monitor set to 120 Hz refresh rate -// AND have gzdoom in true full screen mode -class QuadStereo : public Stereo3DMode -{ -public: - QuadStereo(double ipdMeters); - void Present() const override; - void SetUp() const override; - static const QuadStereo& getInstance(float ipd); -private: - QuadStereoLeftPose leftEye; - QuadStereoRightPose rightEye; - bool bQuadStereoSupported; - void checkInitialRenderContextState(); -}; - - -} /* namespace s3d */ - - -#endif /* GL_QUADSTEREO_H_ */ diff --git a/src/gl/stereo3d/gl_sidebyside3d.cpp b/src/gl/stereo3d/gl_sidebyside3d.cpp deleted file mode 100644 index 12d7cf41f..000000000 --- a/src/gl/stereo3d/gl_sidebyside3d.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* -** gl_sidebyside3d.cpp -** Mosaic image stereoscopic 3D modes for GZDoom -** -**--------------------------------------------------------------------------- -** Copyright 2016 Christopher Bruns -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#include "gl/scene/gl_drawinfo.h" -#include "gl_sidebyside3d.h" -#include "gl/renderer/gl_renderbuffers.h" - -namespace s3d { - -void SideBySideBase::Present() const -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - - // Compute screen regions to use for left and right eye views - int leftWidth = screen->mOutputLetterbox.width / 2; - int rightWidth = screen->mOutputLetterbox.width - leftWidth; - IntRect leftHalfScreen = screen->mOutputLetterbox; - leftHalfScreen.width = leftWidth; - IntRect rightHalfScreen = screen->mOutputLetterbox; - rightHalfScreen.width = rightWidth; - rightHalfScreen.left += leftWidth; - - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(leftHalfScreen, true); - - GLRenderer->mBuffers->BindEyeTexture(1, 0); - GLRenderer->DrawPresentTexture(rightHalfScreen, true); -} - -// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...) -void SideBySideBase::AdjustViewports() const -{ - // Change size of renderbuffer, and align to screen - screen->mSceneViewport.width /= 2; - screen->mSceneViewport.left /= 2; - screen->mScreenViewport.width /= 2; - screen->mScreenViewport.left /= 2; -} - -/* static */ -const SideBySideSquished& SideBySideSquished::getInstance(float ipd) -{ - static SideBySideSquished instance(ipd); - return instance; -} - -SideBySideSquished::SideBySideSquished(double ipdMeters) - : leftEye(ipdMeters), rightEye(ipdMeters) -{ - eye_ptrs.Push(&leftEye); - eye_ptrs.Push(&rightEye); -} - -/* static */ -const SideBySideFull& SideBySideFull::getInstance(float ipd) -{ - static SideBySideFull instance(ipd); - return instance; -} - -SideBySideFull::SideBySideFull(double ipdMeters) - : leftEye(ipdMeters, 0.5f), rightEye(ipdMeters, 0.5f) -{ - eye_ptrs.Push(&leftEye); - eye_ptrs.Push(&rightEye); -} - -/* virtual */ -void SideBySideFull::AdjustPlayerSprites(FDrawInfo *di) const /* override */ -{ - // Show weapon at double width, so it would appear normal width after rescaling - int w = screen->mScreenViewport.width; - int h = screen->mScreenViewport.height; - di->VPUniforms.mProjectionMatrix.ortho(w/2, w + w/2, h, 0, -1.0f, 1.0f); - di->ApplyVPUniforms(); -} - -/* static */ -const TopBottom3D& TopBottom3D::getInstance(float ipd) -{ - static TopBottom3D instance(ipd); - return instance; -} - -void TopBottom3D::Present() const -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - - // Compute screen regions to use for left and right eye views - int topHeight = screen->mOutputLetterbox.height / 2; - int bottomHeight = screen->mOutputLetterbox.height - topHeight; - IntRect topHalfScreen = screen->mOutputLetterbox; - topHalfScreen.height = topHeight; - topHalfScreen.top = topHeight; - IntRect bottomHalfScreen = screen->mOutputLetterbox; - bottomHalfScreen.height = bottomHeight; - bottomHalfScreen.top = 0; - - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(topHalfScreen, true); - - GLRenderer->mBuffers->BindEyeTexture(1, 0); - GLRenderer->DrawPresentTexture(bottomHalfScreen, true); -} - -// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...) -void TopBottom3D::AdjustViewports() const -{ - // Change size of renderbuffer, and align to screen - screen->mSceneViewport.height /= 2; - screen->mSceneViewport.top /= 2; - screen->mScreenViewport.height /= 2; - screen->mScreenViewport.top /= 2; -} - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_sidebyside3d.h b/src/gl/stereo3d/gl_sidebyside3d.h deleted file mode 100644 index ca0a22aa4..000000000 --- a/src/gl/stereo3d/gl_sidebyside3d.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -** gl_sidebyside3d.h -** Mosaic image stereoscopic 3D modes for GZDoom -** -**--------------------------------------------------------------------------- -** Copyright 2016 Christopher Bruns -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#ifndef GL_SIDEBYSIDE3D_H_ -#define GL_SIDEBYSIDE3D_H_ - -#include "gl_stereo3d.h" -#include "gl_stereo_leftright.h" -#include "gl_load/gl_system.h" -#include "gl/renderer/gl_renderstate.h" - - -namespace s3d { - -class SideBySideBase : public Stereo3DMode -{ -public: - void Present() const override; - virtual void AdjustViewports() const override; -}; - -class SideBySideSquished : public SideBySideBase -{ -public: - static const SideBySideSquished& getInstance(float ipd); - SideBySideSquished(double ipdMeters); -private: - LeftEyePose leftEye; - RightEyePose rightEye; -}; - -class SideBySideFull : public SideBySideBase -{ -public: - static const SideBySideFull& getInstance(float ipd); - SideBySideFull(double ipdMeters); - virtual void AdjustPlayerSprites(FDrawInfo *di) const override; -private: - LeftEyePose leftEye; - RightEyePose rightEye; -}; - -class TopBottom3D : public SideBySideSquished -{ -public: - static const TopBottom3D& getInstance(float ipd); - TopBottom3D(double ipdMeters) : SideBySideSquished(ipdMeters) {} - void Present() const override; - virtual void AdjustViewports() const override; -}; - -} /* namespace s3d */ - - -#endif /* GL_SIDEBYSIDE3D_H_ */ diff --git a/src/gl/stereo3d/gl_stereo3d.cpp b/src/gl/stereo3d/gl_stereo3d.cpp deleted file mode 100644 index 609e037db..000000000 --- a/src/gl/stereo3d/gl_stereo3d.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo3d.cpp -** Stereoscopic 3D API -** -*/ - -#include "gl_load/gl_system.h" -#include "gl/stereo3d/gl_stereo3d.h" - -namespace s3d { - - -Stereo3DMode::Stereo3DMode() -{ -} - -Stereo3DMode::~Stereo3DMode() -{ -} - -// Avoid static initialization order fiasco by declaring first Mode type (Mono) here in the -// same source file as Stereo3DMode::getCurrentMode() -// https://isocpp.org/wiki/faq/ctors#static-init-order - -/* static */ -const MonoView& MonoView::getInstance() -{ - static MonoView instance; - return instance; -} - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h deleted file mode 100644 index 8468d5ad9..000000000 --- a/src/gl/stereo3d/gl_stereo3d.h +++ /dev/null @@ -1,90 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo3d.h -** Stereoscopic 3D API -** -*/ - -#ifndef GL_STEREO3D_H_ -#define GL_STEREO3D_H_ - -#include // needed for memcpy on linux, which is needed by VSMatrix copy ctor -#include "tarray.h" -#include "r_data/matrix.h" -#include "gl/renderer/gl_renderer.h" -#include "hwrenderer/stereo3d/hw_eyepose.h" - -/* stereoscopic 3D API */ -namespace s3d { - -/* Base class for stereoscopic 3D rendering modes */ -class Stereo3DMode -{ -public: - /* static methods for managing the selected stereoscopic view state */ - static const Stereo3DMode& getCurrentMode(); - static const Stereo3DMode& getMonoMode(); - - Stereo3DMode(); - virtual ~Stereo3DMode(); - virtual int eye_count() const { return eye_ptrs.Size(); } - virtual const EyePose * getEyePose(int ix) const { return eye_ptrs(ix); } - - /* hooks for setup and cleanup operations for each stereo mode */ - virtual void SetUp() const {}; - - virtual bool IsMono() const { return false; } - virtual void AdjustViewports() const {}; - virtual void AdjustPlayerSprites(FDrawInfo *di) const {}; - virtual void Present() const = 0; - -protected: - TArray eye_ptrs; - -private: - static Stereo3DMode const * currentStereo3DMode; - static void setCurrentMode(const Stereo3DMode& mode); -}; - - -/** -* Ordinary non-3D rendering -*/ -class MonoView : public Stereo3DMode -{ -public: - static const MonoView& getInstance(); - - bool IsMono() const override { return true; } - void Present() const override { } - -protected: - MonoView() { eye_ptrs.Push(¢ralEye); } - EyePose centralEye; -}; - - -} /* namespace st3d */ - - -#endif /* GL_STEREO3D_H_ */ diff --git a/src/gl/stereo3d/gl_stereo_cvars.cpp b/src/gl/stereo3d/gl_stereo_cvars.cpp deleted file mode 100644 index 3fbad45a1..000000000 --- a/src/gl/stereo3d/gl_stereo_cvars.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo_cvars.cpp -** Console variables related to stereoscopic 3D in GZDoom -** -*/ - -#include "gl/stereo3d/gl_stereo3d.h" -#include "gl/stereo3d/gl_stereo_leftright.h" -#include "gl/stereo3d/gl_anaglyph.h" -#include "gl/stereo3d/gl_quadstereo.h" -#include "gl/stereo3d/gl_sidebyside3d.h" -#include "gl/stereo3d/gl_interleaved3d.h" -#include "version.h" - -// Set up 3D-specific console variables: -CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG) - -// switch left and right eye views -CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG) - -// intraocular distance in meters -CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS - -// distance between viewer and the display screen -CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS - -// default conversion between (vertical) DOOM units and meters -CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS - -// Manage changing of 3D modes: -namespace s3d { - -// Initialize static member -Stereo3DMode const * Stereo3DMode::currentStereo3DMode = 0; // "nullptr" not resolved on linux (presumably not C++11) - -/* static */ -void Stereo3DMode::setCurrentMode(const Stereo3DMode& mode) { - Stereo3DMode::currentStereo3DMode = &mode; -} - -/* static */ -const Stereo3DMode& Stereo3DMode::getCurrentMode() -{ - // NOTE: Ensure that these vr_mode values correspond to the ones in wadsrc/static/menudef.z - switch (vr_mode) - { - case 1: - setCurrentMode(GreenMagenta::getInstance(vr_ipd)); - break; - case 2: - setCurrentMode(RedCyan::getInstance(vr_ipd)); - break; - case 3: - setCurrentMode(SideBySideFull::getInstance(vr_ipd)); - break; - case 4: - setCurrentMode(SideBySideSquished::getInstance(vr_ipd)); - break; - case 5: - setCurrentMode(LeftEyeView::getInstance(vr_ipd)); - break; - case 6: - setCurrentMode(RightEyeView::getInstance(vr_ipd)); - break; - case 7: - if (screen->enable_quadbuffered) { - setCurrentMode(QuadStereo::getInstance(vr_ipd)); - } - else { - setCurrentMode(MonoView::getInstance()); - } - break; - // TODO: 8: Oculus Rift - case 9: - setCurrentMode(AmberBlue::getInstance(vr_ipd)); - break; - // TODO: 10: HTC Vive/OpenVR - case 11: - setCurrentMode(TopBottom3D::getInstance(vr_ipd)); - break; - case 12: - setCurrentMode(RowInterleaved3D::getInstance(vr_ipd)); - break; - case 13: - setCurrentMode(ColumnInterleaved3D::getInstance(vr_ipd)); - break; - case 14: - setCurrentMode(CheckerInterleaved3D::getInstance(vr_ipd)); - break; - case 0: - default: - setCurrentMode(MonoView::getInstance()); - break; - } - return *currentStereo3DMode; -} - -const Stereo3DMode& Stereo3DMode::getMonoMode() -{ - setCurrentMode(MonoView::getInstance()); - return *currentStereo3DMode; -} - - -} /* namespace s3d */ - diff --git a/src/gl/stereo3d/gl_stereo_leftright.cpp b/src/gl/stereo3d/gl_stereo_leftright.cpp deleted file mode 100644 index be1d0a3d9..000000000 --- a/src/gl/stereo3d/gl_stereo_leftright.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo_leftright.cpp -** Offsets for left and right eye views -** -*/ - -#include "gl_stereo_leftright.h" -#include "vectors.h" // RAD2DEG -#include "doomtype.h" // M_PI -#include "hwrenderer/utility/hw_cvars.h" -#include "gl_load/gl_system.h" -#include "gl/renderer/gl_renderstate.h" -#include "gl/renderer/gl_renderer.h" -#include "gl/renderer/gl_renderbuffers.h" - -namespace s3d { - - -/* static */ -const LeftEyeView& LeftEyeView::getInstance(float ipd) -{ - static LeftEyeView instance(ipd); - instance.setIpd(ipd); - return instance; -} - -void LeftEyeView::Present() const -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); -} - -/* static */ -const RightEyeView& RightEyeView::getInstance(float ipd) -{ - static RightEyeView instance(ipd); - instance.setIpd(ipd); - return instance; -} - -void RightEyeView::Present() const -{ - GLRenderer->mBuffers->BindOutputFB(); - GLRenderer->ClearBorders(); - GLRenderer->mBuffers->BindEyeTexture(0, 0); - GLRenderer->DrawPresentTexture(screen->mOutputLetterbox, true); -} - - -} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_stereo_leftright.h b/src/gl/stereo3d/gl_stereo_leftright.h deleted file mode 100644 index ad4ccb769..000000000 --- a/src/gl/stereo3d/gl_stereo_leftright.h +++ /dev/null @@ -1,67 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo_leftright.h -** Offsets for left and right eye views -** -*/ - -#ifndef GL_STEREO_LEFTRIGHT_H_ -#define GL_STEREO_LEFTRIGHT_H_ - -#include "gl_stereo3d.h" - -namespace s3d { - - -/** - * As if viewed through the left eye only - */ -class LeftEyeView : public Stereo3DMode -{ -public: - static const LeftEyeView& getInstance(float ipd); - - LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } - void setIpd(float ipd) { eye.setIpd(ipd); } - void Present() const override; -protected: - LeftEyePose eye; -}; - - -class RightEyeView : public Stereo3DMode -{ -public: - static const RightEyeView& getInstance(float ipd); - - RightEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } - void setIpd(float ipd) { eye.setIpd(ipd); } - void Present() const override; -protected: - RightEyePose eye; -}; - - -} /* namespace s3d */ - -#endif /* GL_STEREO_LEFTRIGHT_H_ */ diff --git a/src/gl/stereo3d/scoped_color_mask.h b/src/gl/stereo3d/scoped_color_mask.h deleted file mode 100644 index 4e2103a28..000000000 --- a/src/gl/stereo3d/scoped_color_mask.h +++ /dev/null @@ -1,56 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** scoped_color_mask.h -** Stack-scoped class for temporarily changing the OpenGL color mask setting. -** -*/ - -#ifndef GL_STEREO3D_SCOPED_COLOR_MASK_H_ -#define GL_STEREO3D_SCOPED_COLOR_MASK_H_ - -#include "gl_load/gl_system.h" - -/** -* Temporarily change color mask -*/ -class ScopedColorMask -{ -public: - ScopedColorMask(bool r, bool g, bool b, bool a) - { - gl_RenderState.GetColorMask(saved[0], saved[1], saved[2], saved[3]); - gl_RenderState.SetColorMask(r, g, b, a); - gl_RenderState.ApplyColorMask(); - gl_RenderState.EnableDrawBuffers(1); - } - ~ScopedColorMask() { - gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); - gl_RenderState.SetColorMask(saved[0], saved[1], saved[2], saved[3]); - gl_RenderState.ApplyColorMask(); - } -private: - bool saved[4]; -}; - - -#endif // GL_STEREO3D_SCOPED_COLOR_MASK_H_ diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 37e5f9f45..451a9f7cb 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -37,10 +37,10 @@ #include "gl/renderer/gl_renderbuffers.h" #include "gl/textures/gl_samplers.h" #include "hwrenderer/utility/hw_clock.h" +#include "hwrenderer/utility/hw_vrmodes.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/data/gl_uniformbuffer.h" #include "gl/models/gl_models.h" -#include "gl/stereo3d/gl_stereo3d.h" #include "gl/shaders/gl_shaderprogram.h" #include "gl_debug.h" #include "r_videoscale.h" @@ -375,7 +375,10 @@ void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds) { Super::SetViewportRects(bounds); if (!bounds) - s3d::Stereo3DMode::getCurrentMode().AdjustViewports(); + { + auto vrmode = VRMode::GetVRMode(true); + vrmode->AdjustViewport(); + } } diff --git a/src/hwrenderer/stereo3d/hw_eyepose.cpp b/src/hwrenderer/stereo3d/hw_eyepose.cpp deleted file mode 100644 index 43e6d9bd4..000000000 --- a/src/hwrenderer/stereo3d/hw_eyepose.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** gl_stereo_leftright.cpp -** Offsets for left and right eye views -** -*/ - -#include "vectors.h" // RAD2DEG -#include "doomtype.h" // M_PI -#include "hwrenderer/utility/hw_cvars.h" -#include "hw_eyepose.h" -#include "v_video.h" - -EXTERN_CVAR(Float, vr_screendist) -EXTERN_CVAR(Float, vr_hunits_per_meter) -EXTERN_CVAR(Bool, vr_swap_eyes) - - -/* virtual */ -VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const -{ - VSMatrix result; - - float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); - result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar()); - - return result; -} - -/* virtual */ -void EyePose::GetViewShift(float yaw, float outViewShift[3]) const -{ - // pass-through for Mono view - outViewShift[0] = 0; - outViewShift[1] = 0; - outViewShift[2] = 0; -} - -/* virtual */ -VSMatrix ShiftedEyePose::GetProjection(float fov, float aspectRatio, float fovRatio) const -{ - double zNear = screen->GetZNear(); - double zFar = screen->GetZFar(); - - // For stereo 3D, use asymmetric frustum shift in projection matrix - // Q: shouldn't shift vary with roll angle, at least for desktop display? - // A: No. (lab) roll is not measured on desktop display (yet) - double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units - // double frustumShift = 0; // Turning off shift for debugging - double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; - double fW = fH * aspectRatio * squish; - double left = -fW - frustumShift; - double right = fW - frustumShift; - double bottom = -fH; - double top = fH; - - VSMatrix result(1); - result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar); - return result; -} - - -/* virtual */ -void ShiftedEyePose::GetViewShift(float yaw, float outViewShift[3]) const -{ - float dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); - float dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); - outViewShift[0] = dx; - outViewShift[1] = dy; - outViewShift[2] = 0; -} - -float ShiftedEyePose::getShift() const -{ - return vr_swap_eyes ? -shift : shift; -} - diff --git a/src/hwrenderer/stereo3d/hw_eyepose.h b/src/hwrenderer/stereo3d/hw_eyepose.h deleted file mode 100644 index 7531f29ba..000000000 --- a/src/hwrenderer/stereo3d/hw_eyepose.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "r_data/matrix.h" - - -/* Viewpoint of one eye */ -class EyePose -{ -public: - EyePose() {} - virtual ~EyePose() {} - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; - virtual void GetViewShift(float yaw, float outViewShift[3]) const; - virtual void SetUp() const {} -}; - -class ShiftedEyePose : public EyePose -{ -public: - ShiftedEyePose(float shift, float squish) : shift(shift), squish(squish) {}; - float getShift() const; - virtual VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; - virtual void GetViewShift(float yaw, float outViewShift[3]) const; - -protected: - void setShift(float shift) { this->shift = shift; } - -private: - float shift; - float squish; -}; - - -class LeftEyePose : public ShiftedEyePose -{ -public: - LeftEyePose(float ipd, float squish = 1.f) : ShiftedEyePose( -0.5f * ipd, squish) {} - void setIpd(float ipd) { setShift(-0.5f * ipd); } -}; - - -class RightEyePose : public ShiftedEyePose -{ -public: - RightEyePose(float ipd, float squish = 1.f) : ShiftedEyePose(0.5f * ipd, squish) {} - void setIpd(float ipd) { setShift(0.5f * ipd); } -}; diff --git a/src/hwrenderer/utility/hw_vrmodes.cpp b/src/hwrenderer/utility/hw_vrmodes.cpp new file mode 100644 index 000000000..d43ad1f76 --- /dev/null +++ b/src/hwrenderer/utility/hw_vrmodes.cpp @@ -0,0 +1,176 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_stereo_leftright.cpp +** Offsets for left and right eye views +** +*/ + +#include "vectors.h" // RAD2DEG +#include "doomtype.h" // M_PI +#include "hwrenderer/utility/hw_cvars.h" +#include "hw_vrmodes.h" +#include "v_video.h" + +// Set up 3D-specific console variables: +CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG) + +// switch left and right eye views +CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG) + +// intraocular distance in meters +CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS + +// distance between viewer and the display screen +CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS + +// default conversion between (vertical) DOOM units and meters +CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS + + +#define isqrt2 0.7071067812f +static VRMode vrmi_mono = { 1, 1.f, 1.f, 1.f,{ { 0.f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_stereo = { 2, 1.f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_sbsfull = { 2, .5f, 1.f, 2.f,{ { -.5f, .5f },{ .5f, .5f } } }; +static VRMode vrmi_sbssquished = { 2, .5f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_lefteye = { 1, 1.f, 1.f, 1.f, { { -.5f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_righteye = { 1, 1.f, 1.f, 1.f,{ { .5f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_topbottom = { 2, 1.f, .5f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; + +const VRMode *VRMode::GetVRMode(bool toscreen) +{ + switch (toscreen && vid_rendermode == 4 ? vr_mode : 0) + { + default: + case VR_MONO: + return &vrmi_mono; + + case VR_GREENMAGENTA: + case VR_REDCYAN: + case VR_QUADSTEREO: + case VR_AMBERBLUE: + return &vrmi_stereo; + + case VR_SIDEBYSIDESQUISHED: + case VR_COLUMNINTERLEAVED: + return &vrmi_sbssquished; + + case VR_SIDEBYSIDEFULL: + return &vrmi_sbsfull; + + case VR_TOPBOTTOM: + case VR_ROWINTERLEAVED: + return &vrmi_topbottom; + + case VR_LEFTEYEVIEW: + return &vrmi_lefteye; + + case VR_RIGHTEYEVIEW: + return &vrmi_righteye; + + case VR_CHECKERINTERLEAVED: + return &vrmi_checker; + } +} + +void VRMode::AdjustViewport() const +{ + screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale); + screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale); + screen->mSceneViewport.width = (int)(screen->mSceneViewport.width * mHorizontalViewportScale); + screen->mSceneViewport.left = (int)(screen->mSceneViewport.left * mHorizontalViewportScale); + + screen->mScreenViewport.height = (int)(screen->mScreenViewport.height * mVerticalViewportScale); + screen->mScreenViewport.top = (int)(screen->mScreenViewport.top * mVerticalViewportScale); + screen->mScreenViewport.width = (int)(screen->mScreenViewport.width * mHorizontalViewportScale); + screen->mScreenViewport.left = (int)(screen->mScreenViewport.left * mHorizontalViewportScale); +} + +VSMatrix VRMode::GetHUDSpriteProjection() const +{ + VSMatrix mat; + int w = screen->mScreenViewport.width; + int h = screen->mScreenViewport.height; + float scaled_w = w * mWeaponProjectionScale; + float left_ofs = (scaled_w - w) / 2.f; + mat.ortho(left_ofs, left_ofs + w, (float)h, 0, -1.0f, 1.0f); + return mat; +} + +float VREyeInfo::getShift() const +{ + auto res = mShiftFactor * vr_ipd; + return vr_swap_eyes ? -res : res; +} + +VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const +{ + VSMatrix result; + + if (mShiftFactor == 0) + { + float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); + result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar()); + return result; + } + else + { + double zNear = screen->GetZNear(); + double zFar = screen->GetZFar(); + + // For stereo 3D, use asymmetric frustum shift in projection matrix + // Q: shouldn't shift vary with roll angle, at least for desktop display? + // A: No. (lab) roll is not measured on desktop display (yet) + double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units + // double frustumShift = 0; // Turning off shift for debugging + double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; + double fW = fH * aspectRatio * mScaleFactor; + double left = -fW - frustumShift; + double right = fW - frustumShift; + double bottom = -fH; + double top = fH; + + VSMatrix result(1); + result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar); + return result; + } +} + + + +/* virtual */ +DVector3 VREyeInfo::GetViewShift(float yaw) const +{ + if (mShiftFactor == 0) + { + // pass-through for Mono view + return { 0,0,0 }; + } + else + { + double dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + double dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + return { dx, dy, 0 }; + } +} + diff --git a/src/hwrenderer/utility/hw_vrmodes.h b/src/hwrenderer/utility/hw_vrmodes.h new file mode 100644 index 000000000..db4fe3985 --- /dev/null +++ b/src/hwrenderer/utility/hw_vrmodes.h @@ -0,0 +1,46 @@ +#pragma once + +#include "r_data/matrix.h" + + +enum +{ + VR_MONO = 0, + VR_GREENMAGENTA = 1, + VR_REDCYAN = 2, + VR_SIDEBYSIDEFULL = 3, + VR_SIDEBYSIDESQUISHED = 4, + VR_LEFTEYEVIEW = 5, + VR_RIGHTEYEVIEW = 6, + VR_QUADSTEREO = 7, + VR_AMBERBLUE = 9, + VR_TOPBOTTOM = 11, + VR_ROWINTERLEAVED = 12, + VR_COLUMNINTERLEAVED = 13, + VR_CHECKERINTERLEAVED = 14 +}; + +struct VREyeInfo +{ + float mShiftFactor; + float mScaleFactor; + + VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; + DVector3 GetViewShift(float yaw) const; +private: + float getShift() const; + +}; + +struct VRMode +{ + int mEyeCount; + float mHorizontalViewportScale; + float mVerticalViewportScale; + float mWeaponProjectionScale; + VREyeInfo mEyes[2]; + + static const VRMode *GetVRMode(bool toscreen = true); + void AdjustViewport() const; + VSMatrix GetHUDSpriteProjection() const; +}; diff --git a/src/hwrenderer/utility/scoped_view_shifter.h b/src/hwrenderer/utility/scoped_view_shifter.h deleted file mode 100644 index 9e80a28ad..000000000 --- a/src/hwrenderer/utility/scoped_view_shifter.h +++ /dev/null @@ -1,62 +0,0 @@ -// -//--------------------------------------------------------------------------- -// -// Copyright(C) 2015 Christopher Bruns -// All rights reserved. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//-------------------------------------------------------------------------- -// -/* -** scoped_view_shifter.h -** Stack-scoped class for temporarily changing camera viewpoint -** Used for stereoscopic 3D. -** -*/ - -#ifndef GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_ -#define GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_ - -#include "basictypes.h" -#include "vectors.h" - -/** - * Temporarily shift - */ -class ScopedViewShifter -{ -public: - ScopedViewShifter(DVector3 &var, float dxyz[3]) // in meters - { - // save original values - mVar = &var; - cachedView = var; - // modify values - var += DVector3(dxyz[0], dxyz[1], dxyz[2]); - } - - ~ScopedViewShifter() - { - // restore original values - *mVar = cachedView; - } - -private: - DVector3 *mVar; - DVector3 cachedView; -}; - - -#endif // GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_ From 491898fe2c628ba086659f05f6f251ef9c85ab1f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Jun 2018 17:15:27 +0200 Subject: [PATCH 58/58] - fixed calculation of projection matrix for weapon sprites. - fixed color mask for green/magenta. - fixed crash when initializing video, because it was accessing 'screen' before it was set from within the framebuffer's constructor. --- src/gl/renderer/gl_stereo3d.cpp | 2 +- src/gl/system/gl_framebuffer.cpp | 2 +- src/hwrenderer/utility/hw_vrmodes.cpp | 12 ++++++------ src/hwrenderer/utility/hw_vrmodes.h | 3 ++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/gl/renderer/gl_stereo3d.cpp b/src/gl/renderer/gl_stereo3d.cpp index 6035a67bf..3155bbdc5 100644 --- a/src/gl/renderer/gl_stereo3d.cpp +++ b/src/gl/renderer/gl_stereo3d.cpp @@ -324,7 +324,7 @@ void FGLRenderer::PresentStereo() return; case VR_GREENMAGENTA: - PresentAnaglyph(false, true, true); + PresentAnaglyph(false, true, false); break; case VR_REDCYAN: diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 451a9f7cb..c30f22c63 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -377,7 +377,7 @@ void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds) if (!bounds) { auto vrmode = VRMode::GetVRMode(true); - vrmode->AdjustViewport(); + vrmode->AdjustViewport(this); } } diff --git a/src/hwrenderer/utility/hw_vrmodes.cpp b/src/hwrenderer/utility/hw_vrmodes.cpp index d43ad1f76..ebb2aad9f 100644 --- a/src/hwrenderer/utility/hw_vrmodes.cpp +++ b/src/hwrenderer/utility/hw_vrmodes.cpp @@ -93,7 +93,7 @@ const VRMode *VRMode::GetVRMode(bool toscreen) } } -void VRMode::AdjustViewport() const +void VRMode::AdjustViewport(DFrameBuffer *screen) const { screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale); screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale); @@ -109,11 +109,11 @@ void VRMode::AdjustViewport() const VSMatrix VRMode::GetHUDSpriteProjection() const { VSMatrix mat; - int w = screen->mScreenViewport.width; - int h = screen->mScreenViewport.height; - float scaled_w = w * mWeaponProjectionScale; - float left_ofs = (scaled_w - w) / 2.f; - mat.ortho(left_ofs, left_ofs + w, (float)h, 0, -1.0f, 1.0f); + int w = screen->GetWidth(); + int h = screen->GetHeight(); + float scaled_w = w / mWeaponProjectionScale; + float left_ofs = (w - scaled_w) / 2.f; + mat.ortho(left_ofs, left_ofs + scaled_w, (float)h, 0, -1.0f, 1.0f); return mat; } diff --git a/src/hwrenderer/utility/hw_vrmodes.h b/src/hwrenderer/utility/hw_vrmodes.h index db4fe3985..43caa83d7 100644 --- a/src/hwrenderer/utility/hw_vrmodes.h +++ b/src/hwrenderer/utility/hw_vrmodes.h @@ -2,6 +2,7 @@ #include "r_data/matrix.h" +class DFrameBuffer; enum { @@ -41,6 +42,6 @@ struct VRMode VREyeInfo mEyes[2]; static const VRMode *GetVRMode(bool toscreen = true); - void AdjustViewport() const; + void AdjustViewport(DFrameBuffer *fb) const; VSMatrix GetHUDSpriteProjection() const; };