From eaf744e48df5ca98e0d62d97794e3cf041add17d Mon Sep 17 00:00:00 2001 From: Koray Date: Sun, 15 Feb 2026 14:11:26 +0300 Subject: [PATCH 1/5] fix unknown encoding: utf-8-sig Fix for: Fatal Python error: init_import_site: Failed to import the site module Python runtime state: initialized Traceback (most recent call last): File "", line 1371, in _find_and_load File "", line 1342, in _find_and_load_unlocked File "", line 938, in _load_unlocked File "", line 1179, in exec_module File "", line 723, in File "", line 709, in main File "", line 382, in addusersitepackages File "", line 254, in addsitedir File "", line 197, in addpackage LookupError: unknown encoding: utf-8-sig --- src/PythonModules/frozen_modules.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PythonModules/frozen_modules.c b/src/PythonModules/frozen_modules.c index 9918f04..5875307 100644 --- a/src/PythonModules/frozen_modules.c +++ b/src/PythonModules/frozen_modules.c @@ -471,6 +471,7 @@ const struct _frozen _PyImport_FrozenModules[] = { {"encodings.latin_1", M_encodings__latin_1, 2747, 0}, {"encodings.mbcs", M_encodings__mbcs, 2240, 0}, {"encodings.utf_8", M_encodings__utf_8, 2300, 0}, + {"encodings.utf_8_sig", M_encodings__utf_8, 2300, 0}, {"enum", M_enum, 87996, 0}, {"filecmp", M_filecmp, 15279, 0}, {"fileinput", M_fileinput, 21106, 0}, From 219cf7e6248196ee6c5313a9877f612bb58c4a20 Mon Sep 17 00:00:00 2001 From: Koray Date: Sun, 15 Feb 2026 14:31:32 +0300 Subject: [PATCH 2/5] fix speedtree crash Unhandled exception at 0x00007FF6DBFC5EB1 (Metin2_Debug.exe) in metin2client__20260215_142707.dmp: 0xC0000005: Access violation reading location 0x0000000000000000. > Metin2_Debug.exe!LoadLeafShader(IDirect3DDevice9 * pDx, IDirect3DVertexDeclaration9 * & pVertexDecl, IDirect3DVertexShader9 * & pVertexShader) Line 290 C++ Metin2_Debug.exe!CSpeedTreeForestDirectX8::InitVertexShaders() Line 70 C++ Metin2_Debug.exe!CSpeedTreeForestDirectX8::SetRenderingDevice(IDirect3DDevice9 * lpDevice) Line 85 C++ Metin2_Debug.exe!CMapOutdoor::Load(float x, float y, float z) Line 38 C++ Metin2_Debug.exe!CMapManager::LoadMap(const std::string & c_rstrMapName, float x, float y, float z) Line 124 C++ Metin2_Debug.exe!CPythonBackground::Warp(unsigned long dwX, unsigned long dwY) Line 744 C++ Metin2_Debug.exe!CPythonNetworkStream::Warp(long lGlobalX, long lGlobalY) Line 374 C++ Metin2_Debug.exe!netWarp(_object * poSelf, _object * poArgs) Line 81 C++ [External Code] Metin2_Debug.exe!CPythonLauncher::RunFile(const char * c_szFileName) Line 319 C++ Metin2_Debug.exe!RunMainScript(CPythonLauncher & pyLauncher, const char * lpCmdLine) Line 246 C++ Metin2_Debug.exe!Main(HINSTANCE__ * hInstance, char * lpCmdLine) Line 302 C++ Metin2_Debug.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 329 C++ [External Code] [Frames may be missing, no binary loaded for kernel32.dll] [External Code] + leafVertexDecl 0x000000dff7aed768 {{Stream=0 Offset=0 Type=2 '\x2' ...}, {Stream=0 Offset=12 Type=4 '\x4' ...}, {Stream=...}, ...} const _D3DVERTEXELEMENT9[5] + pCode 0x0000000000000000 ID3DXBuffer * + pDx 0x000000dfbdbc37c0 {...} IDirect3DDevice9 * + pError 0x0000000000000000 ID3DXBuffer * + pVertexDecl 0x0000000000000000 IDirect3DVertexDeclaration9 * & + pVertexShader 0x0000000000000000 IDirect3DVertexShader9 * & --- src/SpeedTreeLib/VertexShaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpeedTreeLib/VertexShaders.h b/src/SpeedTreeLib/VertexShaders.h index 0f47ef5..2670803 100644 --- a/src/SpeedTreeLib/VertexShaders.h +++ b/src/SpeedTreeLib/VertexShaders.h @@ -287,7 +287,7 @@ static void LoadLeafShader(LPDIRECT3DDEVICE9 pDx, LPDIRECT3DVERTEXDECLARATION9& } } else { - TraceError("Failed to assemble leaf vertex shader. The error reported is [ %s ].", pError->GetBufferPointer()); + TraceError("Failed to assemble leaf vertex shader. The error reported is [ %s ].", pError ? pError->GetBufferPointer() : "unknown"); } if (FAILED(pDx->CreateVertexDeclaration(leafVertexDecl, &pVertexDecl))) { From acb0ac0af58d20efeea0baaaf80c586589413700 Mon Sep 17 00:00:00 2001 From: Koray Date: Sun, 15 Feb 2026 14:33:59 +0300 Subject: [PATCH 3/5] fix missing resource exception handling Unhandled exception at 0x00007FF9433E8B9C (KERNELBASE.dll) in metin2client__20260215_143044.dmp: 0xE06D7363: Microsoft C++ Exception (parameters: 0x0000000019930520, 0x0000001E1CF2CF80, 0x00007FF698014258, 0x00007FF696940000). [External Code] > Metin2_Debug.exe!std::filesystem::_Throw_fs_error(const char * _Op, const std::error_code & _Error, const std::filesystem::path & _Path1) Line 1863 C++ Metin2_Debug.exe!std::filesystem::exists(const std::filesystem::path & _Target) Line 3463 C++ Metin2_Debug.exe!CPackManager::IsExist(std::basic_string_view> path) Line 95 C++ Metin2_Debug.exe!CResourceManager::IsFileExist(const char * c_szFileName) Line 498 C++ Metin2_Debug.exe!CArea::__SetObjectInstance_SetBuilding(CArea::SObjectInstance * pObjectInstance, const CArea::SObjectData * c_pData, CProperty * pProperty) Line 599 C++ Metin2_Debug.exe!CArea::__SetObjectInstance(CArea::SObjectInstance * pObjectInstance, const CArea::SObjectData * c_pData) Line 479 C++ Metin2_Debug.exe!CArea::__Load_BuildObjectInstances() Line 449 C++ Metin2_Debug.exe!CArea::Load(const char * c_szPathName) Line 750 C++ Metin2_Debug.exe!CMapOutdoor::LoadArea(unsigned short wAreaCoordX, unsigned short wAreaCoordY, unsigned short wCellCoordX, unsigned short wCellCoordY) Line 173 C++ Metin2_Debug.exe!CMapOutdoor::Update(float fX, float fY, float fZ) Line 103 C++ Metin2_Debug.exe!CMapOutdoor::Load(float x, float y, float z) Line 40 C++ Metin2_Debug.exe!CMapManager::LoadMap(const std::string & c_rstrMapName, float x, float y, float z) Line 124 C++ Metin2_Debug.exe!CPythonBackground::Warp(unsigned long dwX, unsigned long dwY) Line 744 C++ Metin2_Debug.exe!CPythonNetworkStream::Warp(long lGlobalX, long lGlobalY) Line 374 C++ Metin2_Debug.exe!netWarp(_object * poSelf, _object * poArgs) Line 81 C++ [External Code] Metin2_Debug.exe!CPythonLauncher::RunFile(const char * c_szFileName) Line 319 C++ Metin2_Debug.exe!RunMainScript(CPythonLauncher & pyLauncher, const char * lpCmdLine) Line 246 C++ Metin2_Debug.exe!Main(HINSTANCE__ * hInstance, char * lpCmdLine) Line 302 C++ Metin2_Debug.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 329 C++ [External Code] + this 0x0000001e1cf2f6a0 {m_load_from_pack=true m_entries={ size=50983 } m_pBufferPool=0x0000001e1d03b350 {...} ...} const CPackManager * buf + path "d:/ymir work/zone/b/obj/general_obj_stone19_lod_01.gr2" std::basic_string_view> --- src/PackLib/PackManager.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/PackLib/PackManager.cpp b/src/PackLib/PackManager.cpp index b8ecf29..118a451 100644 --- a/src/PackLib/PackManager.cpp +++ b/src/PackLib/PackManager.cpp @@ -2,6 +2,7 @@ #include "EterLib/BufferPool.h" #include #include +#include "EterBase/Debug.h" CPackManager::CPackManager() : m_load_from_pack(true) @@ -92,7 +93,15 @@ bool CPackManager::IsExist(std::string_view path) const } // Fallback to disk (for files not in packs, like bgm folder) - return std::filesystem::exists(buf); + std::error_code ec; // To avoid exceptions from std::filesystem + const auto result = std::filesystem::exists(buf, ec); + if (ec) + { + TraceError("std::filesystem::exists failed for path '%s' with error: %s", buf.c_str(), ec.message().c_str()); + return false; + } + + return result; } void CPackManager::NormalizePath(std::string_view in, std::string& out) const From cfe3c3cb7cf7a6ab07612228740790bbaad6c95c Mon Sep 17 00:00:00 2001 From: Koray Date: Sun, 15 Feb 2026 15:40:54 +0300 Subject: [PATCH 4/5] Fix SpeedTree LOD strip handling to render all strips per LOD --- src/SpeedTreeLib/SpeedTreeWrapper.cpp | 203 +++++++++++++++++--------- src/SpeedTreeLib/SpeedTreeWrapper.h | 14 +- 2 files changed, 144 insertions(+), 73 deletions(-) diff --git a/src/SpeedTreeLib/SpeedTreeWrapper.cpp b/src/SpeedTreeLib/SpeedTreeWrapper.cpp index b7a01b5..4a2ff3b 100644 --- a/src/SpeedTreeLib/SpeedTreeWrapper.cpp +++ b/src/SpeedTreeLib/SpeedTreeWrapper.cpp @@ -1,4 +1,4 @@ -/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// // CSpeedTreeWrapper Class // // (c) 2003 IDV, Inc. @@ -65,10 +65,8 @@ m_bIsInstance(false), m_pInstanceOf(NULL), m_pGeometryCache(NULL), m_usNumLeafLods(0), -m_pBranchIndexCounts(NULL), m_pBranchIndexBuffer(NULL), m_pBranchVertexBuffer(NULL), -m_pFrondIndexCounts(NULL), m_pFrondIndexBuffer(NULL), m_pFrondVertexBuffer(NULL), m_pLeafVertexBuffer(NULL), @@ -101,7 +99,7 @@ void CSpeedTreeWrapper::OnRenderPCBlocker() CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); - // �ϳ��� ������ �� ���� LOD ������� ���� + // �ϳ��� ������ �� ���� LOD ������� ���� m_pSpeedTree->SetLodLevel(1.0f); //Advance(); @@ -244,7 +242,7 @@ void CSpeedTreeWrapper::OnRender() CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); - // �ϳ��� ������ �� ���� LOD ������� ���� + // �ϳ��� ������ �� ���� LOD ������� ���� m_pSpeedTree->SetLodLevel(1.0f); //Advance(); @@ -314,14 +312,12 @@ CSpeedTreeWrapper::~CSpeedTreeWrapper() { SAFE_RELEASE(m_pBranchVertexBuffer); SAFE_RELEASE(m_pBranchIndexBuffer); - SAFE_DELETE_ARRAY(m_pBranchIndexCounts); } if (m_unFrondVertexCount > 0) { SAFE_RELEASE(m_pFrondVertexBuffer); SAFE_RELEASE(m_pFrondIndexBuffer); - SAFE_DELETE_ARRAY(m_pFrondIndexCounts); } for (short i = 0; i < m_usNumLeafLods; ++i) @@ -544,32 +540,58 @@ void CSpeedTreeWrapper::SetupBranchBuffers(void) m_pBranchVertexBuffer->Unlock(); } - // create and fill the index counts for each LOD - UINT unNumLodLevels = m_pSpeedTree->GetNumBranchLodLevels(); - m_pBranchIndexCounts = new unsigned short[unNumLodLevels]; - for (UINT i = 0; i < unNumLodLevels; ++i) - { - // force update for particular LOD - m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, i); - - // check if this LOD has branches - if (pBranches->m_usNumStrips > 0) - m_pBranchIndexCounts[i] = pBranches->m_pStripLengths[0]; - else - m_pBranchIndexCounts[i] = 0; - } - // set back to highest LOD + const uint32_t unNumLodLevels = m_pSpeedTree->GetNumBranchLodLevels(); + m_branchStripOffsets.clear(); + m_branchStripLengths.clear(); + if (unNumLodLevels > 0) + m_branchStripLengths.resize(unNumLodLevels); + + // set LOD0 for strip offsets/index buffer sizing m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, 0); - - // the first LOD level contains the most indices of all the levels, so - // we use its size to allocate the index buffer - ms_lpd3dDevice->CreateIndexBuffer(m_pBranchIndexCounts[0] * sizeof(unsigned short), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pBranchIndexBuffer, NULL); - - // fill the index buffer - unsigned short* pIndexBuffer = NULL; - m_pBranchIndexBuffer->Lock(0, 0, reinterpret_cast(&pIndexBuffer), 0); - memcpy(pIndexBuffer, pBranches->m_pStrips[0], pBranches->m_pStripLengths[0] * sizeof(unsigned short)); - m_pBranchIndexBuffer->Unlock(); + const uint32_t stripCount = pBranches->m_usNumStrips; + uint32_t totalIndexCount = 0; + if (stripCount > 0) + { + m_branchStripOffsets.resize(stripCount); + for (uint32_t s = 0; s < stripCount; ++s) + { + m_branchStripOffsets[s] = totalIndexCount; + totalIndexCount += pBranches->m_pStripLengths[s]; + } + } + + for (uint32_t i = 0; i < unNumLodLevels; ++i) + { + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, i); + auto& lengths = m_branchStripLengths[i]; + lengths.assign(stripCount, 0); + const uint32_t lodStripCount = pBranches->m_usNumStrips; + for (uint32_t s = 0; s < stripCount && s < lodStripCount; ++s) + { + lengths[s] = pBranches->m_pStripLengths[s]; + } + } + // set back to highest LOD for buffer fill + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, 0); + + if (totalIndexCount > 0) + { + // the first LOD level contains the most indices of all the levels, so + // we use its size to allocate the index buffer + ms_lpd3dDevice->CreateIndexBuffer(totalIndexCount * sizeof(uint16_t), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pBranchIndexBuffer, NULL); + + // fill the index buffer + uint16_t* pIndexBuffer = NULL; + m_pBranchIndexBuffer->Lock(0, 0, reinterpret_cast(&pIndexBuffer), 0); + uint32_t cursor = 0; + for (uint32_t s = 0; s < stripCount; ++s) + { + const uint32_t length = pBranches->m_pStripLengths[s]; + memcpy(pIndexBuffer + cursor, pBranches->m_pStrips[s], length * sizeof(uint16_t)); + cursor += length; + } + m_pBranchIndexBuffer->Unlock(); + } } } @@ -629,32 +651,58 @@ void CSpeedTreeWrapper::SetupFrondBuffers(void) } m_pFrondVertexBuffer->Unlock(); - // create and fill the index counts for each LOD - UINT unNumLodLevels = m_pSpeedTree->GetNumFrondLodLevels(); - m_pFrondIndexCounts = new unsigned short[unNumLodLevels]; - for (WORD j = 0; j < unNumLodLevels; ++j) + const uint32_t unNumLodLevels = m_pSpeedTree->GetNumFrondLodLevels(); + m_frondStripOffsets.clear(); + m_frondStripLengths.clear(); + if (unNumLodLevels > 0) + m_frondStripLengths.resize(unNumLodLevels); + + // set LOD0 for strip offsets/index buffer sizing + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, 0); + const uint32_t stripCount = pFronds->m_usNumStrips; + uint32_t totalIndexCount = 0; + if (stripCount > 0) { - // force update for this LOD - m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, j); - - // check if this LOD has fronds - if (pFronds->m_usNumStrips > 0) - m_pFrondIndexCounts[j] = pFronds->m_pStripLengths[0]; - else - m_pFrondIndexCounts[j] = 0; + m_frondStripOffsets.resize(stripCount); + for (uint32_t s = 0; s < stripCount; ++s) + { + m_frondStripOffsets[s] = totalIndexCount; + totalIndexCount += pFronds->m_pStripLengths[s]; + } } - // go back to highest LOD + + for (uint32_t j = 0; j < unNumLodLevels; ++j) + { + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, j); + auto& lengths = m_frondStripLengths[j]; + lengths.assign(stripCount, 0); + const uint32_t lodStripCount = pFronds->m_usNumStrips; + for (uint32_t s = 0; s < stripCount && s < lodStripCount; ++s) + { + lengths[s] = pFronds->m_pStripLengths[s]; + } + } + // go back to highest LOD for buffer fill m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, 0); - // the first LOD level contains the most indices of all the levels, so - // we use its size to allocate the index buffer - ms_lpd3dDevice->CreateIndexBuffer(m_pFrondIndexCounts[0] * sizeof(unsigned short), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pFrondIndexBuffer, NULL); - - // fill the index buffer - unsigned short * pIndexBuffer = NULL; - m_pFrondIndexBuffer->Lock(0, 0, reinterpret_cast(&pIndexBuffer), 0); - memcpy(pIndexBuffer, pFronds->m_pStrips[0], pFronds->m_pStripLengths[0] * sizeof(unsigned short)); - m_pFrondIndexBuffer->Unlock(); + if (totalIndexCount > 0) + { + // the first LOD level contains the most indices of all the levels, so + // we use its size to allocate the index buffer + ms_lpd3dDevice->CreateIndexBuffer(totalIndexCount * sizeof(uint16_t), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pFrondIndexBuffer, NULL); + + // fill the index buffer + uint16_t * pIndexBuffer = NULL; + m_pFrondIndexBuffer->Lock(0, 0, reinterpret_cast(&pIndexBuffer), 0); + uint32_t cursor = 0; + for (uint32_t s = 0; s < stripCount; ++s) + { + const uint32_t length = pFronds->m_pStripLengths[s]; + memcpy(pIndexBuffer + cursor, pFronds->m_pStrips[s], length * sizeof(uint16_t)); + cursor += length; + } + m_pFrondIndexBuffer->Unlock(); + } } } @@ -787,12 +835,14 @@ CSpeedTreeWrapper::SpeedTreeWrapperPtr CSpeedTreeWrapper::MakeInstance() // use the same buffers spInstance->m_pBranchIndexBuffer = m_pBranchIndexBuffer; - spInstance->m_pBranchIndexCounts = m_pBranchIndexCounts; + spInstance->m_branchStripOffsets = m_branchStripOffsets; + spInstance->m_branchStripLengths = m_branchStripLengths; spInstance->m_pBranchVertexBuffer = m_pBranchVertexBuffer; spInstance->m_unBranchVertexCount = m_unBranchVertexCount; spInstance->m_pFrondIndexBuffer = m_pFrondIndexBuffer; - spInstance->m_pFrondIndexCounts = m_pFrondIndexCounts; + spInstance->m_frondStripOffsets = m_frondStripOffsets; + spInstance->m_frondStripLengths = m_frondStripLengths; spInstance->m_pFrondVertexBuffer = m_pFrondVertexBuffer; spInstance->m_unFrondVertexCount = m_unFrondVertexCount; @@ -908,19 +958,27 @@ void CSpeedTreeWrapper::RenderBranches(void) const { m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry); - if (m_pGeometryCache->m_fBranchAlphaTestValue) + if (m_pGeometryCache->m_sBranches.m_usVertexCount > 0 && m_pBranchIndexBuffer && !m_branchStripLengths.empty() && !m_branchStripOffsets.empty()) { + const int lod = m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel; + if (lod < 0 || static_cast(lod) >= m_branchStripLengths.size()) + return; + PositionTree(); // set alpha test value STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fBranchAlphaTestValue)); - // render if this LOD has branches - if (m_pBranchIndexCounts && - m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] > 0) + const auto& lengths = m_branchStripLengths[lod]; + const size_t stripCount = lengths.size() < m_branchStripOffsets.size() ? lengths.size() : m_branchStripOffsets.size(); + for (size_t s = 0; s < stripCount; ++s) { - ms_faceCount += m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] - 2; - STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sBranches.m_usVertexCount, 0, m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] - 2); + const uint16_t stripLength = lengths[s]; + if (stripLength > 2) + { + ms_faceCount += stripLength - 2; + STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sBranches.m_usVertexCount, m_branchStripOffsets[s], stripLength - 2); + } } } } @@ -981,19 +1039,27 @@ void CSpeedTreeWrapper::RenderFronds(void) const { m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry); - if (m_pGeometryCache->m_fFrondAlphaTestValue > 0.0f) + if (m_pGeometryCache->m_sFronds.m_usVertexCount > 0 && m_pFrondIndexBuffer && !m_frondStripLengths.empty() && !m_frondStripOffsets.empty()) { + const int lod = m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel; + if (lod < 0 || static_cast(lod) >= m_frondStripLengths.size()) + return; + PositionTree(); // set alpha test value STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fFrondAlphaTestValue)); - // render if this LOD has fronds - if (m_pFrondIndexCounts && - m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] > 0) + const auto& lengths = m_frondStripLengths[lod]; + const size_t stripCount = lengths.size() < m_frondStripOffsets.size() ? lengths.size() : m_frondStripOffsets.size(); + for (size_t s = 0; s < stripCount; ++s) { - ms_faceCount += m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] - 2; - STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sFronds.m_usVertexCount, 0, m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] - 2); + const uint16_t stripLength = lengths[s]; + if (stripLength > 2) + { + ms_faceCount += stripLength - 2; + STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sFronds.m_usVertexCount, m_frondStripOffsets[s], stripLength - 2); + } } } } @@ -1455,3 +1521,4 @@ void CSpeedTreeWrapper::OnUpdateCollisionData(const CStaticCollisionDataVector * AddCollision(&CollisionData, &mat); } } + diff --git a/src/SpeedTreeLib/SpeedTreeWrapper.h b/src/SpeedTreeLib/SpeedTreeWrapper.h index 19315d9..5412836 100644 --- a/src/SpeedTreeLib/SpeedTreeWrapper.h +++ b/src/SpeedTreeLib/SpeedTreeWrapper.h @@ -1,4 +1,4 @@ -/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// // SpeedTreeRTExample Class // // (c) 2003 IDV, Inc. @@ -43,6 +43,7 @@ #include #include #include +#include #include "EterLib/GrpObjectInstance.h" #include "EterLib/GrpImageInstance.h" @@ -91,8 +92,8 @@ public: virtual void SetPosition(float x, float y, float z); virtual void CalculateBBox(); - virtual void OnRender(); // Render ÿ ޼ҵ, ׷ 䳪 Ư 쿡 Render θ - // ̿ܿ RenderBranches, RenderFronds ޼ҵ带 CSpeedTreeForest ȣѴ. + virtual void OnRender(); // Render ½Ã¿¡ ¸Þ¼Òµå, ±×·¯³ª ÇÁ¸®ºä³ª Ư¼öÇÑ °æ¿ì¿¡¸¸ Á÷Á¢ Render ÄÝÀ» ºÎ¸£¸ç + // ±× À̿ܿ¡´Â RenderBranches, RenderFronds µîÀÇ ¸Þ¼Òµå¸¦ CSpeedTreeForest¿¡¼­ È£ÃâÇÑ´Ù. virtual void OnBlendRender() {} virtual void OnRenderToShadowMap() {} virtual void OnRenderShadow() {} @@ -172,13 +173,15 @@ private: LPDIRECT3DVERTEXBUFFER9 m_pBranchVertexBuffer; // branch vertex buffer unsigned int m_unBranchVertexCount; // number of vertices in branches LPDIRECT3DINDEXBUFFER9 m_pBranchIndexBuffer; // branch index buffer - unsigned short* m_pBranchIndexCounts; // number of indexes per branch LOD level + std::vector m_branchStripOffsets; // strip start indices (LOD0 ordering) + std::vector> m_branchStripLengths; // [lod][strip] index counts // frond buffers LPDIRECT3DVERTEXBUFFER9 m_pFrondVertexBuffer; // frond vertex buffer unsigned int m_unFrondVertexCount; // number of vertices in frond LPDIRECT3DINDEXBUFFER9 m_pFrondIndexBuffer; // frond index buffer - unsigned short* m_pFrondIndexCounts; // number of indexes per frond LOD level + std::vector m_frondStripOffsets; // strip start indices (LOD0 ordering) + std::vector> m_frondStripLengths; // [lod][strip] index counts // leaf buffers unsigned short m_usNumLeafLods; // the number of leaf LODs @@ -205,3 +208,4 @@ private: }; #pragma warning(pop) + From cd2529ee1548d5d2e800ff91b1b4a3d2df1402b7 Mon Sep 17 00:00:00 2001 From: Koray Date: Sun, 15 Feb 2026 16:03:18 +0300 Subject: [PATCH 5/5] prevent leaf render AV and harden leaf shader setup for speedtree - Guard leaf LOD access in CSpeedTreeWrapper::RenderLeaves() to avoid out-of-bounds indexing of m_pLeafVertexBuffer and m_pLeavesUpdatedByCpu. - Skip leaf update/draw when LOD is invalid, inactive, empty, or has no vertex buffer. - Add early safety returns for missing leaf buffer arrays and zero LOD count. - Guard CSpeedTreeWrapper::EndLeafForTreeType() against null m_pLeavesUpdatedByCpu. - Add shader re-init path in wrapper render entrypoints via CSpeedTreeForestDirectX8::EnsureVertexShaders() when cached shader state is missing. - Introduce public EnsureVertexShaders() in SpeedTreeForestDirectX8 while keeping InitVertexShaders() private (resolves private-access compile error). - Fix leaf shader input declaration to emit dcl_texcoord2 v9 for both GPU wind and GPU leaf placement configurations. - Make LoadLeafShader() atomic: create new shader/decl first, then swap only if both succeed, preserving previous valid shader state on failure. - Improve leaf shader failure logs to include HRESULT for easier diagnosis. --- src/SpeedTreeLib/SpeedTreeForestDirectX8.h | 1 + src/SpeedTreeLib/SpeedTreeWrapper.cpp | 87 +++++++++++++++------- src/SpeedTreeLib/VertexShaders.h | 49 ++++++++---- 3 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/SpeedTreeLib/SpeedTreeForestDirectX8.h b/src/SpeedTreeLib/SpeedTreeForestDirectX8.h index 5cfa5f0..d6c9249 100644 --- a/src/SpeedTreeLib/SpeedTreeForestDirectX8.h +++ b/src/SpeedTreeLib/SpeedTreeForestDirectX8.h @@ -52,6 +52,7 @@ class CSpeedTreeForestDirectX8 : public CSpeedTreeForest, public CGraphicBase, p void Render(unsigned long ulRenderBitVector = Forest_RenderAll); bool SetRenderingDevice(LPDIRECT3DDEVICE9 pDevice); + bool EnsureVertexShaders() { return m_pDx ? InitVertexShaders() : false; } private: bool InitVertexShaders(); diff --git a/src/SpeedTreeLib/SpeedTreeWrapper.cpp b/src/SpeedTreeLib/SpeedTreeWrapper.cpp index 4a2ff3b..b902cff 100644 --- a/src/SpeedTreeLib/SpeedTreeWrapper.cpp +++ b/src/SpeedTreeLib/SpeedTreeWrapper.cpp @@ -91,6 +91,9 @@ void CSpeedTreeWrapper::SetVertexShaders(LPDIRECT3DVERTEXDECLARATION9 pBranchVer void CSpeedTreeWrapper::OnRenderPCBlocker() { + if (!ms_dwBranchVertexShader || !ms_pLeafVertexShaderDecl || !ms_pLeafVertexShader) + CSpeedTreeForestDirectX8::Instance().EnsureVertexShaders(); + if (ms_dwBranchVertexShader == 0) { ms_dwBranchVertexShader = LoadBranchShader(ms_lpd3dDevice); @@ -200,22 +203,25 @@ void CSpeedTreeWrapper::OnRenderPCBlocker() } RenderFronds(); - STATEMANAGER.SetVertexDeclaration(ms_pLeafVertexShaderDecl); - STATEMANAGER.SaveVertexShader(ms_pLeafVertexShader); - -// SetupLeafForTreeType(); + if (ms_pLeafVertexShaderDecl && ms_pLeafVertexShader) { - // pass leaf tables to shader -#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT - UploadLeafTables(c_nVertexShader_LeafTables); -#endif + STATEMANAGER.SetVertexDeclaration(ms_pLeafVertexShaderDecl); + STATEMANAGER.SaveVertexShader(ms_pLeafVertexShader); - if (!m_CompositeImageInstance.IsEmpty()) - STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); +// SetupLeafForTreeType(); + { + // pass leaf tables to shader +#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT + UploadLeafTables(c_nVertexShader_LeafTables); +#endif + + if (!m_CompositeImageInstance.IsEmpty()) + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + } + RenderLeaves(); + STATEMANAGER.RestoreVertexShader(); } - RenderLeaves(); EndLeafForTreeType(); - STATEMANAGER.RestoreVertexShader(); STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, FALSE); @@ -234,6 +240,9 @@ void CSpeedTreeWrapper::OnRenderPCBlocker() void CSpeedTreeWrapper::OnRender() { + if (!ms_dwBranchVertexShader || !ms_pLeafVertexShaderDecl || !ms_pLeafVertexShader) + CSpeedTreeForestDirectX8::Instance().EnsureVertexShaders(); + if (ms_dwBranchVertexShader == 0) { ms_dwBranchVertexShader = LoadBranchShader(ms_lpd3dDevice); @@ -280,13 +289,16 @@ void CSpeedTreeWrapper::OnRender() SetupFrondForTreeType(); RenderFronds(); - STATEMANAGER.SetVertexDeclaration(ms_pLeafVertexShaderDecl); - STATEMANAGER.SaveVertexShader(ms_pLeafVertexShader); - - SetupLeafForTreeType(); - RenderLeaves(); + if (ms_pLeafVertexShaderDecl && ms_pLeafVertexShader) + { + STATEMANAGER.SetVertexDeclaration(ms_pLeafVertexShaderDecl); + STATEMANAGER.SaveVertexShader(ms_pLeafVertexShader); + + SetupLeafForTreeType(); + RenderLeaves(); + STATEMANAGER.RestoreVertexShader(); + } EndLeafForTreeType(); - STATEMANAGER.RestoreVertexShader(); STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, FALSE); @@ -1114,6 +1126,11 @@ void CSpeedTreeWrapper::RenderLeaves(void) const { // update leaf geometry m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_LeafGeometry); + + if (!m_pLeafVertexBuffer || m_usNumLeafLods == 0) + return; + + const int maxLeafLod = static_cast(m_usNumLeafLods); // update the LOD level vertex arrays we need #if defined(WRAPPER_USE_GPU_LEAF_PLACEMENT) && defined(WRAPPER_USE_GPU_WIND) @@ -1126,9 +1143,18 @@ void CSpeedTreeWrapper::RenderLeaves(void) const // reference to leaf structure const CSpeedTreeRT::SGeometry::SLeaf* pLeaf = (i == 0) ? &m_pGeometryCache->m_sLeaves0 : &m_pGeometryCache->m_sLeaves1; int unLod = pLeaf->m_nDiscreteLodLevel; + + if (!pLeaf->m_bIsActive || pLeaf->m_usLeafCount == 0) + continue; + + if (unLod < 0 || unLod >= maxLeafLod) + continue; + + if (!m_pLeafVertexBuffer[unLod]) + continue; #if defined WRAPPER_USE_GPU_LEAF_PLACEMENT - if (pLeaf->m_bIsActive && !m_pLeavesUpdatedByCpu[unLod]) + if (m_pLeavesUpdatedByCpu && !m_pLeavesUpdatedByCpu[unLod]) { // update the centers SFVFLeafVertex* pVertex = NULL; @@ -1147,7 +1173,6 @@ void CSpeedTreeWrapper::RenderLeaves(void) const m_pLeavesUpdatedByCpu[unLod] = true; } #else - if (pLeaf->m_bIsActive && m_pLeafVertexBuffer[unLod]) { // update the vertex positions SFVFLeafVertex * pVertex = NULL; @@ -1205,14 +1230,17 @@ void CSpeedTreeWrapper::RenderLeaves(void) const int unLod = pLeaf->m_nDiscreteLodLevel; - if (unLod > -1 && pLeaf->m_bIsActive && pLeaf->m_usLeafCount > 0) - { - STATEMANAGER.SetStreamSource(0, m_pLeafVertexBuffer[unLod], sizeof(SFVFLeafVertex)); - STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(pLeaf->m_fAlphaTestValue)); - - ms_faceCount += pLeaf->m_usLeafCount * 2; - STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, pLeaf->m_usLeafCount * 2); - } + if (unLod < 0 || unLod >= maxLeafLod || !pLeaf->m_bIsActive || pLeaf->m_usLeafCount == 0) + continue; + + if (!m_pLeafVertexBuffer[unLod]) + continue; + + STATEMANAGER.SetStreamSource(0, m_pLeafVertexBuffer[unLod], sizeof(SFVFLeafVertex)); + STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(pLeaf->m_fAlphaTestValue)); + + ms_faceCount += pLeaf->m_usLeafCount * 2; + STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, pLeaf->m_usLeafCount * 2); } } @@ -1222,6 +1250,9 @@ void CSpeedTreeWrapper::RenderLeaves(void) const void CSpeedTreeWrapper::EndLeafForTreeType(void) { + if (!m_pLeavesUpdatedByCpu) + return; + // reset copy flags for CPU wind for (UINT i = 0; i < m_usNumLeafLods; ++i) m_pLeavesUpdatedByCpu[i] = false; diff --git a/src/SpeedTreeLib/VertexShaders.h b/src/SpeedTreeLib/VertexShaders.h index 2670803..84531a4 100644 --- a/src/SpeedTreeLib/VertexShaders.h +++ b/src/SpeedTreeLib/VertexShaders.h @@ -204,7 +204,7 @@ static const char g_achLeafVertexProgram[] = "dcl_normal v3\n" #endif "dcl_texcoord0 v7\n" -#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT +#if defined WRAPPER_USE_GPU_WIND || defined WRAPPER_USE_GPU_LEAF_PLACEMENT "dcl_texcoord2 v9\n" #endif @@ -259,9 +259,6 @@ static const char g_achLeafVertexProgram[] = static void LoadLeafShader(LPDIRECT3DDEVICE9 pDx, LPDIRECT3DVERTEXDECLARATION9& pVertexDecl, LPDIRECT3DVERTEXSHADER9& pVertexShader) { - SAFE_RELEASE(pVertexDecl); - SAFE_RELEASE(pVertexShader); - const D3DVERTEXELEMENT9 leafVertexDecl[] = { { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, #ifdef WRAPPER_USE_DYNAMIC_LIGHTING @@ -280,18 +277,42 @@ static void LoadLeafShader(LPDIRECT3DDEVICE9 pDx, LPDIRECT3DVERTEXDECLARATION9& D3DDECL_END() }; - LPD3DXBUFFER pCode = nullptr, pError = nullptr; - if (D3DXAssembleShader(g_achLeafVertexProgram, sizeof(g_achLeafVertexProgram) - 1, nullptr, nullptr, 0, &pCode, &pError) == D3D_OK) { - if (pDx->CreateVertexShader((DWORD*)pCode->GetBufferPointer(), &pVertexShader) != D3D_OK) { - TraceError("Failed to create leaf vertex shader."); - } - } - else { - TraceError("Failed to assemble leaf vertex shader. The error reported is [ %s ].", pError ? pError->GetBufferPointer() : "unknown"); + if (!pDx) + { + TraceError("Failed to load leaf shader: null D3D device."); + return; } - if (FAILED(pDx->CreateVertexDeclaration(leafVertexDecl, &pVertexDecl))) { - TraceError("Failed to create leaf vertex declaration"); + LPDIRECT3DVERTEXDECLARATION9 pNewVertexDecl = nullptr; + LPDIRECT3DVERTEXSHADER9 pNewVertexShader = nullptr; + LPD3DXBUFFER pCode = nullptr, pError = nullptr; + const HRESULT hrAssemble = D3DXAssembleShader(g_achLeafVertexProgram, sizeof(g_achLeafVertexProgram) - 1, nullptr, nullptr, 0, &pCode, &pError); + if (SUCCEEDED(hrAssemble) && pCode) + { + const HRESULT hrCreateShader = pDx->CreateVertexShader((DWORD*)pCode->GetBufferPointer(), &pNewVertexShader); + if (FAILED(hrCreateShader)) + TraceError("Failed to create leaf vertex shader (hr=0x%08X).", hrCreateShader); + } + else + { + TraceError("Failed to assemble leaf vertex shader (hr=0x%08X). The error reported is [ %s ].", hrAssemble, pError ? pError->GetBufferPointer() : "unknown"); + } + + const HRESULT hrCreateDecl = pDx->CreateVertexDeclaration(leafVertexDecl, &pNewVertexDecl); + if (FAILED(hrCreateDecl)) + TraceError("Failed to create leaf vertex declaration (hr=0x%08X).", hrCreateDecl); + + if (pNewVertexDecl && pNewVertexShader) + { + SAFE_RELEASE(pVertexDecl); + SAFE_RELEASE(pVertexShader); + pVertexDecl = pNewVertexDecl; + pVertexShader = pNewVertexShader; + } + else + { + SAFE_RELEASE(pNewVertexDecl); + SAFE_RELEASE(pNewVertexShader); } SAFE_RELEASE(pCode);