From 6fd4c53a018a7e746d50635b1c74e713b8145119 Mon Sep 17 00:00:00 2001 From: bolrog <81111887+bolrog@users.noreply.github.com> Date: Tue, 25 May 2021 21:42:29 +0200 Subject: [PATCH] Fix motion prediction of shadows, rework how MoP is applied to units a bit, and clean up. --- src/d2dx/D2DXContext.cpp | 54 +++++++++++++++------------ src/d2dx/D2DXContext.h | 5 ++- src/d2dx/Detours.cpp | 20 ++++++---- src/d2dx/ID2InterceptionHandler.h | 6 ++- src/d2dx/UnitMotionPredictor.cpp | 61 ++++++++++++++++++++++++++++--- src/d2dx/UnitMotionPredictor.h | 14 ++++++- 6 files changed, 119 insertions(+), 41 deletions(-) diff --git a/src/d2dx/D2DXContext.cpp b/src/d2dx/D2DXContext.cpp index 7562966..7ecb384 100644 --- a/src/d2dx/D2DXContext.cpp +++ b/src/d2dx/D2DXContext.cpp @@ -413,9 +413,7 @@ void D2DXContext::OnBufferSwap() if (IsFeatureEnabled(Feature::UnitMotionPrediction) && _majorGameState == MajorGameState::InGame) { - const OffsetF offset = _unitMotionPredictor.GetOffset(_gameHelper->GetPlayerUnit()); - const OffsetF scaleFactors{ 32.0f / sqrtf(2.0f), 16.0f / sqrtf(2.0f) }; - const OffsetF screenOffsetf = scaleFactors * OffsetF{ offset.x - offset.y, offset.x + offset.y } + 0.5f; + const Offset offset = _unitMotionPredictor.GetOffset(_gameHelper->GetPlayerUnit()); for (uint32_t i = 0; i < _batchCount; ++i) { @@ -430,8 +428,8 @@ void D2DXContext::OnBufferSwap() for (uint32_t j = 0; j < batchVertexCount; ++j) { _vertices.items[vertexIndex++].AddOffset( - (int32_t)-screenOffsetf.x, - (int32_t)-screenOffsetf.y); + -offset.x, + -offset.y); } } } @@ -898,17 +896,6 @@ void D2DXContext::OnDrawVertexArrayContiguous( return; } - Offset screenOffset = { 0,0 }; - if (_majorGameState == MajorGameState::InGame) - { - if (currentlyDrawingUnit && currentlyDrawingUnit != _gameHelper->GetPlayerUnit()) - { - auto drawOffset = _unitMotionPredictor.GetOffset(currentlyDrawingUnit); - screenOffset.x = (int32_t)(32.0f * (drawOffset.x - drawOffset.y) / sqrt(2.0f) + 0.5f); - screenOffset.y = (int32_t)(16.0f * (drawOffset.x + drawOffset.y) / sqrt(2.0f) + 0.5f); - } - } - Batch batch = PrepareBatchForSubmit(_scratchBatch, PrimitiveType::Triangles, 6, gameContext); if (!batch.IsValid()) @@ -929,7 +916,7 @@ void D2DXContext::OnDrawVertexArrayContiguous( for (int32_t i = 0; i < 4; ++i) { - v.SetPosition((int32_t)d2Vertices[i].x + screenOffset.x, (int32_t)d2Vertices[i].y + screenOffset.y); + v.SetPosition((int32_t)d2Vertices[i].x, (int32_t)d2Vertices[i].y); v.SetTexcoord((int32_t)d2Vertices[i].s >> _glideState.stShift, (int32_t)d2Vertices[i].t >> _glideState.stShift); v.SetColor(maskedConstantColor | (d2Vertices[i].color & iteratedColorMask)); pVertices[i] = v; @@ -1326,31 +1313,50 @@ void D2DXContext::EndDrawText() } _Use_decl_annotations_ -void D2DXContext::BeginDrawImage( +Offset D2DXContext::BeginDrawImage( const D2::CellContext* cellContext, uint32_t drawMode, - Offset pos) + Offset pos, + D2Function d2Function) { + Offset offset{ 0,0 }; + if (_isDrawingText) { - return; + return offset; } if (currentlyDrawingUnit) { + _unitMotionPredictor.SetUnitScreenPos(currentlyDrawingUnit, pos.x, pos.y); + if (currentlyDrawingUnit == _gameHelper->GetPlayerUnit()) { // The player unit itself. _scratchBatch.SetTextureCategory(TextureCategory::Player); _playerScreenPos = pos; } + else + { + offset = _unitMotionPredictor.GetOffset(currentlyDrawingUnit); + } } else { - const bool isPlayerShadow = _playerScreenPos.x > 0 && max(abs(pos.x - _playerScreenPos.x), abs(pos.y - _playerScreenPos.y)) < 4; - if (isPlayerShadow) + if (d2Function == D2Function::D2Gfx_DrawShadow) { - _scratchBatch.SetTextureCategory(TextureCategory::Player); + const bool isPlayerShadow = + _playerScreenPos.x > 0 && + max(abs(pos.x - _playerScreenPos.x), abs(pos.y - _playerScreenPos.y)) < 8; + + if (isPlayerShadow) + { + _scratchBatch.SetTextureCategory(TextureCategory::Player); + } + else + { + offset = _unitMotionPredictor.GetOffsetForShadow(pos.x, pos.y); + } } else { @@ -1364,6 +1370,8 @@ void D2DXContext::BeginDrawImage( } } } + + return offset; } void D2DXContext::EndDrawImage() diff --git a/src/d2dx/D2DXContext.h b/src/d2dx/D2DXContext.h index 07ceec6..4f62aa5 100644 --- a/src/d2dx/D2DXContext.h +++ b/src/d2dx/D2DXContext.h @@ -194,10 +194,11 @@ namespace d2dx virtual void EndDrawText() override; - virtual void BeginDrawImage( + virtual Offset BeginDrawImage( _In_ const D2::CellContext* cellContext, _In_ uint32_t drawMode, - _In_ Offset pos) override; + _In_ Offset pos, + _In_ D2Function d2Function) override; virtual void EndDrawImage() override; diff --git a/src/d2dx/Detours.cpp b/src/d2dx/Detours.cpp index 5034019..0b92b6c 100644 --- a/src/d2dx/Detours.cpp +++ b/src/d2dx/Detours.cpp @@ -315,12 +315,14 @@ void __stdcall D2Gfx_DrawImage_Hooked( auto d2InterceptionHandler = GetD2InterceptionHandler(); + Offset offset{ 0,0 }; + if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, nDrawMode, { nXpos, nYpos }); + offset = d2InterceptionHandler->BeginDrawImage(cellContext, nDrawMode, { nXpos, nYpos }, D2Function::D2Gfx_DrawImage); } - D2Gfx_DrawImage_Real(cellContext, nXpos, nYpos, dwGamma, nDrawMode, pPalette); + D2Gfx_DrawImage_Real(cellContext, nXpos + offset.x, nYpos + offset.y, dwGamma, nDrawMode, pPalette); if (d2InterceptionHandler) { @@ -339,7 +341,7 @@ void __stdcall D2Gfx_DrawClippedImage_Hooked( if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }); + d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }, D2Function::D2Gfx_DrawClippedImage); } D2Gfx_DrawClippedImage_Real(cellContext, nXpos, nYpos, pCropRect, nDrawMode); @@ -378,7 +380,7 @@ void __stdcall D2Gfx_DrawShiftedImage_Hooked( if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }); + d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }, D2Function::D2Gfx_DrawShiftedImage); } D2Gfx_DrawShiftedImage_Real(cellContext, nXpos, nYpos, dwGamma, nDrawMode, nGlobalPaletteShift); @@ -410,7 +412,7 @@ void __stdcall D2Gfx_DrawVerticalCropImage_Hooked( if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }); + d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)nDrawMode, { nXpos, nYpos }, D2Function::D2Gfx_DrawVerticalCropImage); } D2Gfx_DrawVerticalCropImage_Real(cellContext, nXpos, nYpos, nSkipLines, nDrawLines, nDrawMode); @@ -431,7 +433,7 @@ void __stdcall D2Gfx_DrawImageFast_Hooked( if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)-1, { nXpos, nYpos }); + d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)-1, { nXpos, nYpos }, D2Function::D2Gfx_DrawImageFast); } D2Gfx_DrawImageFast_Real(cellContext, nXpos, nYpos, nPaletteIndex); @@ -458,12 +460,14 @@ void __stdcall D2Gfx_DrawShadow_Hooked( auto d2InterceptionHandler = GetD2InterceptionHandler(); + Offset offset{ 0,0 }; + if (d2InterceptionHandler) { - d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)-1, { nXpos, nYpos }); + offset = d2InterceptionHandler->BeginDrawImage(cellContext, (uint32_t)-1, { nXpos, nYpos }, D2Function::D2Gfx_DrawShadow); } - D2Gfx_DrawShadow_Real(cellContext, nXpos, nYpos); + D2Gfx_DrawShadow_Real(cellContext, nXpos + offset.x, nYpos + offset.y); if (d2InterceptionHandler) { diff --git a/src/d2dx/ID2InterceptionHandler.h b/src/d2dx/ID2InterceptionHandler.h index 226ae7d..634c118 100644 --- a/src/d2dx/ID2InterceptionHandler.h +++ b/src/d2dx/ID2InterceptionHandler.h @@ -18,6 +18,7 @@ */ #pragma once +#include "IGameHelper.h" #include "Types.h" #include "D2Types.h" @@ -34,10 +35,11 @@ namespace d2dx virtual void EndDrawText() = 0; - virtual void BeginDrawImage( + virtual Offset BeginDrawImage( _In_ const D2::CellContext* cellContext, _In_ uint32_t drawMode, - _In_ Offset pos) = 0; + _In_ Offset pos, + _In_ D2Function d2Function) = 0; virtual void EndDrawImage() = 0; }; diff --git a/src/d2dx/UnitMotionPredictor.cpp b/src/d2dx/UnitMotionPredictor.cpp index 047f97a..d7f5ae0 100644 --- a/src/d2dx/UnitMotionPredictor.cpp +++ b/src/d2dx/UnitMotionPredictor.cpp @@ -27,7 +27,8 @@ UnitMotionPredictor::UnitMotionPredictor( const std::shared_ptr& gameHelper) : _gameHelper{ gameHelper }, _unitIdAndTypes{ 1024, true }, - _unitMotions{ 1024, true } + _unitMotions{ 1024, true }, + _unitScreenPositions{ 1024, true } { } @@ -157,7 +158,7 @@ void UnitMotionPredictor::Update( } _Use_decl_annotations_ -OffsetF UnitMotionPredictor::GetOffset( +Offset UnitMotionPredictor::GetOffset( const D2::UnitAny* unit) { int32_t unitIndex = -1; @@ -194,9 +195,59 @@ OffsetF UnitMotionPredictor::GetOffset( if (unitIndex < 0) { - return { 0.0f, 0.0f }; + return { 0, 0 }; } - UnitMotion& um = _unitMotions.items[unitIndex]; - return { (um.predictedPos.x - um.lastPos.x) / 65536.0f, (um.predictedPos.y - um.lastPos.y) / 65536.0f }; + return _unitMotions.items[unitIndex].GetOffset(); +} + +_Use_decl_annotations_ +void UnitMotionPredictor::SetUnitScreenPos( + const D2::UnitAny* unit, + int32_t x, + int32_t y) +{ + auto unitId = _gameHelper->GetUnitId(unit); + auto unitType = _gameHelper->GetUnitType(unit); + + for (int32_t unitIndex = 0; unitIndex < _unitsCount; ++unitIndex) + { + if (_unitIdAndTypes.items[unitIndex].unitId == (uint16_t)unitId && + _unitIdAndTypes.items[unitIndex].unitType == (uint16_t)unitType) + { + _unitScreenPositions.items[unitIndex] = { x, y }; + break; + } + } +} + +_Use_decl_annotations_ +Offset UnitMotionPredictor::GetOffsetForShadow( + _In_ int32_t x, + _In_ int32_t y) +{ + for (int32_t i = 0; i < _unitsCount; ++i) + { + if (!_unitIdAndTypes.items[i].unitId) + { + continue; + } + + const int32_t dist = max(abs(_unitScreenPositions.items[i].x - x), abs(_unitScreenPositions.items[i].y - y)); + + if (dist < 8) + { + return _unitMotions.items[i].GetOffset(); + } + } + + return { 0, 0 }; +} + +Offset UnitMotionPredictor::UnitMotion::GetOffset() const +{ + const OffsetF offset{ (predictedPos.x - lastPos.x) / 65536.0f, (predictedPos.y - lastPos.y) / 65536.0f }; + const OffsetF scaleFactors{ 32.0f / sqrtf(2.0f), 16.0f / sqrtf(2.0f) }; + const OffsetF screenOffset = scaleFactors * OffsetF{ offset.x - offset.y, offset.x + offset.y } + 0.5f; + return { (int32_t)screenOffset.x, (int32_t)screenOffset.y }; } diff --git a/src/d2dx/UnitMotionPredictor.h b/src/d2dx/UnitMotionPredictor.h index b1c2435..54b18c0 100644 --- a/src/d2dx/UnitMotionPredictor.h +++ b/src/d2dx/UnitMotionPredictor.h @@ -32,9 +32,18 @@ namespace d2dx void Update( _In_ IRenderContext* renderContext); - OffsetF GetOffset( + Offset GetOffset( _In_ const D2::UnitAny* unit); + void SetUnitScreenPos( + _In_ const D2::UnitAny* unit, + _In_ int32_t x, + _In_ int32_t y); + + Offset GetOffsetForShadow( + _In_ int32_t x, + _In_ int32_t y); + private: struct UnitIdAndType final { @@ -44,6 +53,8 @@ namespace d2dx struct UnitMotion final { + Offset GetOffset() const; + uint32_t lastUsedFrame = 0; Offset lastPos = { 0, 0 }; Offset velocity = { 0, 0 }; @@ -56,6 +67,7 @@ namespace d2dx uint32_t _frame = 0; Buffer _unitIdAndTypes; Buffer _unitMotions; + Buffer _unitScreenPositions; int32_t _unitsCount = 0; }; }