From a90c86e9a5d757a0ee90da7cd6686cc2cc5e5c7e Mon Sep 17 00:00:00 2001 From: Fireball Date: Fri, 5 Aug 2022 00:16:44 +0100 Subject: [PATCH] Parallax-aware zoom How this works: parallax values configure perceived distance from camera when it's moving along x and y axes. Assume that zoom is moving the camera away and scale layers accordingly, with background layers (furtherst away) changing the least. New per-ItemGroup (LayerGroup) setting allows to set the new parallax value independently from the other two. This can be used to do tricks like on Time Shop zoom correctly or make it feel like the camera is changing the field of view at the same time as moving in space. --- src/engine/shared/config_variables.h | 1 - src/game/client/components/hud.cpp | 2 +- src/game/client/components/maplayers.cpp | 7 +------ src/game/client/components/nameplates.cpp | 4 ++-- src/game/client/render.cpp | 17 +++++++++++++++-- src/game/client/render.h | 5 +++-- src/game/editor/editor.cpp | 10 ++++++---- src/game/editor/editor.h | 1 + src/game/editor/io.cpp | 4 ++++ src/game/editor/popups.cpp | 4 ++++ src/game/layers.cpp | 5 +++++ src/game/mapitems.h | 14 +++++++++++++- 12 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index e5a13a3f9..61c566805 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -322,7 +322,6 @@ MACRO_CONFIG_INT(ClShowOthers, cl_show_others, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show players in other teams (alpha value, 0 invisible, 100 fully visible)") MACRO_CONFIG_INT(ClOverlayEntities, cl_overlay_entities, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Overlay game tiles with a percentage of opacity") MACRO_CONFIG_INT(ClShowQuads, cl_showquads, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show quads (only interesting for mappers, or if your system has extremely bad performance)") -MACRO_CONFIG_INT(ClZoomBackgroundLayers, cl_zoom_background_layers, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom background layers") MACRO_CONFIG_COL(ClBackgroundColor, cl_background_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background color") // 0 0 128 MACRO_CONFIG_COL(ClBackgroundEntitiesColor, cl_background_entities_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities) color") // 0 0 128 MACRO_CONFIG_STR(ClBackgroundEntities, cl_background_entities, 100, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities)") diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 7dcbf7e95..a02e11365 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -621,7 +621,7 @@ void CHud::RenderCursor() if(!m_pClient->m_Snap.m_pLocalCharacter || Client()->State() == IClient::STATE_DEMOPLAYBACK) return; - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); // render cursor int CurWeapon = m_pClient->m_Snap.m_pLocalCharacter->m_Weapon % NUM_WEAPONS; diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 6c21603af..f54834742 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1592,12 +1592,7 @@ void CMapLayers::OnRender() (int)((x1 - x0) * Graphics()->ScreenWidth()), (int)((y1 - y0) * Graphics()->ScreenHeight())); } - if((!g_Config.m_ClZoomBackgroundLayers || m_Type == TYPE_FULL_DESIGN) && !pGroup->m_ParallaxX && !pGroup->m_ParallaxY) - { - RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, 1.0f); - } - else - RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom); + RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom); for(int l = 0; l < pGroup->m_NumLayers; l++) { diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 60c42a642..0519ab641 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -101,7 +101,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP // create nameplates at standard zoom float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); m_aNamePlates[ClientID].m_NameTextWidth = TextRender()->TextWidth(0, FontSize, pName, -1, -1.0f); @@ -126,7 +126,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP // create nameplates at standard zoom float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); - RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup()); + RenderTools()->MapScreenToInterface(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y); m_aNamePlates[ClientID].m_ClanNameTextWidth = TextRender()->TextWidth(0, FontSizeClan, pClan, -1, -1.0f); diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 825e7bbc0..6d0a1deeb 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -783,10 +783,15 @@ void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *pWidth, flo } void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, - float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) + float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) { float Width, Height; CalcScreenParams(Aspect, Zoom, &Width, &Height); + + float Scale = (ParallaxZoom * (Zoom - 1.0f) + 100.0f) / 100.0f / Zoom; + Width *= Scale; + Height *= Scale; + CenterX *= ParallaxX / 100.0f; CenterY *= ParallaxY / 100.0f; pPoints[0] = OffsetX + CenterX - Width / 2; @@ -798,7 +803,15 @@ void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float Parallax void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom) { float aPoints[4]; - MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, + MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, pGroup->GetParallaxZoom(), pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, aPoints); Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); } + +void CRenderTools::MapScreenToInterface(float CenterX, float CenterY) +{ + float aPoints[4]; + MapScreenToWorld(CenterX, CenterY, 100.0f, 100.0f, 100.0f, + 0, 0, Graphics()->ScreenAspect(), 1.0f, aPoints); + Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); +} diff --git a/src/game/client/render.h b/src/game/client/render.h index bc63d031b..2675a4187 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -142,8 +142,9 @@ public: // helpers void CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight); void MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, - float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); - void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f); + float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); + void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom); + void MapScreenToInterface(float CenterX, float CenterY); // DDRace diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index ac556a6ab..cf0bf744b 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -102,6 +102,7 @@ CLayerGroup::CLayerGroup() m_OffsetY = 0; m_ParallaxX = 100; m_ParallaxY = 100; + m_ParallaxZoom = 100; m_UseClipping = 0; m_ClipX = 0; @@ -133,7 +134,7 @@ void CLayerGroup::Mapping(float *pPoints) { m_pMap->m_pEditor->RenderTools()->MapScreenToWorld( m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY, - m_ParallaxX, m_ParallaxY, m_OffsetX, m_OffsetY, + m_ParallaxX, m_ParallaxY, 100.0f, m_OffsetX, m_OffsetY, m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints); pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX; @@ -2870,7 +2871,7 @@ void CEditor::DoMapEditor(CUIRect View) RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); if(i == 0) { @@ -2912,7 +2913,7 @@ void CEditor::DoMapEditor(CUIRect View) RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); CUIRect r; r.x = aPoints[0]; @@ -6118,7 +6119,7 @@ void CEditor::ZoomMouseTarget(float ZoomFactor) float aPoints[4]; RenderTools()->MapScreenToWorld( m_WorldOffsetX, m_WorldOffsetY, - 100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints); + 100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints); float WorldWidth = aPoints[2] - aPoints[0]; float WorldHeight = aPoints[3] - aPoints[1]; @@ -6230,6 +6231,7 @@ void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture) CLayerGroup *pGroup = NewGroup(); pGroup->m_ParallaxX = 0; pGroup->m_ParallaxY = 0; + pGroup->m_ParallaxZoom = 0; CLayerQuads *pLayer = new CLayerQuads; pLayer->m_pEditor = m_pEditor; CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200); diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 0c0ec15af..b0244ba38 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -176,6 +176,7 @@ public: int m_ParallaxX; int m_ParallaxY; + int m_ParallaxZoom; int m_UseClipping; int m_ClipX; diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 3b441fc28..5aeb82f9a 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -184,6 +184,8 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // save group name StrToInts(GItem.m_aName, sizeof(GItem.m_aName) / sizeof(int), pGroup->m_aName); + GItem.m_ParallaxZoom = pGroup->m_ParallaxZoom; + for(const auto &pLayer : pGroup->m_vpLayers) { if(pLayer->m_Type == LAYERTYPE_TILES) @@ -607,6 +609,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pGItem->m_Version >= 3) IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName) / sizeof(int), pGroup->m_aName); + pGroup->m_ParallaxZoom = pGItem->GetParallaxZoom(); + for(int l = 0; l < pGItem->m_NumLayers; l++) { CLayer *pLayer = nullptr; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 42648aab4..4b63412f8 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -320,6 +320,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) PROP_POS_Y, PROP_PARA_X, PROP_PARA_Y, + PROP_PARA_ZOOM, PROP_USE_CLIPPING, PROP_CLIP_X, PROP_CLIP_Y, @@ -334,6 +335,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) {"Pos Y", -pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Para Y", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Use Clipping", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_UseClipping, PROPTYPE_BOOL, 0, 1}, {"Clip X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, @@ -364,6 +366,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX = NewVal; else if(Prop == PROP_PARA_Y) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY = NewVal; + else if(Prop == PROP_PARA_ZOOM) + pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom = NewVal; else if(Prop == PROP_POS_X) pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetX = -NewVal; else if(Prop == PROP_POS_Y) diff --git a/src/game/layers.cpp b/src/game/layers.cpp index ef28343a9..d4024777a 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -66,6 +66,8 @@ void CLayers::Init(class IKernel *pKernel) m_pGameGroup->m_ClipH = 0; } + if(m_pGameGroup->m_Version >= 4) + m_pGameGroup->m_ParallaxZoom = 100; //break; } if(pTilemap->m_Flags & TILESLAYERFLAG_TELE) @@ -158,6 +160,9 @@ void CLayers::InitBackground(class IMap *pMap) m_pGameGroup->m_ClipW = 0; m_pGameGroup->m_ClipH = 0; } + + if(m_pGameGroup->m_Version >= 4) + m_pGameGroup->m_ParallaxZoom = 100; //We don't care about tile layers. } } diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 64ac181fe..664c59461 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -278,7 +278,7 @@ struct CMapItemGroup : public CMapItemGroup_v1 { enum { - CURRENT_VERSION = 3 + CURRENT_VERSION = 4 }; int m_UseClipping; @@ -288,6 +288,18 @@ struct CMapItemGroup : public CMapItemGroup_v1 int m_ClipH; int m_aName[3]; + + // ItemGroup's perceived distance from camera when zooming. Similar to how + // Parallax{X,Y} works when camera is moving along the X and Y axes, + // this setting applies to camera moving closer or away (zooming in or out). + int m_ParallaxZoom; + int GetParallaxZoom() const + { + if(m_Version >= 4) + return m_ParallaxZoom; + else + return maximum(m_ParallaxX, m_ParallaxY); + } }; struct CMapItemLayer