Fix SpeedTree LOD strip handling to render all strips per LOD

This commit is contained in:
Koray
2026-02-15 15:40:54 +03:00
parent acb0ac0af5
commit cfe3c3cb7c
2 changed files with 144 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// CSpeedTreeWrapper Class // CSpeedTreeWrapper Class
// //
// (c) 2003 IDV, Inc. // (c) 2003 IDV, Inc.
@@ -65,10 +65,8 @@ m_bIsInstance(false),
m_pInstanceOf(NULL), m_pInstanceOf(NULL),
m_pGeometryCache(NULL), m_pGeometryCache(NULL),
m_usNumLeafLods(0), m_usNumLeafLods(0),
m_pBranchIndexCounts(NULL),
m_pBranchIndexBuffer(NULL), m_pBranchIndexBuffer(NULL),
m_pBranchVertexBuffer(NULL), m_pBranchVertexBuffer(NULL),
m_pFrondIndexCounts(NULL),
m_pFrondIndexBuffer(NULL), m_pFrondIndexBuffer(NULL),
m_pFrondVertexBuffer(NULL), m_pFrondVertexBuffer(NULL),
m_pLeafVertexBuffer(NULL), m_pLeafVertexBuffer(NULL),
@@ -101,7 +99,7 @@ void CSpeedTreeWrapper::OnRenderPCBlocker()
CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f);
// <EFBFBD>ϳ<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> LOD <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> // �ϳ��� ������ �� ���� LOD ������� ����
m_pSpeedTree->SetLodLevel(1.0f); m_pSpeedTree->SetLodLevel(1.0f);
//Advance(); //Advance();
@@ -244,7 +242,7 @@ void CSpeedTreeWrapper::OnRender()
CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f);
// <EFBFBD>ϳ<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> LOD <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> // �ϳ��� ������ �� ���� LOD ������� ����
m_pSpeedTree->SetLodLevel(1.0f); m_pSpeedTree->SetLodLevel(1.0f);
//Advance(); //Advance();
@@ -314,14 +312,12 @@ CSpeedTreeWrapper::~CSpeedTreeWrapper()
{ {
SAFE_RELEASE(m_pBranchVertexBuffer); SAFE_RELEASE(m_pBranchVertexBuffer);
SAFE_RELEASE(m_pBranchIndexBuffer); SAFE_RELEASE(m_pBranchIndexBuffer);
SAFE_DELETE_ARRAY(m_pBranchIndexCounts);
} }
if (m_unFrondVertexCount > 0) if (m_unFrondVertexCount > 0)
{ {
SAFE_RELEASE(m_pFrondVertexBuffer); SAFE_RELEASE(m_pFrondVertexBuffer);
SAFE_RELEASE(m_pFrondIndexBuffer); SAFE_RELEASE(m_pFrondIndexBuffer);
SAFE_DELETE_ARRAY(m_pFrondIndexCounts);
} }
for (short i = 0; i < m_usNumLeafLods; ++i) for (short i = 0; i < m_usNumLeafLods; ++i)
@@ -544,32 +540,58 @@ void CSpeedTreeWrapper::SetupBranchBuffers(void)
m_pBranchVertexBuffer->Unlock(); m_pBranchVertexBuffer->Unlock();
} }
// create and fill the index counts for each LOD const uint32_t unNumLodLevels = m_pSpeedTree->GetNumBranchLodLevels();
UINT unNumLodLevels = m_pSpeedTree->GetNumBranchLodLevels(); m_branchStripOffsets.clear();
m_pBranchIndexCounts = new unsigned short[unNumLodLevels]; m_branchStripLengths.clear();
for (UINT i = 0; i < unNumLodLevels; ++i) if (unNumLodLevels > 0)
{ m_branchStripLengths.resize(unNumLodLevels);
// force update for particular LOD
m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, i);
// check if this LOD has branches // set LOD0 for strip offsets/index buffer sizing
if (pBranches->m_usNumStrips > 0) m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, 0);
m_pBranchIndexCounts[i] = pBranches->m_pStripLengths[0]; const uint32_t stripCount = pBranches->m_usNumStrips;
else uint32_t totalIndexCount = 0;
m_pBranchIndexCounts[i] = 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];
}
} }
// set back to highest LOD
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); m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry, 0);
// the first LOD level contains the most indices of all the levels, so if (totalIndexCount > 0)
// 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); // 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 // fill the index buffer
unsigned short* pIndexBuffer = NULL; uint16_t* pIndexBuffer = NULL;
m_pBranchIndexBuffer->Lock(0, 0, reinterpret_cast<void**>(&pIndexBuffer), 0); m_pBranchIndexBuffer->Lock(0, 0, reinterpret_cast<void**>(&pIndexBuffer), 0);
memcpy(pIndexBuffer, pBranches->m_pStrips[0], pBranches->m_pStripLengths[0] * sizeof(unsigned short)); uint32_t cursor = 0;
m_pBranchIndexBuffer->Unlock(); 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(); m_pFrondVertexBuffer->Unlock();
// create and fill the index counts for each LOD const uint32_t unNumLodLevels = m_pSpeedTree->GetNumFrondLodLevels();
UINT unNumLodLevels = m_pSpeedTree->GetNumFrondLodLevels(); m_frondStripOffsets.clear();
m_pFrondIndexCounts = new unsigned short[unNumLodLevels]; m_frondStripLengths.clear();
for (WORD j = 0; j < unNumLodLevels; ++j) if (unNumLodLevels > 0)
{ m_frondStripLengths.resize(unNumLodLevels);
// force update for this LOD
m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, j);
// check if this LOD has fronds // set LOD0 for strip offsets/index buffer sizing
if (pFronds->m_usNumStrips > 0) m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, 0);
m_pFrondIndexCounts[j] = pFronds->m_pStripLengths[0]; const uint32_t stripCount = pFronds->m_usNumStrips;
else uint32_t totalIndexCount = 0;
m_pFrondIndexCounts[j] = 0; if (stripCount > 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); m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry, -1, 0);
// the first LOD level contains the most indices of all the levels, so if (totalIndexCount > 0)
// 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); // 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 // fill the index buffer
unsigned short * pIndexBuffer = NULL; uint16_t * pIndexBuffer = NULL;
m_pFrondIndexBuffer->Lock(0, 0, reinterpret_cast<void**>(&pIndexBuffer), 0); m_pFrondIndexBuffer->Lock(0, 0, reinterpret_cast<void**>(&pIndexBuffer), 0);
memcpy(pIndexBuffer, pFronds->m_pStrips[0], pFronds->m_pStripLengths[0] * sizeof(unsigned short)); uint32_t cursor = 0;
m_pFrondIndexBuffer->Unlock(); 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 // use the same buffers
spInstance->m_pBranchIndexBuffer = m_pBranchIndexBuffer; 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_pBranchVertexBuffer = m_pBranchVertexBuffer;
spInstance->m_unBranchVertexCount = m_unBranchVertexCount; spInstance->m_unBranchVertexCount = m_unBranchVertexCount;
spInstance->m_pFrondIndexBuffer = m_pFrondIndexBuffer; 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_pFrondVertexBuffer = m_pFrondVertexBuffer;
spInstance->m_unFrondVertexCount = m_unFrondVertexCount; spInstance->m_unFrondVertexCount = m_unFrondVertexCount;
@@ -908,19 +958,27 @@ void CSpeedTreeWrapper::RenderBranches(void) const
{ {
m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry); 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<size_t>(lod) >= m_branchStripLengths.size())
return;
PositionTree(); PositionTree();
// set alpha test value // set alpha test value
STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fBranchAlphaTestValue)); STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fBranchAlphaTestValue));
// render if this LOD has branches const auto& lengths = m_branchStripLengths[lod];
if (m_pBranchIndexCounts && const size_t stripCount = lengths.size() < m_branchStripOffsets.size() ? lengths.size() : m_branchStripOffsets.size();
m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] > 0) for (size_t s = 0; s < stripCount; ++s)
{ {
ms_faceCount += m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] - 2; const uint16_t stripLength = lengths[s];
STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sBranches.m_usVertexCount, 0, m_pBranchIndexCounts[m_pGeometryCache->m_sBranches.m_nDiscreteLodLevel] - 2); 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); 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<size_t>(lod) >= m_frondStripLengths.size())
return;
PositionTree(); PositionTree();
// set alpha test value // set alpha test value
STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fFrondAlphaTestValue)); STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_fFrondAlphaTestValue));
// render if this LOD has fronds const auto& lengths = m_frondStripLengths[lod];
if (m_pFrondIndexCounts && const size_t stripCount = lengths.size() < m_frondStripOffsets.size() ? lengths.size() : m_frondStripOffsets.size();
m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] > 0) for (size_t s = 0; s < stripCount; ++s)
{ {
ms_faceCount += m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] - 2; const uint16_t stripLength = lengths[s];
STATEMANAGER.DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, m_pGeometryCache->m_sFronds.m_usVertexCount, 0, m_pFrondIndexCounts[m_pGeometryCache->m_sFronds.m_nDiscreteLodLevel] - 2); 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); AddCollision(&CollisionData, &mat);
} }
} }

View File

@@ -1,4 +1,4 @@
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// SpeedTreeRTExample Class // SpeedTreeRTExample Class
// //
// (c) 2003 IDV, Inc. // (c) 2003 IDV, Inc.
@@ -43,6 +43,7 @@
#include <d3dx9.h> #include <d3dx9.h>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <cstdint>
#include "EterLib/GrpObjectInstance.h" #include "EterLib/GrpObjectInstance.h"
#include "EterLib/GrpImageInstance.h" #include "EterLib/GrpImageInstance.h"
@@ -91,8 +92,8 @@ public:
virtual void SetPosition(float x, float y, float z); virtual void SetPosition(float x, float y, float z);
virtual void CalculateBBox(); virtual void CalculateBBox();
virtual void OnRender(); // Render 시에 메소드, 그러나 프리뷰나 특수한 경우에만 직접 Render 콜을 부르며 virtual void OnRender(); // Render ½Ã¿¡ ¸Þ¼Òµå, ±×·¯³ª ÇÁ¸®ºä³ª Ư¼öÇÑ °æ¿ì¿¡¸¸ Á÷Á¢ Render ÄÝÀ» ºÎ¸£¸ç
// 그 이외에는 RenderBranches, RenderFronds 등의 메소드를 CSpeedTreeForest에서 호출한다. // ±× À̿ܿ¡´Â RenderBranches, RenderFronds µîÀÇ ¸Þ¼Òµå¸¦ CSpeedTreeForest¿¡¼­ È£ÃâÇÑ´Ù.
virtual void OnBlendRender() {} virtual void OnBlendRender() {}
virtual void OnRenderToShadowMap() {} virtual void OnRenderToShadowMap() {}
virtual void OnRenderShadow() {} virtual void OnRenderShadow() {}
@@ -172,13 +173,15 @@ private:
LPDIRECT3DVERTEXBUFFER9 m_pBranchVertexBuffer; // branch vertex buffer LPDIRECT3DVERTEXBUFFER9 m_pBranchVertexBuffer; // branch vertex buffer
unsigned int m_unBranchVertexCount; // number of vertices in branches unsigned int m_unBranchVertexCount; // number of vertices in branches
LPDIRECT3DINDEXBUFFER9 m_pBranchIndexBuffer; // branch index buffer LPDIRECT3DINDEXBUFFER9 m_pBranchIndexBuffer; // branch index buffer
unsigned short* m_pBranchIndexCounts; // number of indexes per branch LOD level std::vector<uint32_t> m_branchStripOffsets; // strip start indices (LOD0 ordering)
std::vector<std::vector<uint16_t>> m_branchStripLengths; // [lod][strip] index counts
// frond buffers // frond buffers
LPDIRECT3DVERTEXBUFFER9 m_pFrondVertexBuffer; // frond vertex buffer LPDIRECT3DVERTEXBUFFER9 m_pFrondVertexBuffer; // frond vertex buffer
unsigned int m_unFrondVertexCount; // number of vertices in frond unsigned int m_unFrondVertexCount; // number of vertices in frond
LPDIRECT3DINDEXBUFFER9 m_pFrondIndexBuffer; // frond index buffer LPDIRECT3DINDEXBUFFER9 m_pFrondIndexBuffer; // frond index buffer
unsigned short* m_pFrondIndexCounts; // number of indexes per frond LOD level std::vector<uint32_t> m_frondStripOffsets; // strip start indices (LOD0 ordering)
std::vector<std::vector<uint16_t>> m_frondStripLengths; // [lod][strip] index counts
// leaf buffers // leaf buffers
unsigned short m_usNumLeafLods; // the number of leaf LODs unsigned short m_usNumLeafLods; // the number of leaf LODs
@@ -205,3 +208,4 @@ private:
}; };
#pragma warning(pop) #pragma warning(pop)