5696: Add option for parallax-aware zoom r=def- a=Fireball-Teeworlds

## How this works

Currently we have parallax X and Y parameters that control how far away a layer feels. Currently it's only taken into account for camera moving sideways.

This pull request makes zoom behave as if the camera was moving closer or further away, taking into account the distance to the layer.

In order to provide flexibility, this behaviour is using a separate parallax value, similar to how we have separate values for X and Y. Para Zoom will default to the minimum of parallax X, Y, clamped to be between 0 and 100. This seems to look well on most maps.

This pull request also introduces two new features to the Editor:
* Controls for configuring per-group Para Zoom value. If Custom Zoom is set to No, Para Zoom will automatically keep following to the default value described above, so that people new to the feature don't have to figure out how it works.
* Zoom button that previews the zoom behavior (next to Proof at the top).

## Editor Screenshots

### Para Zoom controls

![screenshot_2022-08-22_21-38-04](https://user-images.githubusercontent.com/68162181/186014490-f7b91245-460f-405f-8d5c-3f91db2a1b9a.png)

![screenshot_2022-08-22_21-37-58](https://user-images.githubusercontent.com/68162181/186014522-03b6e814-4dd9-4d07-9af9-7db5fb434a56.png)

### Zoom Button

![screenshot_2022-08-22_21-40-46](https://user-images.githubusercontent.com/68162181/186014856-2d1d499d-9011-439c-b217-536957e0c1e3.png)

![screenshot_2022-08-22_21-40-50](https://user-images.githubusercontent.com/68162181/186014874-6d2939d3-00ff-4327-a790-414b5151ba31.png)

## In-Game Screenshots

Video: https://youtu.be/W7eXQN0gRFI

This is an older version of the pull request that had an option to disable the new behaviour. The current version can only be disabled in map editor for a particular map.

### Springlobe 3

With new feature:
![screenshot_2022-08-02_04-28-19](https://user-images.githubusercontent.com/68162181/182286371-b67cee1c-73d8-4a24-a9c3-1b28340a3b42.png)

Without:
![screenshot_2022-08-02_04-28-25](https://user-images.githubusercontent.com/68162181/182286367-24555381-1700-4ff1-80c7-39e5dce63820.png)

### Beyond Dreams

With new feature:
![screenshot_2022-08-02_04-28-59](https://user-images.githubusercontent.com/68162181/182286322-114ecb90-0860-462c-9012-bb2f3ff848fb.png)

Without:
![screenshot_2022-08-02_04-28-55](https://user-images.githubusercontent.com/68162181/182286654-f34da72b-7991-4f42-89ad-679279fcb83e.png)

## Checklist

- [X] Tested the change ingame
- [X] Provided screenshots if it is a visual change
- [X] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [X] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [X] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Fireball <fireball.teeworlds@gmail.com>
This commit is contained in:
bors[bot] 2022-08-22 21:15:41 +00:00 committed by GitHub
commit 49bed71932
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 184 additions and 47 deletions

View file

@ -324,7 +324,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)")

View file

@ -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;

View file

@ -1562,6 +1562,7 @@ void CMapLayers::OnRender()
for(int g = 0; g < m_pLayers->NumGroups(); g++)
{
CMapItemGroup *pGroup = m_pLayers->GetGroup(g);
CMapItemGroupEx *pGroupEx = m_pLayers->GetGroupEx(g);
if(!pGroup)
{
@ -1575,7 +1576,7 @@ void CMapLayers::OnRender()
{
// set clipping
float aPoints[4];
RenderTools()->MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), GetCurCamera()->m_Zoom);
RenderTools()->MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), m_pLayers->GameGroupEx(), GetCurCamera()->m_Zoom);
Graphics()->GetScreen(&aPoints[0], &aPoints[1], &aPoints[2], &aPoints[3]);
float x0 = (pGroup->m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]);
float y0 = (pGroup->m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]);
@ -1593,12 +1594,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, pGroupEx, GetCurCamera()->m_Zoom);
for(int l = 0; l < pGroup->m_NumLayers; l++)
{

View file

@ -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);

View file

@ -15,6 +15,7 @@
#include <game/generated/protocol.h>
#include <game/mapitems.h>
#include <game/mapitems_ex.h>
static float gs_SpriteWScale;
static float gs_SpriteHScale;
@ -389,10 +390,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;
@ -401,10 +407,19 @@ void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float Parallax
pPoints[3] = pPoints[1] + Height;
}
void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom)
void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, CMapItemGroupEx *pGroupEx, float Zoom)
{
float ParallaxZoom = GetParallaxZoom(pGroup, pGroupEx);
float aPoints[4];
MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY,
MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, ParallaxZoom,
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]);
}

View file

@ -20,6 +20,7 @@ struct CDataSprite;
struct CDataSprite;
struct CEnvPoint;
struct CMapItemGroup;
struct CMapItemGroupEx;
struct CQuad;
class CTeeRenderInfo
@ -126,8 +127,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, CMapItemGroupEx *pGroupEx, float Zoom);
void MapScreenToInterface(float CenterX, float CenterY);
// DDRace

View file

@ -102,6 +102,7 @@ CLayerGroup::CLayerGroup()
m_OffsetY = 0;
m_ParallaxX = 100;
m_ParallaxY = 100;
m_ParallaxZoom = 100;
m_UseClipping = 0;
m_ClipX = 0;
@ -131,9 +132,11 @@ void CLayerGroup::Convert(CUIRect *pRect)
void CLayerGroup::Mapping(float *pPoints)
{
float ParallaxZoom = m_pMap->m_pEditor->m_PreviewZoom ? m_ParallaxZoom : 100.0f;
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, ParallaxZoom, 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;
@ -880,6 +883,16 @@ void CEditor::DoToolbar(CUIRect ToolBar)
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
// zoom button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ZoomButton = 0;
if(DoButton_Editor(&s_ZoomButton, "Zoom", m_PreviewZoom, &Button, 0, "Toggles preview of how layers will be zoomed in-game"))
{
m_PreviewZoom = !m_PreviewZoom;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
// grid button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_GridButton = 0;
@ -1250,8 +1263,8 @@ void CEditor::DoSoundSource(CSoundSource *pSource, int Index)
float CenterX = fx2f(pSource->m_Position.x);
float CenterY = fx2f(pSource->m_Position.y);
float dx = (CenterX - wx) / m_WorldZoom;
float dy = (CenterY - wy) / m_WorldZoom;
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
@ -1343,7 +1356,7 @@ void CEditor::DoSoundSource(CSoundSource *pSource, int Index)
Graphics()->SetColor(0, 1, 0, 1);
}
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -1371,8 +1384,8 @@ void CEditor::DoQuad(CQuad *pQuad, int Index)
float CenterX = fx2f(pQuad->m_aPoints[4].x);
float CenterY = fx2f(pQuad->m_aPoints[4].y);
float dx = (CenterX - wx) / m_WorldZoom;
float dy = (CenterY - wy) / m_WorldZoom;
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
@ -1382,7 +1395,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index)
if(IsQuadSelected(Index))
{
Graphics()->SetColor(0, 0, 0, 1);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f * m_WorldZoom, 7.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -1598,7 +1611,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index)
else
Graphics()->SetColor(0, 1, 0, 1);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -1612,8 +1625,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
float px = fx2f(pQuad->m_aPoints[V].x);
float py = fx2f(pQuad->m_aPoints[V].y);
float dx = (px - wx) / m_WorldZoom;
float dy = (py - wy) / m_WorldZoom;
float dx = (px - wx) / m_MouseWScale;
float dy = (py - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
@ -1621,7 +1634,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
if(IsQuadSelected(QuadIndex) && m_SelectedPoints & (1 << V))
{
Graphics()->SetColor(0, 0, 0, 1);
IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_WorldZoom, 7.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -1805,7 +1818,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
else
Graphics()->SetColor(1, 0, 0, 1);
IGraphics::CQuadItem QuadItem(px, py, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(px, py, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -1841,7 +1854,7 @@ void CEditor::DoQuadKnife(int QuadIndex)
CQuad *pQuad = &pLayer->m_vQuads[QuadIndex];
bool IgnoreGrid = Input()->KeyIsPressed(KEY_LALT) || Input()->KeyIsPressed(KEY_RALT);
float SnapRadius = 4.f * m_WorldZoom;
float SnapRadius = 4.f * m_MouseWScale;
vec2 Mouse = vec2(UI()->MouseWorldX(), UI()->MouseWorldY());
vec2 Point = Mouse;
@ -2042,14 +2055,14 @@ void CEditor::DoQuadKnife(int QuadIndex)
IGraphics::CQuadItem aMarkers[4];
for(int i = 0; i < m_QuadKnifeCount; i++)
aMarkers[i] = IGraphics::CQuadItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, 5.f * m_WorldZoom, 5.f * m_WorldZoom);
aMarkers[i] = IGraphics::CQuadItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, 5.f * m_MouseWScale, 5.f * m_MouseWScale);
Graphics()->SetColor(0.f, 0.f, 1.f, 1.f);
Graphics()->QuadsDraw(aMarkers, m_QuadKnifeCount);
if(ValidPosition)
{
IGraphics::CQuadItem MarkerCurrent(Point.x, Point.y, 5.f * m_WorldZoom, 5.f * m_WorldZoom);
IGraphics::CQuadItem MarkerCurrent(Point.x, Point.y, 5.f * m_MouseWScale, 5.f * m_MouseWScale);
Graphics()->QuadsDraw(&MarkerCurrent, 1);
}
@ -2196,8 +2209,8 @@ void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex)
float CenterX = fx2f(pQuad->m_aPoints[4].x) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[0]);
float CenterY = fx2f(pQuad->m_aPoints[4].y) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[1]);
float dx = (CenterX - wx) / m_WorldZoom;
float dy = (CenterY - wy) / m_WorldZoom;
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50.0f && UI()->CheckActiveItem(nullptr))
{
UI()->SetHotItem(pID);
@ -2289,7 +2302,7 @@ void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex)
else
Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_WorldZoom, 5.0f * m_WorldZoom);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
@ -2723,6 +2736,7 @@ void CEditor::DoMapEditor(CUIRect View)
m_Brush.m_OffsetY += pGroup->m_OffsetY;
m_Brush.m_ParallaxX = pGroup->m_ParallaxX;
m_Brush.m_ParallaxY = pGroup->m_ParallaxY;
m_Brush.m_ParallaxZoom = pGroup->m_ParallaxZoom;
m_Brush.Render();
float w, h;
m_Brush.GetSize(&w, &h);
@ -2799,13 +2813,13 @@ void CEditor::DoMapEditor(CUIRect View)
{
if(s_Operation == OP_PAN_WORLD)
{
m_WorldOffsetX -= m_MouseDeltaX * m_WorldZoom;
m_WorldOffsetY -= m_MouseDeltaY * m_WorldZoom;
m_WorldOffsetX -= m_MouseDeltaX * m_MouseWScale;
m_WorldOffsetY -= m_MouseDeltaY * m_MouseWScale;
}
else if(s_Operation == OP_PAN_EDITOR)
{
m_EditorOffsetX -= m_MouseDeltaX * m_WorldZoom;
m_EditorOffsetY -= m_MouseDeltaY * m_WorldZoom;
m_EditorOffsetX -= m_MouseDeltaX * m_MouseWScale;
m_EditorOffsetY -= m_MouseDeltaY * m_MouseWScale;
}
// release mouse
@ -2821,13 +2835,13 @@ void CEditor::DoMapEditor(CUIRect View)
{
float PanSpeed = 64.0f;
if(Input()->KeyPress(KEY_A))
m_WorldOffsetX -= PanSpeed * m_WorldZoom;
m_WorldOffsetX -= PanSpeed * m_MouseWScale;
else if(Input()->KeyPress(KEY_D))
m_WorldOffsetX += PanSpeed * m_WorldZoom;
m_WorldOffsetX += PanSpeed * m_MouseWScale;
if(Input()->KeyPress(KEY_W))
m_WorldOffsetY -= PanSpeed * m_WorldZoom;
m_WorldOffsetY -= PanSpeed * m_MouseWScale;
else if(Input()->KeyPress(KEY_S))
m_WorldOffsetY += PanSpeed * m_WorldZoom;
m_WorldOffsetY += PanSpeed * m_MouseWScale;
}
}
else if(UI()->CheckActiveItem(s_pEditorID))
@ -2886,7 +2900,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)
{
@ -2928,7 +2942,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];
@ -3360,7 +3374,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View)
static int s_GroupPopupId = 0;
if(Result == 2)
UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 230, PopupGroup);
UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 256, PopupGroup);
if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick())
m_Map.m_vpGroups[g]->m_Collapse ^= 1;
@ -6134,7 +6148,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];
@ -6246,6 +6260,8 @@ void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture)
CLayerGroup *pGroup = NewGroup();
pGroup->m_ParallaxX = 0;
pGroup->m_ParallaxY = 0;
pGroup->m_CustomParallaxZoom = 0;
pGroup->m_ParallaxZoom = 0;
CLayerQuads *pLayer = new CLayerQuads;
pLayer->m_pEditor = m_pEditor;
CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200);
@ -6416,6 +6432,8 @@ void CEditor::OnUpdate()
float WorldWidth = aPoints[2] - aPoints[0];
float WorldHeight = aPoints[3] - aPoints[1];
m_MouseWScale = WorldWidth / Graphics()->WindowWidth();
m_MouseWorldX = aPoints[0] + WorldWidth * (s_MouseX / Graphics()->WindowWidth());
m_MouseWorldY = aPoints[1] + WorldHeight * (s_MouseY / Graphics()->WindowHeight());
m_MouseDeltaWx = m_MouseDeltaX * (WorldWidth / Graphics()->WindowWidth());

View file

@ -11,6 +11,7 @@
#include <game/client/render.h>
#include <game/client/ui.h>
#include <game/mapitems.h>
#include <game/mapitems_ex.h>
#include <engine/editor.h>
#include <engine/graphics.h>
@ -187,6 +188,8 @@ public:
int m_ParallaxX;
int m_ParallaxY;
int m_CustomParallaxZoom;
int m_ParallaxZoom;
int m_UseClipping;
int m_ClipX;
@ -253,6 +256,12 @@ public:
}
}*/
void OnEdited()
{
if(!m_CustomParallaxZoom)
m_ParallaxZoom = GetParallaxZoomDefault(m_ParallaxX, m_ParallaxY);
}
void Clear()
{
m_vpLayers.clear();
@ -788,6 +797,7 @@ public:
m_GuiActive = true;
m_ProofBorders = false;
m_PreviewZoom = false;
m_ShowTileInfo = false;
m_ShowDetail = true;
@ -962,6 +972,8 @@ public:
bool m_ShowMousePointer;
bool m_GuiActive;
bool m_ProofBorders;
bool m_PreviewZoom;
float m_MouseWScale = 1.0f; // Mouse (i.e. UI) scale relative to the World (selected Group)
float m_MouseX = 0.0f;
float m_MouseY = 0.0f;
float m_MouseWorldX = 0.0f;

View file

@ -184,6 +184,10 @@ 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);
CMapItemGroupEx GItemEx;
GItemEx.m_Version = CMapItemGroupEx::CURRENT_VERSION;
GItemEx.m_ParallaxZoom = pGroup->m_ParallaxZoom;
for(const auto &pLayer : pGroup->m_vpLayers)
{
if(pLayer->m_Type == LAYERTYPE_TILES)
@ -331,7 +335,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
}
}
df.AddItem(MAPITEMTYPE_GROUP, GroupCount++, sizeof(GItem), &GItem);
df.AddItem(MAPITEMTYPE_GROUP, GroupCount, sizeof(GItem), &GItem);
df.AddItem(MAPITEMTYPE_GROUP_EX, GroupCount, sizeof(GItemEx), &GItemEx);
GroupCount++;
}
// save envelopes
@ -581,9 +587,15 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
int Start, Num;
DataFile.GetType(MAPITEMTYPE_GROUP, &Start, &Num);
int StartEx, NumEx;
DataFile.GetType(MAPITEMTYPE_GROUP_EX, &StartEx, &NumEx);
for(int g = 0; g < Num; g++)
{
CMapItemGroup *pGItem = (CMapItemGroup *)DataFile.GetItem(Start + g, nullptr, nullptr);
CMapItemGroupEx *pGItemEx = nullptr;
if(NumEx)
pGItemEx = (CMapItemGroupEx *)DataFile.GetItem(StartEx + g, nullptr, nullptr);
if(pGItem->m_Version < 1 || pGItem->m_Version > CMapItemGroup::CURRENT_VERSION)
continue;
@ -607,6 +619,9 @@ 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 = GetParallaxZoom(pGItem, pGItemEx);
pGroup->m_CustomParallaxZoom = pGroup->m_ParallaxZoom != GetParallaxZoomDefault(pGroup->m_ParallaxX, pGroup->m_ParallaxY);
for(int l = 0; l < pGItem->m_NumLayers; l++)
{
CLayer *pLayer = nullptr;

View file

@ -320,6 +320,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext)
PROP_POS_Y,
PROP_PARA_X,
PROP_PARA_Y,
PROP_CUSTOM_ZOOM,
PROP_PARA_ZOOM,
PROP_USE_CLIPPING,
PROP_CLIP_X,
PROP_CLIP_Y,
@ -334,6 +336,8 @@ 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},
{"Custom Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom, PROPTYPE_BOOL, 0, 1},
{"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 +368,13 @@ 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_CUSTOM_ZOOM)
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = NewVal;
else if(Prop == PROP_PARA_ZOOM)
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = 1;
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)
@ -378,6 +389,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext)
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipW = NewVal;
else if(Prop == PROP_CLIP_H)
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipH = NewVal;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->OnEdited();
}
return 0;

View file

@ -3,6 +3,7 @@
#include "layers.h"
#include "mapitems.h"
#include "mapitems_ex.h"
#include <engine/map.h>
@ -10,9 +11,12 @@ CLayers::CLayers()
{
m_GroupsNum = 0;
m_GroupsStart = 0;
m_GroupsExNum = 0;
m_GroupsExStart = 0;
m_LayersNum = 0;
m_LayersStart = 0;
m_pGameGroup = 0;
m_pGameGroupEx = 0;
m_pGameLayer = 0;
m_pMap = 0;
@ -27,6 +31,7 @@ void CLayers::Init(class IKernel *pKernel)
{
m_pMap = pKernel->RequestInterface<IMap>();
m_pMap->GetType(MAPITEMTYPE_GROUP, &m_GroupsStart, &m_GroupsNum);
m_pMap->GetType(MAPITEMTYPE_GROUP_EX, &m_GroupsExStart, &m_GroupsExNum);
m_pMap->GetType(MAPITEMTYPE_LAYER, &m_LayersStart, &m_LayersNum);
m_pTeleLayer = 0;
@ -38,6 +43,7 @@ void CLayers::Init(class IKernel *pKernel)
for(int g = 0; g < NumGroups(); g++)
{
CMapItemGroup *pGroup = GetGroup(g);
CMapItemGroupEx *pGroupEx = GetGroupEx(g);
for(int l = 0; l < pGroup->m_NumLayers; l++)
{
CMapItemLayer *pLayer = GetLayer(pGroup->m_StartLayer + l);
@ -50,6 +56,7 @@ void CLayers::Init(class IKernel *pKernel)
{
m_pGameLayer = pTilemap;
m_pGameGroup = pGroup;
m_pGameGroupEx = pGroupEx;
// make sure the game group has standard settings
m_pGameGroup->m_OffsetX = 0;
@ -66,6 +73,9 @@ void CLayers::Init(class IKernel *pKernel)
m_pGameGroup->m_ClipH = 0;
}
if(pGroupEx)
pGroupEx->m_ParallaxZoom = 100;
//break;
}
if(pTilemap->m_Flags & TILESLAYERFLAG_TELE)
@ -119,6 +129,7 @@ void CLayers::InitBackground(class IMap *pMap)
{
m_pMap = pMap;
m_pMap->GetType(MAPITEMTYPE_GROUP, &m_GroupsStart, &m_GroupsNum);
m_pMap->GetType(MAPITEMTYPE_GROUP_EX, &m_GroupsExStart, &m_GroupsExNum);
m_pMap->GetType(MAPITEMTYPE_LAYER, &m_LayersStart, &m_LayersNum);
//following is here to prevent crash using standard map as background
@ -131,6 +142,7 @@ void CLayers::InitBackground(class IMap *pMap)
for(int g = 0; g < NumGroups(); g++)
{
CMapItemGroup *pGroup = GetGroup(g);
CMapItemGroupEx *pGroupEx = GetGroupEx(g);
for(int l = 0; l < pGroup->m_NumLayers; l++)
{
CMapItemLayer *pLayer = GetLayer(pGroup->m_StartLayer + l);
@ -143,6 +155,7 @@ void CLayers::InitBackground(class IMap *pMap)
{
m_pGameLayer = pTilemap;
m_pGameGroup = pGroup;
m_pGameGroupEx = pGroupEx;
// make sure the game group has standard settings
m_pGameGroup->m_OffsetX = 0;
@ -158,6 +171,10 @@ void CLayers::InitBackground(class IMap *pMap)
m_pGameGroup->m_ClipW = 0;
m_pGameGroup->m_ClipH = 0;
}
if(pGroupEx)
pGroupEx->m_ParallaxZoom = 100;
//We don't care about tile layers.
}
}
@ -206,6 +223,15 @@ CMapItemGroup *CLayers::GetGroup(int Index) const
return static_cast<CMapItemGroup *>(m_pMap->GetItem(m_GroupsStart + Index, 0, 0));
}
CMapItemGroupEx *CLayers::GetGroupEx(int Index) const
{
// Map doesn't have GroupEx items or GroupEx indexes don't match groups for some other reason. Lets turn them off completely to be safe.
if(m_GroupsExNum != m_GroupsNum)
return nullptr;
return static_cast<CMapItemGroupEx *>(m_pMap->GetItem(m_GroupsExStart + Index, 0, 0));
}
CMapItemLayer *CLayers::GetLayer(int Index) const
{
return static_cast<CMapItemLayer *>(m_pMap->GetItem(m_LayersStart + Index, 0, 0));

View file

@ -7,6 +7,7 @@ class IKernel;
class IMap;
struct CMapItemGroup;
struct CMapItemGroupEx;
struct CMapItemLayer;
struct CMapItemLayerTilemap;
@ -14,9 +15,12 @@ class CLayers
{
int m_GroupsNum;
int m_GroupsStart;
int m_GroupsExNum;
int m_GroupsExStart;
int m_LayersNum;
int m_LayersStart;
CMapItemGroup *m_pGameGroup;
CMapItemGroupEx *m_pGameGroupEx;
CMapItemLayerTilemap *m_pGameLayer;
IMap *m_pMap;
@ -30,8 +34,10 @@ public:
int NumLayers() const { return m_LayersNum; }
IMap *Map() const { return m_pMap; }
CMapItemGroup *GameGroup() const { return m_pGameGroup; }
CMapItemGroupEx *GameGroupEx() const { return m_pGameGroupEx; }
CMapItemLayerTilemap *GameLayer() const { return m_pGameLayer; }
CMapItemGroup *GetGroup(int Index) const;
CMapItemGroupEx *GetGroupEx(int Index) const;
CMapItemLayer *GetLayer(int Index) const;
// DDRace

View file

@ -1,7 +1,21 @@
#include "mapitems_ex.h"
#include <base/math.h>
#include <engine/shared/uuid_manager.h>
int GetParallaxZoom(const CMapItemGroup *pGroup, const CMapItemGroupEx *pGroupEx)
{
if(pGroupEx)
return pGroupEx->m_ParallaxZoom;
return GetParallaxZoomDefault(pGroup->m_ParallaxX, pGroup->m_ParallaxY);
}
int GetParallaxZoomDefault(int ParallaxX, int ParallaxY)
{
return clamp(maximum(ParallaxX, ParallaxY), 0, 100);
}
void RegisterMapItemTypeUuids(CUuidManager *pManager)
{
#define UUID(id, name) pManager->RegisterName(id, name);

View file

@ -2,6 +2,8 @@
#define GAME_MAPITEMS_EX_H
#include <game/generated/protocol.h>
#include "mapitems.h"
enum
{
__MAPITEMTYPE_UUID_HELPER = OFFSET_MAPITEMTYPE_UUID - 1,
@ -43,5 +45,23 @@ struct CMapItemAutoMapperConfig
int m_Flags;
};
struct CMapItemGroupEx
{
enum
{
CURRENT_VERSION = 1
};
int m_Version;
// 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 CMapItemGroup *pGroup, const CMapItemGroupEx *pGroupEx);
int GetParallaxZoomDefault(int ParallaxX, int ParallaxY);
void RegisterMapItemTypeUuids(class CUuidManager *pManager);
#endif // GAME_MAPITEMS_EX_H

View file

@ -2,3 +2,4 @@
UUID(MAPITEMTYPE_TEST, "mapitemtype-test@ddnet.tw")
UUID(MAPITEMTYPE_AUTOMAPPER_CONFIG, "mapitemtype-automapper-config@ddnet.tw")
UUID(MAPITEMTYPE_GROUP_EX, "mapitemtype-group@ddnet.tw")