render parity unit J: 40250 automatic sight range in CPythonApplication::Process

Process now runs 40250's render-time / face-count block and sets view
distance set 0 each frame (25600 under 5000 faces, else scaled by render
time, or the forced range). SetForceSightRange and SkipRenderBuffering are
ported from stubs; the invented SetCursorPosition in UpdateGame is removed.
Port-map boilerplate notes replaced; Destroy recorded as DIVERGENT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-28 14:22:41 +09:00
co-authored by Claude Opus 5.5
parent 0e8a1574cb
commit dda885a17a
3 changed files with 115 additions and 19 deletions
+1
View File
@@ -567,3 +567,4 @@
{"date": "2026-09-28T04:56:02+00:00", "unit": "GameLib/MapOutdoorRenderHTP.cpp", "action": "unit H1: platform rewrite replaced by the full 40250 source (native-terrain gate only); drops the invented texture-0 fallback splat, restores first-splat stage-1 ALPHAOP DISABLE, GetNumTextures() loop bound, shadow-pass FOGCOLOR white/restore, stage-0 ALPHAOP DISABLE + CLAMP, TEXTURE1 = m_matDynamicShadow, stage-1 SELECTARG1 restore, TEXTURE0 1/640 tiling", "evidence": "ctest (see commit); fake-server frame 900: white far band gone, terrain matches STP-path layout (mean abs diff vs STP ~20/255 from per-texture m_matTransform vs STP's fixed 1/640, as in 40250); HTP pixels NEEDS_LIVE"}
{"date": "2026-09-28T04:59:25+00:00", "unit": "GameLib/MapOutdoorWater.cpp", "action": "unit H2: platform rewrite replaced by the full 40250 source (native-terrain gate only): far-fog second water pass restored, invented LIGHTING=FALSE save/restore and empty-texture guard removed", "evidence": "build ok; fake-server spawn frame 900 unchanged (mean abs diff ~0.4/255, no water in view); water visuals NEEDS_LIVE"}
{"date": "2026-09-28T05:08:46+00:00", "unit": "EterPythonLib/PythonGraphic.cpp", "action": "unit I: SetGamma, RenderCoolTimeBox, RenderUp/DownButton, GetAvailableMemory ported from 40250 (stubs removed); cooldown fan drawn as polygon UI commands; SetViewport/RestoreViewport documented as ADAPTED (device viewport + UI clip)", "evidence": "port.app_loop drawing_primitives checks fan geometry at fTime 0.25/0.3/1.0 and viewport clip push/pop; cooldown visuals NEEDS_LIVE"}
{"date": "2026-09-28T05:22:26+00:00", "unit": "UserInterface/PythonApplication.cpp", "action": "unit J: Process gains 40250's render-time/face-count block and automatic sight range (SetViewDistanceSet(0)); SetForceSightRange/SkipRenderBuffering ported from stubs; invented SetCursorPosition in UpdateGame removed; boilerplate notes on RenderGame/UpdateGame/Process/Destroy replaced (Destroy marked DIVERGENT: never called, teardown via PythonBoot::Stop)", "evidence": "ctest -j3 35/35; fake-server spawn frame 900 mean abs diff 0.33/255 vs previous (fog range not in view); sight-range visuals NEEDS_LIVE"}
@@ -25,8 +25,8 @@
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::SetMinFog"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"test": "extension/tests/port_login_flow_test.cpp",
"note": "40250 body verbatim in the platform layer."
},
"CPythonApplication::SetFrameSkip": {
"status": "ADAPTED",
@@ -73,32 +73,32 @@
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::RenderGame"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"test": "extension/tests/port_login_flow_test.cpp",
"note": "40250's !PERF_CHECKER_RENDER_GAME branch line for line, members reached through their singletons; the perf-timing branch (PERF_CHECKER_RENDER_GAME is FALSE) is left out."
},
"CPythonApplication::UpdateGame": {
"status": "ADAPTED",
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::UpdateGame"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"test": "extension/tests/port_login_flow_test.cpp",
"note": "40250 body through the singletons, minus the __PERFORMANCE_CHECK__/PERF_CHECKER timing. An invented CScreen::SetCursorPosition in the perspective block was removed (unit J): as in 40250 the pick ray comes from RenderGame's SetCursorPosition."
},
"CPythonApplication::SkipRenderBuffering": {
"status": "ADAPTED",
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::SkipRenderBuffering"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"test": "extension/tests/port_login_flow_test.cpp",
"note": "40250 body verbatim in the platform layer."
},
"CPythonApplication::Process": {
"status": "ADAPTED",
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::Process"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"test": "extension/tests/port_login_flow_test.cpp",
"note": "Frame order as 40250 (network, account connector, camera, resource, mouse/UI update, specular, culling, Begin/UI/mouse render/End). Left out: guild mark up/download (not ported), lost-device restore, ClearDepthBuffer/Show (host presents), frame-skip timing/Sleep (host paces frames), FPS counters, and the !IsActive() SkipRenderBuffering(3000) (CMSWindow::IsActive is a stub). Unit J restored 40250:741-842: render time, face count, frame buffering average and the automatic sight range (SetViewDistanceSet(0, ...): 25600 under 5000 faces, else MIN_FOG..25600 scaled by render time, or the forced range), with a PORT guard on the face-speed division. Fog/far-clip effect NEEDS_LIVE against the reference frame."
},
"CPythonApplication::UpdateClientRect": {
"status": "ADAPTED",
@@ -285,7 +285,7 @@
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::SetForceSightRange"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"note": "40250 body verbatim in the platform layer. m_iForceSightRange lives in the file-static s_iForceSightRange (initial -1 as the 40250 constructor sets) until the application object exists.",
"test": "extension/tests/port_login_flow_test.cpp"
},
"CPythonApplication::Clear": {
@@ -297,12 +297,11 @@
"test": "extension/tests/port_login_flow_test.cpp"
},
"CPythonApplication::Destroy": {
"status": "ADAPTED",
"status": "DIVERGENT",
"impl": [
"extension/src/platform/UserInterface/PythonApplication.cpp:CPythonApplication::Destroy"
],
"note": "40250 CPythonApplication lifecycle and state adapted for platform host and singletons",
"test": "extension/tests/port_login_flow_test.cpp"
"note": "Empty and never called: the host tears down through the singleton resets in PythonBoot::Stop, not 40250 Main's app->Destroy(). 40250's body (window manager, SaveConfig/SaveInterfaceStatus writing into the client dir, managers incl. m_LightManager.Destroy(), device) is not run; porting it needs Stop to call it with config writes redirected out of the 40250 client dir."
}
}
}
@@ -70,6 +70,8 @@ float CPythonApplication::GetGlobalTime() { return CTimer::Instance().GetCurrent
// PORT: read straight from the timer, as GetGlobalTime above.
float CPythonApplication::GetGlobalElapsedTime() { return CTimer::Instance().GetElapsedSecond(); }
static bool s_isFrameSkipDisable = false;
// m_iForceSightRange (the 40250 constructor sets -1: automatic sight range).
static int s_iForceSightRange = -1;
void CPythonApplication::SetFrameSkip(bool isEnable) { s_isFrameSkipDisable = !isEnable; }
// PORT: 40250 creates the Win32 window, the D3D8 device, cursors, sound, keyboard, the net device,
// fonts and the IME here. The Godot window and its canvas already exist when the host starts the
@@ -261,7 +263,6 @@ void CPythonApplication::UpdateGame()
s.SetPerspective(30.0f,fAspect, 100.0f, fFarClip);
s.BuildViewFrustum();
s.SetCursorPosition(ptMouse.x, ptMouse.y, UI::CWindowManager::Instance().GetScreenWidth(), UI::CWindowManager::Instance().GetScreenHeight());
}
TPixelPosition kPPosMainActor;
@@ -315,8 +316,11 @@ bool CPythonApplication::Process()
if (IsNativeTerrainRenderEnabled())
CGrannyMaterial::TranslateSpecularMatrix(g_specularSpd, g_specularSpd, 0.0f);
// PORT: the render block without the lost-device restore, ClearDepthBuffer, Show and the render-time
// statistics (Godot clears and presents the frame). The frame's UI and 3D command lists restart here.
// PORT: the render block without the lost-device restore, ClearDepthBuffer and Show (Godot clears
// and presents the frame), and without the !IsActive() SkipRenderBuffering(3000): the host has no
// Win32 activation (CMSWindow::IsActive is a stub). The frame's UI and 3D command lists restart here.
// 40250 PythonApplication.cpp:681.
DWORD dwRenderStartTime = ELTimer_GetMSec();
CCullingManager::Instance().Update();
UIRenderBeginFrame();
Render3DBeginFrame();
@@ -336,6 +340,93 @@ bool CPythonApplication::Process()
}
rkGraphic.End();
// 40250 PythonApplication.cpp:741-842 (render time, face count, automatic sight range), minus the
// per-second FPS/face counters and the windowed dwBufRenderTime rounding, whose result 40250 never
// uses (its Sleep is commented out).
DWORD dwRenderEndTime = ELTimer_GetMSec();
static DWORD s_dwRenderCheckTime = dwRenderEndTime;
static DWORD s_dwRenderRangeTime = 0;
static DWORD s_dwRenderRangeFrame = 0;
m_dwCurRenderTime = dwRenderEndTime - dwRenderStartTime;
s_dwRenderRangeTime += m_dwCurRenderTime;
++s_dwRenderRangeFrame;
if (dwRenderEndTime-s_dwRenderCheckTime>1000)
{
m_fAveRenderTime=float(double(s_dwRenderRangeTime)/double(s_dwRenderRangeFrame));
s_dwRenderCheckTime=ELTimer_GetMSec();
s_dwRenderRangeTime=0;
s_dwRenderRangeFrame=0;
}
DWORD dwCurFaceCount=rkGraphic.GetFaceCount();
rkGraphic.ResetFaceCount();
if (dwCurFaceCount > 5000)
{
// 프레임 완충 처리
if (dwRenderEndTime > m_dwBufSleepSkipTime)
{
static float s_fBufRenderTime = 0.0f;
float fCurRenderTime = m_dwCurRenderTime;
if (fCurRenderTime > s_fBufRenderTime)
{
float fRatio = fMAX(0.5f, (fCurRenderTime - s_fBufRenderTime) / 30.0f);
s_fBufRenderTime = (s_fBufRenderTime * (100.0f - fRatio) + (fCurRenderTime + 5) * fRatio) / 100.0f;
}
else
{
float fRatio = 0.5f;
s_fBufRenderTime = (s_fBufRenderTime * (100.0f - fRatio) + fCurRenderTime * fRatio) / 100.0f;
}
// 한계치를 정한다
if (s_fBufRenderTime > 100.0f)
s_fBufRenderTime = 100.0f;
m_fAveRenderTime=s_fBufRenderTime;
}
m_dwFaceAccCount += dwCurFaceCount;
m_dwFaceAccTime += m_dwCurRenderTime;
// PORT: recording a frame can take under 1 ms, so the integer division is guarded (40250's
// D3D frame never took 0 ms).
if (m_dwFaceAccTime)
m_fFaceSpd=(m_dwFaceAccCount/m_dwFaceAccTime);
// 거리 자동 조절
if (-1 == s_iForceSightRange)
{
static float s_fAveRenderTime = 16.0f;
float fRatio=0.3f;
s_fAveRenderTime=(s_fAveRenderTime*(100.0f-fRatio)+max(16.0f, m_dwCurRenderTime)*fRatio)/100.0f;
float fFar=25600.0f;
float fNear=MIN_FOG;
double dbAvePow=double(1000.0f/s_fAveRenderTime);
double dbMaxPow=60.0;
float fDistance=max(fNear+(fFar-fNear)*(dbAvePow)/dbMaxPow, fNear);
CPythonBackground::Instance().SetViewDistanceSet(0, fDistance);
}
// 거리 강제 설정시
else
{
CPythonBackground::Instance().SetViewDistanceSet(0, float(s_iForceSightRange));
}
}
else
{
// 10000 폴리곤 보다 적을때는 가장 멀리 보이게 한다
CPythonBackground::Instance().SetViewDistanceSet(0, 25600.0f);
}
}
return true;
}
@@ -483,7 +574,11 @@ time_t CPythonApplication::GetServerTime() { return GetPlatformServerTime(); }
time_t CPythonApplication::GetServerTimeStamp() { return GetPlatformServerTimeStamp(); }
void CPythonApplication::SetConnectData(const char*, int) { MT_PLATFORM_STUB(); }
void CPythonApplication::GetConnectData(std::string& ip, int& port) { MT_PLATFORM_STUB(); ip.clear(); port = 0; }
void CPythonApplication::SetForceSightRange(int) { MT_PLATFORM_STUB(); }
// 40250 PythonApplication.cpp:1377.
void CPythonApplication::SetForceSightRange(int iRange)
{
s_iForceSightRange = iRange;
}
int CPythonApplication::CheckDeviceState()
{
@@ -506,9 +601,10 @@ void CPythonApplication::NotifyHack(const char* c_szFormat, ...)
CPythonNetworkStream::Instance().NotifyHack(szBuf);
}
// 40250 PythonApplication.cpp:394
void CPythonApplication::SkipRenderBuffering(DWORD dwSleepMSec)
{
MT_PLATFORM_STUB();
m_dwBufSleepSkipTime=ELTimer_GetMSec()+dwSleepMSec;
}
void CPythonApplication::UpdateClientRect()