diff --git a/audit/history.jsonl b/audit/history.jsonl index 986925b8..dab38bab 100644 --- a/audit/history.jsonl +++ b/audit/history.jsonl @@ -561,3 +561,4 @@ {"date": "2026-09-28T03:59:30+00:00", "unit": "EterLib/GrpLightManager.cpp", "action": "unit B: platform stub removed, 40250 GrpLightManager.cpp copied verbatim into port/ (port_copy.py); CPythonApplication::Create now calls m_LightManager.Initialize() (40250 :1263) so light IDs start at skip index 1; 40250 never calls FlushLight/RestoreLight, so no device-light change", "evidence": "ctest 35/35 (login_live excluded); fake-server frame 900 vs baseline mean abs diff 0.21/255"} {"date": "2026-09-28T04:02:30+00:00", "unit": "EterLib/LensFlare.cpp + ScreenFilter.cpp + Decal.cpp", "action": "unit C (part 1): platform stubs removed, 40250 sources copied verbatim (GL code is commented out in 40250 too); CMapOutdoor callers were already ported, so lens flare, screen filter and CDecal clipping (TerrainDecal) now run", "evidence": "ctest 35/35 (login_live excluded); fake-server frame 900 vs unit B mean abs diff 0.014/255; flare/filter visuals NEEDS_LIVE"} {"date": "2026-09-28T04:05:55+00:00", "unit": "EterLib/GrpShadowTexture.cpp + character shadow render target (RecordingDevice)", "action": "unit C part 2 / unit D: GrpShadowTexture stub replaced with verbatim 40250 copy (dead in 40250); CPU shadow-target rasterizer kept (flat TFACTOR pass, output-equivalent) and switched to the D3D top-left fill rule; Vulkan offscreen targets not needed since snow blur is hard-disabled in 40250", "evidence": "ctest 35/35 (login_live excluded); fake-server frame 900 vs previous mean abs diff 0.013/255 (frame noise); shadow parity NEEDS_LIVE"} +{"date": "2026-09-28T04:31:20+00:00", "unit": "SpeedTreeLib/SpeedTreeWrapper.cpp + SpeedTreeForest.cpp + SpeedTreeForestDirectX8.cpp (+ CSpeedTreeRT stand-in)", "action": "unit E: platform proxy tree renderer deleted; 40250 wrapper/forest/DirectX8 copied verbatim (with SpeedTreeConfig.h, VertexShaders.h); SDK SpeedTreeRT.h public API as shim, closed CSpeedTreeRT reimplemented in platform/SpeedTreeLib/SpeedTreeRT.cpp (procedural geometry kept, DIVERGENT); D3D8 vertex-shader decl tokens, D3D_OK, CreateVertexShader/DeleteVertexShader, ID3DXBuffer/D3DXAssembleShader, MessageBox, __min/__max added to shims", "evidence": "ctest 35/35 (login_live excluded); fake-server frame 900 at spawn vs unit D mean abs diff ~3.2/255 (area-name fade + anim phase, no trees in view); spawn moved next to a tree (temporary) shows trunk/branches/leaf cards drawn by the wrapper; tree visuals NEEDS_LIVE"} diff --git a/audit/port-map/SpeedTreeLib/SpeedTreeForest.cpp.json b/audit/port-map/SpeedTreeLib/SpeedTreeForest.cpp.json new file mode 100644 index 00000000..82bccd6e --- /dev/null +++ b/audit/port-map/SpeedTreeLib/SpeedTreeForest.cpp.json @@ -0,0 +1,80 @@ +{ + "reference": "SpeedTreeLib/SpeedTreeForest.cpp", + "reference_sha256": "655f3a39fac7fd4e0c5a151a5c2ab21d94bee5035acf9045af23b78bf609d2aa", + "priority": "P4", + "contracts": [], + "functions": { + "CSpeedTreeForest::CSpeedTreeForest": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::CSpeedTreeForest" + ] + }, + "CSpeedTreeForest::~CSpeedTreeForest": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::~CSpeedTreeForest" + ] + }, + "CSpeedTreeForest::Clear": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::Clear" + ] + }, + "CSpeedTreeForest::GetMainTree": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::GetMainTree" + ] + }, + "CSpeedTreeForest::CreateInstance": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::CreateInstance" + ] + }, + "CSpeedTreeForest::DeleteInstance": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::DeleteInstance" + ] + }, + "CSpeedTreeForest::UpdateSystem": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::UpdateSystem" + ] + }, + "CSpeedTreeForest::AdjustExtents": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::AdjustExtents" + ] + }, + "CSpeedTreeForest::SetWindStrength": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::SetWindStrength" + ] + }, + "CSpeedTreeForest::SetupWindMatrices": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::SetupWindMatrices" + ] + }, + "CSpeedTreeForest::SetLight": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::SetLight" + ] + }, + "CSpeedTreeForest::SetFog": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp:CSpeedTreeForest::SetFog" + ] + } + } +} diff --git a/audit/port-map/SpeedTreeLib/SpeedTreeForestDirectX8.cpp.json b/audit/port-map/SpeedTreeLib/SpeedTreeForestDirectX8.cpp.json new file mode 100644 index 00000000..6f5ba83d --- /dev/null +++ b/audit/port-map/SpeedTreeLib/SpeedTreeForestDirectX8.cpp.json @@ -0,0 +1,54 @@ +{ + "reference": "SpeedTreeLib/SpeedTreeForestDirectX8.cpp", + "reference_sha256": "7dd29079a158ed8af665e45ee1ed7b319732c5d33b5c8a8edcc458b5337c005a", + "priority": "P4", + "contracts": [], + "functions": { + "CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8" + ] + }, + "CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8" + ] + }, + "CSpeedTreeForestDirectX8::InitVertexShaders": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::InitVertexShaders" + ], + "note": "verbatim; WRAPPER_USE_NO_WIND / no GPU placement in SpeedTreeConfig.h, so the shader path returns the FVF as in 40250" + }, + "CSpeedTreeForestDirectX8::SetRenderingDevice": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::SetRenderingDevice" + ], + "note": "verbatim; light 0 attributes reach the CSpeedTreeRT stand-in's static lighting; tone NEEDS_LIVE" + }, + "CSpeedTreeForestDirectX8::UploadWindMatrix": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::UploadWindMatrix" + ], + "note": "verbatim; WRAPPER_USE_NO_WIND / no GPU placement in SpeedTreeConfig.h, so the shader path returns the FVF as in 40250" + }, + "CSpeedTreeForestDirectX8::UpdateCompundMatrix": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::UpdateCompundMatrix" + ] + }, + "CSpeedTreeForestDirectX8::Render": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp:CSpeedTreeForestDirectX8::Render" + ], + "note": "verbatim; render states as 40250 (branch CULL_CW, leaf/billboard passes); visual NEEDS_LIVE" + } + } +} diff --git a/audit/port-map/SpeedTreeLib/SpeedTreeWrapper.cpp.json b/audit/port-map/SpeedTreeLib/SpeedTreeWrapper.cpp.json new file mode 100644 index 00000000..ce439670 --- /dev/null +++ b/audit/port-map/SpeedTreeLib/SpeedTreeWrapper.cpp.json @@ -0,0 +1,231 @@ +{ + "reference": "SpeedTreeLib/SpeedTreeWrapper.cpp", + "reference_sha256": "938924f5303879f1b322ad48f11c974319471fcdcca616fcb958d8cf43469fa5", + "priority": "P4", + "contracts": [], + "functions": { + "CSpeedTreeWrapper::CSpeedTreeWrapper": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::CSpeedTreeWrapper" + ] + }, + "CSpeedTreeWrapper::SetVertexShaders": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetVertexShaders" + ], + "note": "verbatim; WRAPPER_USE_NO_WIND / no GPU placement in SpeedTreeConfig.h, so the shader path returns the FVF as in 40250" + }, + "CSpeedTreeWrapper::OnRenderPCBlocker": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::OnRenderPCBlocker" + ], + "note": "verbatim; PC-blocker alpha visual NEEDS_LIVE" + }, + "CSpeedTreeWrapper::OnRender": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::OnRender" + ], + "note": "verbatim; visual NEEDS_LIVE" + }, + "CSpeedTreeWrapper::~CSpeedTreeWrapper": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::~CSpeedTreeWrapper" + ] + }, + "CSpeedTreeWrapper::LoadTree": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::LoadTree" + ] + }, + "CSpeedTreeWrapper::SetupBuffers": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupBuffers" + ] + }, + "CSpeedTreeWrapper::SetupBranchBuffers": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupBranchBuffers" + ], + "note": "verbatim. DIVERGENT input: CSpeedTreeRT is the closed IDV lib; platform/SpeedTreeLib/SpeedTreeRT.cpp stands in for it and yields procedural branch strips (height 1000, species from file name), not the .spt-computed geometry; visual NEEDS_LIVE" + }, + "CSpeedTreeWrapper::SetupFrondBuffers": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupFrondBuffers" + ], + "note": "verbatim; the CSpeedTreeRT stand-in reports no fronds (0 vertices), so palms/ferns lose their fronds -- DIVERGENT until the .spt engine is reproduced; NEEDS_LIVE" + }, + "CSpeedTreeWrapper::SetupLeafBuffers": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupLeafBuffers" + ], + "note": "verbatim. DIVERGENT input: the stand-in's leaf clusters and composite-atlas rects are hand-picked (not the .spt leaf-map texcoords); NEEDS_LIVE" + }, + "CSpeedTreeWrapper::Advance": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::Advance" + ] + }, + "CSpeedTreeWrapper::MakeInstance": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::MakeInstance" + ] + }, + "CSpeedTreeWrapper::GetInstances": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetInstances" + ] + }, + "CSpeedTreeWrapper::DeleteInstance": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::DeleteInstance" + ] + }, + "CSpeedTreeWrapper::SetupBranchForTreeType": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupBranchForTreeType" + ] + }, + "CSpeedTreeWrapper::RenderBranches": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::RenderBranches" + ], + "note": "verbatim; static-lit colours (LIGHT_STATIC, LIGHTING FALSE), stage-1 self-shadow modulate, ALPHAREF 84; visual NEEDS_LIVE" + }, + "CSpeedTreeWrapper::SetupFrondForTreeType": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupFrondForTreeType" + ] + }, + "CSpeedTreeWrapper::RenderFronds": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::RenderFronds" + ] + }, + "CSpeedTreeWrapper::SetupLeafForTreeType": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupLeafForTreeType" + ] + }, + "CSpeedTreeWrapper::UploadLeafTables": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::UploadLeafTables" + ], + "note": "verbatim; WRAPPER_USE_NO_WIND / no GPU placement in SpeedTreeConfig.h, so the shader path returns the FVF as in 40250" + }, + "CSpeedTreeWrapper::RenderLeaves": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::RenderLeaves" + ], + "note": "verbatim; camera-facing leaf cards come from the stand-in's m_pLeafMapCoords (rebuilt from SetCamera); visual NEEDS_LIVE" + }, + "CSpeedTreeWrapper::EndLeafForTreeType": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::EndLeafForTreeType" + ] + }, + "CSpeedTreeWrapper::RenderBillboards": { + "status": "NEEDS_LIVE", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::RenderBillboards" + ], + "note": "verbatim; the stand-in keeps billboards inactive (single LOD), so far trees never drop to billboards -- NEEDS_LIVE" + }, + "CSpeedTreeWrapper::CleanUpMemory": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::CleanUpMemory" + ] + }, + "CSpeedTreeWrapper::PositionTree": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::PositionTree" + ] + }, + "CSpeedTreeWrapper::LoadTexture": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::LoadTexture" + ] + }, + "CSpeedTreeWrapper::SetShaderConstants": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetShaderConstants" + ], + "note": "verbatim; WRAPPER_USE_NO_WIND / no GPU placement in SpeedTreeConfig.h, so the shader path returns the FVF as in 40250" + }, + "CSpeedTreeWrapper::SetPosition": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetPosition" + ] + }, + "CSpeedTreeWrapper::GetBoundingSphere": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetBoundingSphere" + ] + }, + "CSpeedTreeWrapper::CalculateBBox": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::CalculateBBox" + ] + }, + "CSpeedTreeWrapper::GetCollisionObjectCount": { + "status": "DIVERGENT", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObjectCount" + ], + "note": "verbatim; the stand-in returns one trunk CO_CYLINDER (radius 35, height 750), not the .spt collision objects" + }, + "CSpeedTreeWrapper::GetCollisionObject": { + "status": "DIVERGENT", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObject" + ], + "note": "verbatim; the stand-in returns one trunk CO_CYLINDER (radius 35, height 750), not the .spt collision objects" + }, + "CSpeedTreeWrapper::GetPosition": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetPosition" + ] + }, + "CSpeedTreeWrapper::GetTreeSize": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetTreeSize" + ] + }, + "CSpeedTreeWrapper::OnUpdateCollisionData": { + "status": "PORTED", + "impl": [ + "extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::OnUpdateCollisionData" + ] + } + } +} diff --git a/extension/src/platform/EterLib/RecordingDevice.cpp b/extension/src/platform/EterLib/RecordingDevice.cpp index ba8ad176..db377b18 100644 --- a/extension/src/platform/EterLib/RecordingDevice.cpp +++ b/extension/src/platform/EterLib/RecordingDevice.cpp @@ -463,6 +463,14 @@ public: // PORT: fixed function only; CGraphicDevice's stream "shaders" are the FVFs they describe. HRESULT SetVertexShader(DWORD handle) override { m_fvf = handle; return S_OK; } HRESULT SetPixelShader(DWORD) override { return S_OK; } + // PORT: no programmable vertex shaders; 40250 falls back to its FVF when creation fails. + HRESULT CreateVertexShader(const DWORD*, const DWORD*, DWORD* pHandle, DWORD) override + { + if (pHandle) + *pHandle = 0; + return E_FAIL; + } + HRESULT DeleteVertexShader(DWORD) override { return S_OK; } HRESULT SetVertexShaderConstant(DWORD, const void*, DWORD) override { return S_OK; } HRESULT SetPixelShaderConstant(DWORD, const void*, DWORD) override { return S_OK; } HRESULT SetStreamSource(UINT stream, IDirect3DVertexBuffer8* buffer, UINT stride) override diff --git a/extension/src/platform/SpeedTreeLib/SpeedTreeForest.cpp b/extension/src/platform/SpeedTreeLib/SpeedTreeForest.cpp deleted file mode 100644 index 1f71ff9f..00000000 --- a/extension/src/platform/SpeedTreeLib/SpeedTreeForest.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "SpeedTreeLib/StdAfx.h" -#include "SpeedTreeLib/SpeedTreeForest.h" - -#include "EterPack/EterPackManager.h" -#include "GameLib/Property.h" -#include "GameLib/PropertyManager.h" -#include "../EterLib/RenderCommands3D.h" - -#include -#include -#include -#include -#include - -CSpeedTreeForest::CSpeedTreeForest() - : m_fWindStrength(0.2f) - , m_fAccumTime(0.0f) -{ - std::memset(m_afLighting, 0, sizeof(m_afLighting)); - std::memset(m_afFog, 0, sizeof(m_afFog)); - m_afForestExtents[0] = m_afForestExtents[1] = m_afForestExtents[2] = FLT_MAX; - m_afForestExtents[3] = m_afForestExtents[4] = m_afForestExtents[5] = -FLT_MAX; -} - -CSpeedTreeForest::~CSpeedTreeForest() -{ -} - -auto CSpeedTreeForest::ClearMainTree() -> void -{ - Clear(); -} - -auto CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, const char * c_pszFileName) -> BOOL -{ - if (!ppMainTree) - return FALSE; - *ppMainTree = NULL; - - if (!IsNativeTerrainRenderEnabled()) - return FALSE; - - TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); - CSpeedTreeWrapper * pTree = NULL; - - if (itor != m_pMainTreeMap.end()) - { - pTree = itor->second; - } - else - { - if (!c_pszFileName || !c_pszFileName[0]) - return FALSE; - - CMappedFile file; - LPCVOID c_pvData = NULL; - if (!CEterPackManager::Instance().Get(file, c_pszFileName, &c_pvData)) - return FALSE; - - float fSize = 1000.0f; - float fVariance = 0.0f; - CProperty * pProperty = NULL; - if (CPropertyManager::InstancePtr() && CPropertyManager::Instance().Get(dwCRC, &pProperty) && pProperty) - { - const char * c_pszTreeSize = NULL; - const char * c_pszTreeVariance = NULL; - if (pProperty->GetString("TreeSize", &c_pszTreeSize) && c_pszTreeSize) - fSize = static_cast( std::atof(c_pszTreeSize)); - if (pProperty->GetString("TreeVariance", &c_pszTreeVariance) && c_pszTreeVariance) - fVariance = static_cast(std::atof(c_pszTreeVariance)); - } - - pTree = new CSpeedTreeWrapper; - if (!pTree->LoadTree(c_pszFileName, static_cast(c_pvData), file.Size(), 1, fSize, fVariance)) - { - delete pTree; - return FALSE; - } - - m_pMainTreeMap.insert(TTreeMap::value_type(dwCRC, pTree)); - file.Destroy(); - } - - *ppMainTree = pTree; - return TRUE; -} - -auto CSpeedTreeForest::GetMainTree(DWORD dwCRC) -> CSpeedTreeWrapper * -{ - TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); - if (itor == m_pMainTreeMap.end()) - return NULL; - return itor->second; -} - -auto CSpeedTreeForest::DeleteMainTree(DWORD dwCRC) -> void -{ - TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); - if (itor == m_pMainTreeMap.end()) - return; - - CSpeedTreeWrapper * pMainTree = itor->second; - UINT uiCount = 0; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - for (UINT i = 0; i < uiCount; ++i) - delete ppInstances[i]; - - delete pMainTree; - m_pMainTreeMap.erase(itor); -} - -auto CSpeedTreeForest::CreateInstance(float x, float y, float z, DWORD dwTreeCRC, const char * c_pszTreeName) -> CSpeedTreeWrapper * -{ - if (!IsNativeTerrainRenderEnabled()) - return NULL; - - CSpeedTreeWrapper * pMainTree = NULL; - if (!GetMainTree(dwTreeCRC, &pMainTree, c_pszTreeName) || !pMainTree) - return NULL; - - CSpeedTreeWrapper * pTreeInst = pMainTree->MakeInstance(); - if (!pTreeInst) - return NULL; - - pTreeInst->SetPosition(x, y, z); - pTreeInst->RegisterBoundingSphere(); - AdjustExtents(x, y, z); - return pTreeInst; -} - -auto CSpeedTreeForest::DeleteInstance(CSpeedTreeWrapper * pInstance) -> void -{ - if (!pInstance) - return; - - CSpeedTreeWrapper * pParentTree = pInstance->InstanceOf(); - if (!pParentTree) - return; - - pParentTree->DeleteInstance(pInstance); -} - -auto CSpeedTreeForest::UpdateSystem(float fCurrentTime) -> void -{ - static float fLastTime = fCurrentTime; - float fElapsedTime = fCurrentTime - fLastTime; - fLastTime = fCurrentTime; - if (fElapsedTime > 0.0f && fElapsedTime < 10.0f) - m_fAccumTime += fElapsedTime; - SetupWindMatrices(m_fAccumTime); -} - -auto CSpeedTreeForest::Clear() -> void -{ - TTreeMap::iterator itor = m_pMainTreeMap.begin(); - UINT uiCount = 0; - - while (itor != m_pMainTreeMap.end()) - { - CSpeedTreeWrapper * pMainTree = (itor++)->second; - if (!pMainTree) - continue; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - for (UINT i = 0; i < uiCount; ++i) - delete ppInstances[i]; - delete pMainTree; - } - - m_pMainTreeMap.clear(); -} - -auto CSpeedTreeForest::SetLight(const float * afDirection, const float * afAmbient, const float * afDiffuse) -> void -{ - if (!afDirection || !afAmbient || !afDiffuse) - return; - - m_afLighting[0] = afDirection[0]; - m_afLighting[1] = afDirection[1]; - m_afLighting[2] = afDirection[2]; - m_afLighting[3] = 1.0f; - - m_afLighting[4] = afAmbient[0]; - m_afLighting[5] = afAmbient[1]; - m_afLighting[6] = afAmbient[2]; - m_afLighting[7] = afAmbient[3]; - - m_afLighting[8] = afDiffuse[0]; - m_afLighting[9] = afDiffuse[1]; - m_afLighting[10] = afDiffuse[2]; - m_afLighting[11] = afDiffuse[3]; -} - -auto CSpeedTreeForest::SetFog(float fFogNear, float fFogFar) -> void -{ - const float denom = (fFogFar - fFogNear); - const float c_fFogLinearScale = (std::fabs(denom) > 1e-4f) ? (1.0f / denom) : 0.0f; - - m_afFog[0] = fFogNear; - m_afFog[1] = fFogFar; - m_afFog[2] = c_fFogLinearScale; - m_afFog[3] = 0.0f; -} - -auto CSpeedTreeForest::SetWindStrength(float fStrength) -> void -{ - if (m_fWindStrength == fStrength) - return; - - m_fWindStrength = fStrength; - - TTreeMap::iterator itor = m_pMainTreeMap.begin(); - UINT uiCount = 0; - - while (itor != m_pMainTreeMap.end()) - { - CSpeedTreeWrapper * pMainTree = (itor++)->second; - if (!pMainTree) - continue; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - for (UINT i = 0; i < uiCount; ++i) - { - if (ppInstances[i] && ppInstances[i]->GetSpeedTree()) - ppInstances[i]->GetSpeedTree()->SetWindStrength(m_fWindStrength); - } - } -} - -auto CSpeedTreeForest::SetupWindMatrices(float /*fTimeInSecs*/) -> void -{ -} - -auto CSpeedTreeForest::AdjustExtents(float x, float y, float z) -> void -{ - m_afForestExtents[0] = std::min(m_afForestExtents[0], x); - m_afForestExtents[1] = std::min(m_afForestExtents[1], y); - m_afForestExtents[2] = std::min(m_afForestExtents[2], z); - - m_afForestExtents[3] = std::max(m_afForestExtents[3], x); - m_afForestExtents[4] = std::max(m_afForestExtents[4], y); - m_afForestExtents[5] = std::max(m_afForestExtents[5], z); -} diff --git a/extension/src/platform/SpeedTreeLib/SpeedTreeForestDirectX8.cpp b/extension/src/platform/SpeedTreeLib/SpeedTreeForestDirectX8.cpp deleted file mode 100644 index f657e8fe..00000000 --- a/extension/src/platform/SpeedTreeLib/SpeedTreeForestDirectX8.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "SpeedTreeLib/StdAfx.h" -#include "SpeedTreeLib/SpeedTreeForestDirectX8.h" - -#include "EterBase/Timer.h" -#include "EterLib/Camera.h" -#include "EterLib/StateManager.h" -#include "../EterLib/RenderCommands3D.h" - -CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8() - : m_pDx(NULL) - , m_dwBranchVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1) - , m_dwLeafVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1) -{ -} - -CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8() -{ - Clear(); -} - -auto CSpeedTreeForestDirectX8::UploadWindMatrix(unsigned int uiLocation, const float * pMatrix) const -> void -{ - if (pMatrix) - STATEMANAGER.SetVertexShaderConstant(uiLocation, pMatrix, 4); -} - -auto CSpeedTreeForestDirectX8::UpdateCompundMatrix(const D3DXVECTOR3 & /*c_rEyeVec*/, const D3DXMATRIX & c_rmatView, const D3DXMATRIX & c_rmatProj) -> void -{ - D3DXMATRIX matBlendShader; - D3DXMatrixMultiply(&matBlendShader, &c_rmatView, &c_rmatProj); - D3DXMatrixTranspose(&matBlendShader, &matBlendShader); - STATEMANAGER.SetVertexShaderConstant(0, &matBlendShader, 4); -} - -auto CSpeedTreeForestDirectX8::Render(unsigned long ulRenderBitVector) -> void -{ - if (!IsNativeTerrainRenderEnabled()) - return; - - UpdateSystem(CTimer::Instance().GetCurrentSecond()); - - if (m_pMainTreeMap.empty()) - return; - - if (!(ulRenderBitVector & Forest_RenderToShadow) && !(ulRenderBitVector & Forest_RenderToMiniMap)) - { - CCamera * pCamera = CCameraManager::Instance().GetCurrentCamera(); - if (pCamera) - UpdateCompundMatrix(pCamera->GetEye(), ms_matView, ms_matProj); - } - - DWORD dwLightState = STATEMANAGER.GetRenderState(D3DRS_LIGHTING); - DWORD dwColorVertexState = STATEMANAGER.GetRenderState(D3DRS_COLORVERTEX); - DWORD dwFogVertexMode = STATEMANAGER.GetRenderState(D3DRS_FOGVERTEXMODE); - - STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE); - STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, TRUE); - - TTreeMap::iterator itor = m_pMainTreeMap.begin(); - UINT uiCount = 0; - - while (itor != m_pMainTreeMap.end()) - { - CSpeedTreeWrapper * pMainTree = (itor++)->second; - if (!pMainTree) - continue; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - for (UINT i = 0; i < uiCount; ++i) - { - if (ppInstances[i]) - ppInstances[i]->Advance(); - } - } - - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR); - STATEMANAGER.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR); - STATEMANAGER.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR); - - STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0x00000060); - STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - STATEMANAGER.SetVertexShader(m_dwBranchVertexShader); - - // Render branches - if (ulRenderBitVector & Forest_RenderBranches) - { - STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); - itor = m_pMainTreeMap.begin(); - while (itor != m_pMainTreeMap.end()) - { - CSpeedTreeWrapper * pMainTree = (itor++)->second; - if (!pMainTree) - continue; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - pMainTree->SetupBranchForTreeType(); - for (UINT i = 0; i < uiCount; ++i) - { - if (ppInstances[i] && ppInstances[i]->isShow()) - ppInstances[i]->RenderBranches(); - } - } - } - - // Render leaves - if (ulRenderBitVector & Forest_RenderLeaves) - { - STATEMANAGER.SetVertexShader(m_dwLeafVertexShader); - STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); - STATEMANAGER.SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); - STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, 0x00000060); - STATEMANAGER.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - itor = m_pMainTreeMap.begin(); - while (itor != m_pMainTreeMap.end()) - { - CSpeedTreeWrapper * pMainTree = (itor++)->second; - if (!pMainTree) - continue; - CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); - pMainTree->SetupLeafForTreeType(); - for (UINT i = 0; i < uiCount; ++i) - { - if (ppInstances[i] && ppInstances[i]->isShow()) - ppInstances[i]->RenderLeaves(); - } - pMainTree->EndLeafForTreeType(); - } - } - - STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLightState); - STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, dwColorVertexState); - STATEMANAGER.SetRenderState(D3DRS_FOGVERTEXMODE, dwFogVertexMode); - - STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF); - STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); -} - -auto CSpeedTreeForestDirectX8::SetRenderingDevice(LPDIRECT3DDEVICE8 pDevice) -> bool -{ - m_pDx = pDevice; - return InitVertexShaders(); -} - -auto CSpeedTreeForestDirectX8::InitVertexShaders() -> bool -{ - m_dwBranchVertexShader = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1; - m_dwLeafVertexShader = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1; - CSpeedTreeWrapper::SetVertexShaders(m_dwBranchVertexShader, m_dwLeafVertexShader); - return true; -} diff --git a/extension/src/platform/SpeedTreeLib/SpeedTreeRT.cpp b/extension/src/platform/SpeedTreeLib/SpeedTreeRT.cpp new file mode 100644 index 00000000..a8bb8069 --- /dev/null +++ b/extension/src/platform/SpeedTreeLib/SpeedTreeRT.cpp @@ -0,0 +1,858 @@ +// PORT: stand-in for the closed IDV SpeedTreeRT 1.6.0 library (40250 extern/library/SpeedTreeRT.lib, +// Windows COFF). The class interface is the SDK header (port/common/shim/sdk/SpeedTreeRT.h) and the +// 40250 SpeedTreeLib wrapper/forest call it unchanged; this file implements the members they call. +// +// What is the SDK's contract and kept here: +// - GetGeometry fills SGeometry the way SpeedTreeWrapper reads it: one contiguous branch strip per +// LOD, static-lit m_pColors (LIGHT_STATIC; light 0 from SetLightAttributes, the tree material), +// m_pTexCoords1 self-shadow coordinates, camera-facing leaf clusters (m_pCenterCoords + +// m_pLeafMapCoords rebuilt from SetCamera) with m_pLeafMapTexCoords, alpha test values of 84 +// (SetLeafTargetAlphaMask default 0x54 == c_nDefaultAlphaTestValue). +// - SetLodLevel(1.0f), which the wrapper always applies, selects discrete LOD 0 with billboards +// inactive; there is only that LOD. +// - GetTextures returns bare file names: the wrapper prefixes the .spt directory. +// - GetTreeSize reads the Size / SizeVariance header tokens (2001 / 2002) of the .spt. +// What is not the SDK (DIVERGENT; the notes are on the wrapper in audit/port-map/SpeedTreeLib/): +// - Branch and leaf geometry cannot be generated from the .spt parameters without the closed +// engine; it is the previous procedural proxy (species hints from the file name, 1000-unit +// height, crossed-branch trunk, hand-picked composite atlas rects), no fronds. +// - Collision objects are one trunk cylinder; the .spt collision block is not parsed. +// - Materials are the SDK defaults (white); the .spt material block is not parsed. +#include "SpeedTreeLib/StdAfx.h" + +#include +#include + +#include "../EterLib/RenderCommands3D.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +constexpr float kPI = 3.14159265358979323846f; +constexpr float kTAU = 2.0f * kPI; +constexpr float kAlphaTestValue = 84.0f; // SetLeafTargetAlphaMask(0x54) default +constexpr float kProxyHeight = 1000.0f; + +struct Vec3 +{ + float x = 0.0f, y = 0.0f, z = 0.0f; + Vec3() = default; + Vec3(float a, float b, float c) : x(a), y(b), z(c) {} + Vec3 operator+(const Vec3 & o) const { return { x + o.x, y + o.y, z + o.z }; } + Vec3 operator-(const Vec3 & o) const { return { x - o.x, y - o.y, z - o.z }; } + Vec3 operator*(float s) const { return { x * s, y * s, z * s }; } +}; + +float dot(const Vec3 & a, const Vec3 & b) { return a.x * b.x + a.y * b.y + a.z * b.z; } +Vec3 cross(const Vec3 & a, const Vec3 & b) { return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; } +Vec3 normalize(const Vec3 & v) +{ + const float l = std::sqrt(dot(v, v)); + return l > 1e-6f ? v * (1.0f / l) : Vec3(0.0f, 0.0f, 1.0f); +} + +std::string to_lower_str(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + return s; +} + +std::string basename_only(std::string path) +{ + std::replace(path.begin(), path.end(), '\\', '/'); + const size_t slash = path.find_last_of('/'); + return slash == std::string::npos ? path : path.substr(slash + 1); +} + +bool has_any(const std::string & hint, std::initializer_list keys) +{ + const std::string h = to_lower_str(hint); + for (const char * k : keys) + if (h.find(k) != std::string::npos) + return true; + return false; +} + +bool is_conifer_species(const std::string & hint) +{ + return has_any(hint, { "cedar", "cypress", "pine", "fir", "spruce", "conifer", "juniper", "christmastree" }); +} + +bool is_palm_species(const std::string & hint) +{ + return has_any(hint, { "palm", "banana", "aloe", "fern", "joshua" }); +} + +uint32_t hash32(uint32_t s) +{ + s ^= s >> 16; + s *= 0x7feb352dU; + s ^= s >> 15; + s *= 0x846ca68bU; + s ^= s >> 16; + return s; +} + +float hash01(uint32_t s) { return float(hash32(s) & 0x00FFFFFFU) / float(0x01000000U); } + +uint32_t species_seed(const std::string & s) +{ + uint32_t h = 2166136261U; + for (unsigned char c : s) + { + h ^= c; + h *= 16777619U; + } + return h; +} + +// Composite-atlas leaf rects, D3D image space (v down). +struct UVRect +{ + float u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f; +}; + +std::vector foliage_rects(const std::string & species, const std::string & composite) +{ + if (composite.empty()) + return { { 0.0f, 0.0f, 1.0f, 1.0f } }; + const std::string s = to_lower_str(species); + const std::string c = to_lower_str(composite); + const bool fall = s.find("fall") != std::string::npos; + const bool winter = s.find("winter") != std::string::npos; + if (c.find("b1") != std::string::npos) + { + if (fall) + return { { 0.00f, 0.05f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } }; + return { { 0.25f, 0.02f, 0.50f, 0.23f }, { 0.25f, 0.18f, 0.50f, 0.36f }, { 0.00f, 0.27f, 0.27f, 0.49f } }; + } + if (c.find("b2") != std::string::npos) + { + if (fall) + return { { 0.00f, 0.00f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } }; + return { { 0.50f, 0.38f, 0.75f, 0.63f }, { 0.50f, 0.63f, 0.75f, 0.88f }, { 0.00f, 0.38f, 0.25f, 0.62f } }; + } + if (c.find("b3") != std::string::npos) + { + if (fall) + return { { 0.25f, 0.25f, 0.50f, 0.50f } }; + return { { 0.00f, 0.25f, 0.25f, 0.50f }, { 0.00f, 0.50f, 0.25f, 0.75f }, { 0.25f, 0.50f, 0.50f, 0.75f } }; + } + if (c.find("n1") != std::string::npos) + { + if (winter) + return { { 0.00f, 0.36f, 0.50f, 0.58f }, { 0.25f, 0.55f, 0.52f, 0.75f } }; + return { { 0.00f, 0.72f, 0.28f, 0.96f }, { 0.25f, 0.74f, 0.53f, 0.97f } }; + } + if (c.find("n2") != std::string::npos) + return { { 0.00f, 0.48f, 0.27f, 0.75f }, { 0.26f, 0.73f, 0.58f, 1.00f }, { 0.75f, 0.48f, 1.00f, 0.80f } }; + return { { 0.0f, 0.0f, 1.0f, 1.0f } }; +} + +// .spt header: 1000 "__IdvSpt_02_", 1002, 2000 , 2001 , +// 2002 . +bool read_spt_size(const unsigned char * p, size_t n, float & size, float & variance) +{ + size_t o = 0; + auto i32 = [&](int32_t & v) { + if (o + 4 > n) + return false; + std::memcpy(&v, p + o, 4); + o += 4; + return true; + }; + int32_t t = 0, len = 0; + if (!i32(t) || t != 1000 || !i32(len) || len < 0 || (o += size_t(len)) > n) + return false; + if (!i32(t) || t != 1002 || !i32(t) || t != 2000 || !i32(len) || len < 0 || (o += size_t(len)) > n) + return false; + int32_t bits = 0; + if (!i32(t) || t != 2001 || !i32(bits)) + return false; + std::memcpy(&size, &bits, 4); + if (!i32(t) || t != 2002 || !i32(bits)) + return false; + std::memcpy(&variance, &bits, 4); + return true; +} + +struct SStaticLight +{ + float afAttributes[16] = { + -0.707f, 0.0f, 0.707f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f + }; + bool bOn = false; +}; + +SStaticLight g_aLights[8]; +float g_afCameraPos[3] = { 0.0f, 0.0f, 0.0f }; +float g_afCameraDir[3] = { 0.0f, 1.0f, 0.0f }; +bool g_bTextureFlip = false; +bool g_bDropToBillboard = false; +std::string g_strError; + +void SetError(const char * pError) { g_strError = pError ? pError : ""; } + +const float c_afDefaultMaterial[13] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 5.0f }; + +// Static lighting (SLS_BASIC): material ambient x light ambient + material diffuse x light diffuse x N.L +// for every light switched on, clamped per channel. +unsigned long static_color(const Vec3 & n, const float * mat) +{ + float rgb[3] = { 0.0f, 0.0f, 0.0f }; + bool any = false; + for (const SStaticLight & light : g_aLights) + { + if (!light.bOn) + continue; + any = true; + const float * a = light.afAttributes; + const float ndotl = std::max(0.0f, dot(n, normalize(Vec3(a[0], a[1], a[2])))); + for (int c = 0; c < 3; ++c) + rgb[c] += mat[3 + c] * a[6 + c] + mat[c] * a[3 + c] * ndotl; + } + if (!any) + rgb[0] = rgb[1] = rgb[2] = 1.0f; + auto channel = [](float v) { return static_cast(std::clamp(v, 0.0f, 1.0f) * 255.0f + 0.5f); }; + return 0xFF000000UL | (channel(rgb[0]) << 16) | (channel(rgb[1]) << 8) | channel(rgb[2]); +} + +// Geometry shared by a tree and its instances (the SDK shares it through m_pRefCount). +struct STreeData +{ + std::string strName; + float fFileSize = -1.0f, fFileVariance = 0.0f; + float fSize = -1.0f, fVariance = 0.0f; + + std::string strBranchTexture, strCompositeTexture, strSelfShadowTexture; + + // branches + std::vector vBranchCoords, vBranchNormals, vBranchTex0, vBranchTex1; + std::vector vBranchColors; + std::vector vBranchStrip; + unsigned short usBranchStripLength = 0; + const unsigned short * pBranchStrip = nullptr; + + // leaves + struct SCluster + { + Vec3 vCenter, vNormal; + float fWidth = 0.0f, fHeight = 0.0f; + UVRect rect; + bool bFlip = false; + }; + std::vector vClusters; + std::vector vLeafCenters, vLeafNormals, vLeafTexCoords, vLeafCoords; + std::vector vLeafColors; + std::vector vLeafMapIndices; + std::vector vLeafTexPtrs, vLeafCoordPtrs; + + float afBoundingBox[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + float fTrunkRadius = 35.0f, fTrunkHeight = 600.0f; + float afBranchMaterial[13], afFrondMaterial[13], afLeafMaterial[13]; + float fLeafLightingAdjustment = 0.75f; + bool bComputed = false; + + STreeData() + { + std::memcpy(afBranchMaterial, c_afDefaultMaterial, sizeof(afBranchMaterial)); + std::memcpy(afFrondMaterial, c_afDefaultMaterial, sizeof(afFrondMaterial)); + std::memcpy(afLeafMaterial, c_afDefaultMaterial, sizeof(afLeafMaterial)); + } + + void Parse(const unsigned char * p, size_t n) + { + fmt::SptInfo info; + fmt::sniff_spt(std::string(reinterpret_cast(p), n), info); + for (const std::string & ref : info.texture_refs) + if (to_lower_str(ref).find("bark") != std::string::npos) + { + strBranchTexture = basename_only(ref); + break; + } + if (strBranchTexture.empty() && !info.texture_refs.empty()) + strBranchTexture = basename_only(info.texture_refs.front()); + strCompositeTexture = basename_only(info.composite_texture); + strSelfShadowTexture = basename_only(info.self_shadow_texture); + read_spt_size(p, n, fFileSize, fFileVariance); + fSize = fFileSize; + fVariance = fFileVariance; + } + + void AppendStrip(const std::vector & sub) + { + if (sub.empty()) + return; + if (!vBranchStrip.empty()) + { + // degenerate join; keep the next strip on an even triangle so its winding is unchanged + vBranchStrip.push_back(vBranchStrip.back()); + vBranchStrip.push_back(sub.front()); + if (vBranchStrip.size() & 1) + vBranchStrip.push_back(sub.front()); + } + vBranchStrip.insert(vBranchStrip.end(), sub.begin(), sub.end()); + } + + void AddTube(const Vec3 & from, const Vec3 & to, float r0, float r1, int seg, float bark_repeat) + { + Vec3 axis = to - from; + if (dot(axis, axis) < 1e-6f) + return; + axis = normalize(axis); + const Vec3 helper = std::fabs(axis.z) > 0.9f ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(0.0f, 0.0f, 1.0f); + const Vec3 u = normalize(cross(axis, helper)); + const Vec3 w = normalize(cross(axis, u)); + + std::vector strip; + for (int i = 0; i <= seg; ++i) + { + if (vBranchCoords.size() / 3 + 2 > 65530) + break; + const float a = float(i) / float(seg) * kTAU; + const Vec3 n = u * std::cos(a) + w * std::sin(a); + const float s = float(i) / float(seg); + for (int end = 0; end < 2; ++end) + { + const Vec3 p = end ? to + n * r1 : from + n * r0; + strip.push_back(static_cast(vBranchCoords.size() / 3)); + vBranchCoords.insert(vBranchCoords.end(), { p.x, p.y, p.z }); + vBranchNormals.insert(vBranchNormals.end(), { n.x, n.y, n.z }); + vBranchTex0.insert(vBranchTex0.end(), { s, end ? 0.0f : bark_repeat }); + } + } + AppendStrip(strip); + } + + void BuildBranches(float H, bool conifer, bool palm) + { + const float trunk_top = H * (palm ? 0.82f : (conifer ? 0.90f : 0.76f)); + const float trunk_r = H * (palm ? 0.028f : 0.035f); + const float H_m = H * 0.01f; + AddTube(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 0.0f, trunk_top), trunk_r * 1.35f, trunk_r * 0.42f, 9, H_m * 0.22f); + if (palm) + return; + + const int count = conifer ? 9 : 8; + const uint32_t seed = species_seed(strName); + for (int i = 0; i < count; ++i) + { + const float f = (i + 1.0f) / (count + 1.0f); + const float z = H * (conifer ? (0.28f + f * 0.52f) : (0.32f + f * 0.34f)); + const float angle = kTAU * (f * 1.6180339f + hash01(seed + i * 17U)); + const float len = H * (conifer ? (0.24f * (1.0f - f * 0.55f)) : (0.18f + 0.08f * hash01(seed + i * 29U))); + const Vec3 from(0.0f, 0.0f, z); + const Vec3 to(std::cos(angle) * len, std::sin(angle) * len, + z + H * (conifer ? 0.06f : (0.10f + 0.06f * hash01(seed + i * 31U)))); + AddTube(from, to, trunk_r * (0.55f - 0.20f * f), trunk_r * 0.12f, 6, H_m * 0.08f); + if (!conifer && (i % 2 == 0)) + { + const float side = angle + (hash01(seed + i * 37U) > 0.5f ? 0.65f : -0.65f); + const Vec3 tip = to + Vec3(std::cos(side), std::sin(side), 0.65f) * (len * 0.42f); + AddTube(to, tip, trunk_r * 0.16f, trunk_r * 0.05f, 5, H_m * 0.04f); + } + } + } + + void BuildLeaves(float H, bool conifer, bool palm) + { + const std::vector rects = foliage_rects(strName, strCompositeTexture); + const uint32_t seed = species_seed(strName); + const int count = palm ? 16 : 24; + for (int i = 0; i < count; ++i) + { + const float a = kTAU * (float(i) * 0.6180339f + hash01(seed + i * 101U) * 0.15f); + SCluster c; + if (palm) + { + const float radial = H * (0.10f + 0.18f * hash01(seed + i * 103U)); + c.vCenter = Vec3(std::cos(a) * radial, std::sin(a) * radial, H * (0.78f + 0.12f * hash01(seed + i * 107U))); + c.fWidth = H * 0.32f; + c.fHeight = H * 0.18f; + } + else if (conifer) + { + const float zf = 0.30f + 0.62f * (float(i) + 0.5f) / float(count); + const float radial = H * 0.23f * (1.0f - zf * 0.70f) * (0.35f + 0.65f * hash01(seed + i * 109U)); + c.vCenter = Vec3(std::cos(a) * radial, std::sin(a) * radial, H * zf); + c.fWidth = H * (0.18f + 0.10f * (1.0f - zf)); + c.fHeight = H * 0.18f; + } + else + { + const float zf = hash01(seed + i * 109U); + const float zn = zf * 2.0f - 1.0f; + const float radial = H * 0.34f * std::sqrt(std::max(0.05f, 1.0f - zn * zn)) * + (0.25f + 0.75f * std::sqrt(hash01(seed + i * 113U))); + c.vCenter = Vec3(std::cos(a) * radial, std::sin(a) * radial, H * (0.58f + zf * 0.34f)); + c.fWidth = H * (0.23f + 0.10f * hash01(seed + i * 127U)); + c.fHeight = H * (0.15f + 0.08f * hash01(seed + i * 131U)); + } + // cluster normal: away from the crown centre, biased up + const Vec3 out = normalize(c.vCenter - Vec3(0.0f, 0.0f, H * 0.6f)); + c.vNormal = normalize(Vec3(out.x, out.y, std::fabs(out.z) * 0.5f + 0.5f)); + c.rect = rects[size_t(i) % rects.size()]; + c.bFlip = (i & 1) != 0; + vClusters.push_back(c); + } + } + + void Compute() + { + const bool enabled = IsNativeTerrainRenderEnabled(); + const float H = kProxyHeight; + const float R = H * 0.35f; + afBoundingBox[0] = -R; + afBoundingBox[1] = -R; + afBoundingBox[2] = 0.0f; + afBoundingBox[3] = R; + afBoundingBox[4] = R; + afBoundingBox[5] = H; + fTrunkRadius = H * 0.035f; + fTrunkHeight = H * 0.75f; + + // PORT: the Godot host draws its own trees unless the native terrain renderer is on; the tree + // then has no geometry so the forest submits nothing. + if (enabled) + { + const bool conifer = is_conifer_species(strName); + const bool palm = is_palm_species(strName); + BuildBranches(H, conifer, palm); + BuildLeaves(H, conifer, palm); + } + + const size_t nBranchVerts = vBranchCoords.size() / 3; + vBranchColors.resize(nBranchVerts); + vBranchTex1.resize(nBranchVerts * 2); + for (size_t i = 0; i < nBranchVerts; ++i) + { + const Vec3 n(vBranchNormals[i * 3], vBranchNormals[i * 3 + 1], vBranchNormals[i * 3 + 2]); + vBranchColors[i] = static_color(n, afBranchMaterial); + // self-shadow map projected straight down over the bounding box + vBranchTex1[i * 2] = (vBranchCoords[i * 3] - afBoundingBox[0]) / (afBoundingBox[3] - afBoundingBox[0]); + vBranchTex1[i * 2 + 1] = (vBranchCoords[i * 3 + 1] - afBoundingBox[1]) / (afBoundingBox[4] - afBoundingBox[1]); + } + if (g_bTextureFlip) + for (size_t i = 0; i < nBranchVerts; ++i) + { + vBranchTex0[i * 2 + 1] = 1.0f - vBranchTex0[i * 2 + 1]; + vBranchTex1[i * 2 + 1] = 1.0f - vBranchTex1[i * 2 + 1]; + } + usBranchStripLength = static_cast(vBranchStrip.size()); + pBranchStrip = vBranchStrip.empty() ? nullptr : vBranchStrip.data(); + + const size_t nLeaves = vClusters.size(); + vLeafCenters.resize(nLeaves * 3); + vLeafNormals.resize(nLeaves * 3); + vLeafColors.resize(nLeaves); + vLeafMapIndices.assign(nLeaves, 0); + vLeafTexCoords.resize(nLeaves * 8); + vLeafCoords.assign(nLeaves * 16, 0.0f); + vLeafTexPtrs.resize(nLeaves); + vLeafCoordPtrs.resize(nLeaves); + for (size_t i = 0; i < nLeaves; ++i) + { + const SCluster & c = vClusters[i]; + std::memcpy(&vLeafCenters[i * 3], &c.vCenter, 12); + std::memcpy(&vLeafNormals[i * 3], &c.vNormal, 12); + vLeafColors[i] = static_color(c.vNormal, afLeafMaterial); + // corners 0..3: (+r,+u) (-r,+u) (-r,-u) (+r,-u); drawn as 0,1,2 / 0,2,3 + const float uR = c.bFlip ? c.rect.u0 : c.rect.u1, uL = c.bFlip ? c.rect.u1 : c.rect.u0; + float * t = &vLeafTexCoords[i * 8]; + const float vTop = g_bTextureFlip ? c.rect.v0 : 1.0f - c.rect.v0; + const float vBottom = g_bTextureFlip ? c.rect.v1 : 1.0f - c.rect.v1; + t[0] = uR; t[1] = vTop; + t[2] = uL; t[3] = vTop; + t[4] = uL; t[5] = vBottom; + t[6] = uR; t[7] = vBottom; + vLeafTexPtrs[i] = t; + vLeafCoordPtrs[i] = &vLeafCoords[i * 16]; + } + bComputed = true; + } + + // SDK leaf clusters face the camera given to SetCamera. + void UpdateLeafCoords() + { + const Vec3 dir = normalize(Vec3(g_afCameraDir[0], g_afCameraDir[1], g_afCameraDir[2])); + Vec3 right = cross(dir, Vec3(0.0f, 0.0f, 1.0f)); + right = dot(right, right) < 1e-6f ? Vec3(1.0f, 0.0f, 0.0f) : normalize(right); + const Vec3 up = normalize(cross(right, dir)); + for (size_t i = 0; i < vClusters.size(); ++i) + { + const SCluster & c = vClusters[i]; + const Vec3 r = right * (c.fWidth * 0.5f), u = up * (c.fHeight * 0.5f); + const Vec3 corners[4] = { r + u, u - r, Vec3() - r - u, r - u }; + float * p = &vLeafCoords[i * 16]; + for (int k = 0; k < 4; ++k) + { + p[k * 4 + 0] = corners[k].x; + p[k * 4 + 1] = corners[k].y; + p[k * 4 + 2] = corners[k].z; + p[k * 4 + 3] = 0.0f; + } + } + } +}; + +} // namespace + +struct CSpeedTreeRT::SImpl +{ + std::shared_ptr pTree = std::make_shared(); + const CSpeedTreeRT * pInstanceOf = nullptr; + float afPosition[3] = { 0.0f, 0.0f, 0.0f }; + float fLodLevel = 1.0f; + float fLodNear = 100.0f, fLodFar = 1000.0f; + float fWindStrength = 0.0f; + unsigned int uiLocalStart = 0, uiLocalSpan = 0; + unsigned int uiSeed = 1; + bool bLeafRocking = false; + unsigned int uiRockingGroups = 1; + ELightingMethod eBranchLighting = LIGHT_STATIC, eLeafLighting = LIGHT_STATIC, eFrondLighting = LIGHT_STATIC; + EWindMethod eBranchWind = WIND_NONE, eLeafWind = WIND_NONE, eFrondWind = WIND_NONE; + float afLeafLodSizes[1] = { 1.0f }; +}; + +CSpeedTreeRT::SGeometry::SIndexed::SIndexed() + : m_nDiscreteLodLevel(-1), m_usNumStrips(0), m_pStripLengths(nullptr), m_pStrips(nullptr), m_usVertexCount(0), + m_pColors(nullptr), m_pNormals(nullptr), m_pBinormals(nullptr), m_pTangents(nullptr), m_pCoords(nullptr), + m_pTexCoords0(nullptr), m_pTexCoords1(nullptr), m_pWindWeights(nullptr), m_pWindMatrixIndices(nullptr) +{ +} + +CSpeedTreeRT::SGeometry::SIndexed::~SIndexed() {} + +CSpeedTreeRT::SGeometry::SLeaf::SLeaf() + : m_bIsActive(false), m_fAlphaTestValue(0.0f), m_nDiscreteLodLevel(-1), m_usLeafCount(0), m_pLeafMapIndices(nullptr), + m_pLeafClusterIndices(nullptr), m_pCenterCoords(nullptr), m_pLeafMapTexCoords(nullptr), m_pLeafMapCoords(nullptr), + m_pColors(nullptr), m_pNormals(nullptr), m_pBinormals(nullptr), m_pTangents(nullptr), m_pWindWeights(nullptr), + m_pWindMatrixIndices(nullptr) +{ +} + +CSpeedTreeRT::SGeometry::SLeaf::~SLeaf() {} + +CSpeedTreeRT::SGeometry::SBillboard::SBillboard() + : m_bIsActive(false), m_pTexCoords(nullptr), m_pCoords(nullptr), m_fAlphaTestValue(0.0f) +{ +} + +CSpeedTreeRT::SGeometry::SBillboard::~SBillboard() {} + +CSpeedTreeRT::SGeometry::SGeometry() : m_fBranchAlphaTestValue(0.0f), m_fFrondAlphaTestValue(0.0f) {} +CSpeedTreeRT::SGeometry::~SGeometry() {} + +CSpeedTreeRT::STextures::STextures() + : m_pBranchTextureFilename(nullptr), m_uiLeafTextureCount(0), m_pLeafTextureFilenames(nullptr), + m_uiFrondTextureCount(0), m_pFrondTextureFilenames(nullptr), m_pCompositeFilename(nullptr), + m_pSelfShadowFilename(nullptr) +{ +} + +CSpeedTreeRT::STextures::~STextures() {} + +CSpeedTreeRT::CSpeedTreeRT() : m_pImpl(new SImpl) {} +CSpeedTreeRT::~CSpeedTreeRT() { delete m_pImpl; } + +void * CSpeedTreeRT::operator new(size_t nSize) { return ::operator new(nSize); } +void * CSpeedTreeRT::operator new[](size_t nSize) { return ::operator new[](nSize); } +void CSpeedTreeRT::operator delete(void * pRawMemory) { ::operator delete(pRawMemory); } +void CSpeedTreeRT::operator delete[](void * pRawMemory) { ::operator delete[](pRawMemory); } + +bool CSpeedTreeRT::LoadTree(const char * pFilename) +{ + SetError(pFilename ? "SpeedTreeRT: file loading is not supported, pass the pack block" : "SpeedTreeRT: no file name"); + return false; +} + +bool CSpeedTreeRT::LoadTree(const unsigned char * pBlock, unsigned int nNumBytes) +{ + if (!pBlock || nNumBytes < 16) + { + SetError("SpeedTreeRT: empty tree block"); + return false; + } + m_pImpl->pTree = std::make_shared(); + m_pImpl->pTree->Parse(pBlock, nNumBytes); + return true; +} + +bool CSpeedTreeRT::Compute(const float * /*pTransform*/, unsigned int nSeed, bool /*bCompositeStrips*/) +{ + STreeData & tree = *m_pImpl->pTree; + if (tree.bComputed) + { + SetError("SpeedTreeRT: tree already computed"); + return false; + } + m_pImpl->uiSeed = nSeed; + tree.Compute(); + return true; +} + +CSpeedTreeRT * CSpeedTreeRT::MakeInstance(void) +{ + CSpeedTreeRT * pInstance = new CSpeedTreeRT; + *pInstance->m_pImpl = *m_pImpl; + pInstance->m_pImpl->pInstanceOf = this; + return pInstance; +} + +const CSpeedTreeRT * CSpeedTreeRT::InstanceOf(void) const { return m_pImpl->pInstanceOf; } + +void CSpeedTreeRT::DeleteTransientData(void) {} + +void CSpeedTreeRT::GetTreeSize(float & fSize, float & fVariance) const +{ + fSize = m_pImpl->pTree->fSize; + fVariance = m_pImpl->pTree->fVariance; +} + +void CSpeedTreeRT::SetTreeSize(float fNewSize, float fNewVariance) +{ + m_pImpl->pTree->fSize = fNewSize; + m_pImpl->pTree->fVariance = fNewVariance; +} + +unsigned int CSpeedTreeRT::GetSeed() const { return m_pImpl->uiSeed; } + +const float * CSpeedTreeRT::GetTreePosition(void) const { return m_pImpl->afPosition; } + +void CSpeedTreeRT::SetTreePosition(float x, float y, float z) +{ + m_pImpl->afPosition[0] = x; + m_pImpl->afPosition[1] = y; + m_pImpl->afPosition[2] = z; +} + +CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetBranchLightingMethod(void) const { return m_pImpl->eBranchLighting; } +void CSpeedTreeRT::SetBranchLightingMethod(ELightingMethod eMethod) { m_pImpl->eBranchLighting = eMethod; } +CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetLeafLightingMethod(void) const { return m_pImpl->eLeafLighting; } +void CSpeedTreeRT::SetLeafLightingMethod(ELightingMethod eMethod) { m_pImpl->eLeafLighting = eMethod; } +CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetFrondLightingMethod(void) const { return m_pImpl->eFrondLighting; } +void CSpeedTreeRT::SetFrondLightingMethod(ELightingMethod eMethod) { m_pImpl->eFrondLighting = eMethod; } + +float CSpeedTreeRT::GetLeafLightingAdjustment() const { return m_pImpl->pTree->fLeafLightingAdjustment; } +void CSpeedTreeRT::SetLeafLightingAdjustment(float fScalar) { m_pImpl->pTree->fLeafLightingAdjustment = fScalar; } + +bool CSpeedTreeRT::GetLightState(unsigned int nLightIndex) { return nLightIndex < 8 && g_aLights[nLightIndex].bOn; } + +void CSpeedTreeRT::SetLightState(unsigned int nLightIndex, bool bLightOn) +{ + if (nLightIndex < 8) + g_aLights[nLightIndex].bOn = bLightOn; +} + +const float * CSpeedTreeRT::GetLightAttributes(unsigned int nLightIndex) +{ + return nLightIndex < 8 ? g_aLights[nLightIndex].afAttributes : nullptr; +} + +void CSpeedTreeRT::SetLightAttributes(unsigned int nLightIndex, const float * pLightAttributes) +{ + if (nLightIndex < 8 && pLightAttributes) + std::memcpy(g_aLights[nLightIndex].afAttributes, pLightAttributes, sizeof(g_aLights[nLightIndex].afAttributes)); +} + +const float * CSpeedTreeRT::GetBranchMaterial(void) const { return m_pImpl->pTree->afBranchMaterial; } +void CSpeedTreeRT::SetBranchMaterial(const float * pMaterial) { std::memcpy(m_pImpl->pTree->afBranchMaterial, pMaterial, 13 * sizeof(float)); } +const float * CSpeedTreeRT::GetLeafMaterial(void) const { return m_pImpl->pTree->afLeafMaterial; } +void CSpeedTreeRT::SetLeafMaterial(const float * pMaterial) { std::memcpy(m_pImpl->pTree->afLeafMaterial, pMaterial, 13 * sizeof(float)); } +const float * CSpeedTreeRT::GetFrondMaterial(void) const { return m_pImpl->pTree->afFrondMaterial; } +void CSpeedTreeRT::SetFrondMaterial(const float * pMaterial) { std::memcpy(m_pImpl->pTree->afFrondMaterial, pMaterial, 13 * sizeof(float)); } + +void CSpeedTreeRT::GetCamera(float * pPosition, float * pDirection) +{ + if (pPosition) + std::memcpy(pPosition, g_afCameraPos, sizeof(g_afCameraPos)); + if (pDirection) + std::memcpy(pDirection, g_afCameraDir, sizeof(g_afCameraDir)); +} + +void CSpeedTreeRT::SetCamera(const float * pPosition, const float * pDirection) +{ + if (pPosition) + std::memcpy(g_afCameraPos, pPosition, sizeof(g_afCameraPos)); + if (pDirection) + std::memcpy(g_afCameraDir, pDirection, sizeof(g_afCameraDir)); +} + +// WRAPPER_USE_NO_WIND: the wind entry points only keep their state. +void CSpeedTreeRT::SetTime(float /*fTime*/) {} +void CSpeedTreeRT::ComputeWindEffects(bool /*bBranches*/, bool /*bLeaves*/, bool /*bFronds*/) {} +void CSpeedTreeRT::ResetLeafWindState(void) {} +bool CSpeedTreeRT::GetLeafRockingState(void) const { return m_pImpl->bLeafRocking; } +void CSpeedTreeRT::SetLeafRockingState(bool bFlag) { m_pImpl->bLeafRocking = bFlag; } +void CSpeedTreeRT::SetNumLeafRockingGroups(unsigned int nRockingGroups) { m_pImpl->uiRockingGroups = nRockingGroups; } +CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetLeafWindMethod(void) const { return m_pImpl->eLeafWind; } +void CSpeedTreeRT::SetLeafWindMethod(EWindMethod eMethod) { m_pImpl->eLeafWind = eMethod; } +CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetBranchWindMethod(void) const { return m_pImpl->eBranchWind; } +void CSpeedTreeRT::SetBranchWindMethod(EWindMethod eMethod) { m_pImpl->eBranchWind = eMethod; } +CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetFrondWindMethod(void) const { return m_pImpl->eFrondWind; } +void CSpeedTreeRT::SetFrondWindMethod(EWindMethod eMethod) { m_pImpl->eFrondWind = eMethod; } +float CSpeedTreeRT::GetWindStrength(void) const { return m_pImpl->fWindStrength; } + +float CSpeedTreeRT::SetWindStrength(float fNewStrength, float /*fOldStrength*/, float /*fFrequencyTimeOffset*/) +{ + const float fOld = m_pImpl->fWindStrength; + m_pImpl->fWindStrength = fNewStrength; + return fOld; +} + +void CSpeedTreeRT::SetNumWindMatrices(unsigned int /*nNumMatrices*/) {} +void CSpeedTreeRT::SetWindMatrix(unsigned int /*nMatrixIndex*/, const float * /*pMatrix*/) {} + +void CSpeedTreeRT::GetLocalMatrices(unsigned int & nStartingIndex, unsigned int & nMatrixSpan) +{ + nStartingIndex = m_pImpl->uiLocalStart; + nMatrixSpan = m_pImpl->uiLocalSpan; +} + +void CSpeedTreeRT::SetLocalMatrices(unsigned int nStartingMatrix, unsigned int nMatrixSpan) +{ + m_pImpl->uiLocalStart = nStartingMatrix; + m_pImpl->uiLocalSpan = nMatrixSpan; +} + +void CSpeedTreeRT::ComputeLodLevel(void) +{ + const float dx = m_pImpl->afPosition[0] - g_afCameraPos[0]; + const float dy = m_pImpl->afPosition[1] - g_afCameraPos[1]; + const float dz = m_pImpl->afPosition[2] - g_afCameraPos[2]; + const float fDistance = std::sqrt(dx * dx + dy * dy + dz * dz); + const float fRange = std::max(1e-3f, m_pImpl->fLodFar - m_pImpl->fLodNear); + m_pImpl->fLodLevel = 1.0f - std::clamp((fDistance - m_pImpl->fLodNear) / fRange, 0.0f, 1.0f); +} + +float CSpeedTreeRT::GetLodLevel(void) const { return m_pImpl->fLodLevel; } +void CSpeedTreeRT::SetLodLevel(float fLodLevel) { m_pImpl->fLodLevel = std::clamp(fLodLevel, 0.0f, 1.0f); } +void CSpeedTreeRT::SetDropToBillboard(bool bFlag) { g_bDropToBillboard = bFlag; } + +void CSpeedTreeRT::GetLodLimits(float & fNear, float & fFar) const +{ + fNear = m_pImpl->fLodNear; + fFar = m_pImpl->fLodFar; +} + +void CSpeedTreeRT::SetLodLimits(float fNear, float fFar) +{ + m_pImpl->fLodNear = fNear; + m_pImpl->fLodFar = fFar; +} + +short CSpeedTreeRT::GetDiscreteBranchLodLevel(float /*fLodLevel*/) const { return 0; } +unsigned short CSpeedTreeRT::GetDiscreteLeafLodLevel(float /*fLodLevel*/) const { return 0; } +short CSpeedTreeRT::GetDiscreteFrondLodLevel(float /*fLodLevel*/) const { return 0; } +unsigned short CSpeedTreeRT::GetNumBranchLodLevels(void) const { return 1; } +unsigned short CSpeedTreeRT::GetNumLeafLodLevels(void) const { return 1; } +unsigned short CSpeedTreeRT::GetNumFrondLodLevels(void) const { return 1; } + +const float * CSpeedTreeRT::GetLeafBillboardTable(unsigned int & nEntryCount) const +{ + nEntryCount = 0; + return nullptr; +} + +const float * CSpeedTreeRT::GetLeafLodSizeAdjustments(void) { return m_pImpl->afLeafLodSizes; } + +void CSpeedTreeRT::GetGeometry(SGeometry & sGeometry, unsigned long ulBitVector, short /*sOverrideBranchLodValue*/, + short /*sOverrideFrondLodValue*/, short /*sOverrideLeafLodValue*/) +{ + STreeData & tree = *m_pImpl->pTree; + + if (ulBitVector & SpeedTree_BranchGeometry) + { + SGeometry::SIndexed & b = sGeometry.m_sBranches; + b = SGeometry::SIndexed(); + b.m_nDiscreteLodLevel = 0; + b.m_usVertexCount = static_cast(tree.vBranchCoords.size() / 3); + b.m_usNumStrips = tree.usBranchStripLength ? 1 : 0; + b.m_pStripLengths = &tree.usBranchStripLength; + b.m_pStrips = &tree.pBranchStrip; + b.m_pColors = tree.vBranchColors.data(); + b.m_pNormals = tree.vBranchNormals.data(); + b.m_pCoords = tree.vBranchCoords.data(); + b.m_pTexCoords0 = tree.vBranchTex0.data(); + b.m_pTexCoords1 = tree.vBranchTex1.data(); + sGeometry.m_fBranchAlphaTestValue = kAlphaTestValue; + } + + if (ulBitVector & SpeedTree_FrondGeometry) + { + sGeometry.m_sFronds = SGeometry::SIndexed(); + sGeometry.m_sFronds.m_nDiscreteLodLevel = 0; + sGeometry.m_fFrondAlphaTestValue = kAlphaTestValue; + } + + if (ulBitVector & SpeedTree_LeafGeometry) + { + tree.UpdateLeafCoords(); + SGeometry::SLeaf & l = sGeometry.m_sLeaves0; + l = SGeometry::SLeaf(); + l.m_bIsActive = !tree.vClusters.empty(); + l.m_fAlphaTestValue = kAlphaTestValue; + l.m_nDiscreteLodLevel = 0; + l.m_usLeafCount = static_cast(tree.vClusters.size()); + l.m_pLeafMapIndices = tree.vLeafMapIndices.data(); + l.m_pCenterCoords = tree.vLeafCenters.data(); + l.m_pLeafMapTexCoords = tree.vLeafTexPtrs.data(); + l.m_pLeafMapCoords = tree.vLeafCoordPtrs.data(); + l.m_pColors = tree.vLeafColors.data(); + l.m_pNormals = tree.vLeafNormals.data(); + sGeometry.m_sLeaves1 = SGeometry::SLeaf(); + } + + if (ulBitVector & SpeedTree_BillboardGeometry) + { + sGeometry.m_sBillboard0 = SGeometry::SBillboard(); + sGeometry.m_sBillboard1 = SGeometry::SBillboard(); + sGeometry.m_sHorizontalBillboard = SGeometry::SBillboard(); + } +} + +void CSpeedTreeRT::GetTextures(STextures & sTextures) const +{ + const STreeData & tree = *m_pImpl->pTree; + sTextures = STextures(); + sTextures.m_pBranchTextureFilename = tree.strBranchTexture.c_str(); + sTextures.m_pCompositeFilename = tree.strCompositeTexture.empty() ? nullptr : tree.strCompositeTexture.c_str(); + sTextures.m_pSelfShadowFilename = tree.strSelfShadowTexture.empty() ? nullptr : tree.strSelfShadowTexture.c_str(); +} + +bool CSpeedTreeRT::GetTextureFlip(void) { return g_bTextureFlip; } +void CSpeedTreeRT::SetTextureFlip(bool bFlag) { g_bTextureFlip = bFlag; } + +const char * CSpeedTreeRT::GetCurrentError(void) { return g_strError.c_str(); } +void CSpeedTreeRT::ResetError(void) { g_strError.clear(); } + +void CSpeedTreeRT::GetBoundingBox(float * pBounds) const +{ + std::memcpy(pBounds, m_pImpl->pTree->afBoundingBox, 6 * sizeof(float)); +} + +unsigned int CSpeedTreeRT::GetCollisionObjectCount(void) { return 1; } + +void CSpeedTreeRT::GetCollisionObject(unsigned int /*nIndex*/, ECollisionObjectType & eType, float * pPosition, float * pDimensions) +{ + const STreeData & tree = *m_pImpl->pTree; + eType = CO_CYLINDER; + if (pPosition) + pPosition[0] = pPosition[1] = pPosition[2] = 0.0f; + if (pDimensions) + { + pDimensions[0] = tree.fTrunkRadius; + pDimensions[1] = tree.fTrunkHeight; + pDimensions[2] = 0.0f; + } +} diff --git a/extension/src/platform/SpeedTreeLib/SpeedTreeWrapper.cpp b/extension/src/platform/SpeedTreeLib/SpeedTreeWrapper.cpp deleted file mode 100644 index cfe7c7a8..00000000 --- a/extension/src/platform/SpeedTreeLib/SpeedTreeWrapper.cpp +++ /dev/null @@ -1,1097 +0,0 @@ -#include "SpeedTreeLib/StdAfx.h" -#include "SpeedTreeLib/SpeedTreeWrapper.h" -#include "SpeedTreeLib/SpeedTreeForestDirectX8.h" - -#include "EterBase/Filename.h" -#include "EterBase/Timer.h" -#include "EterLib/Camera.h" -#include "EterLib/ResourceManager.h" -#include "EterLib/StateManager.h" -#include "EterPack/EterPackManager.h" -#include "../EterLib/RenderCommands3D.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -struct CSpeedTreeRT::SGeometry -{ - std::string species; - std::string composite_name; - float height = 1000.0f; - float variance = 0.0f; - float radius = 350.0f; - float trunk_radius = 35.0f; - float trunk_height = 750.0f; - uint32_t branch_index_count = 0; - uint32_t leaf_vertex_count = 0; -}; - -struct CSpeedTreeRT::STextures -{ - std::string branch_tex; - std::string composite_tex; - std::string shadow_tex; -}; - -float CSpeedTreeRT::GetLeafLightingAdjustment() const -{ - return 0.5f; -} - -float CSpeedTreeRT::SetWindStrength(float fNewStrength, float /*fOldStrength*/, float /*fFrequencyTimeOffset*/) -{ - return fNewStrength; -} - -void CSpeedTreeRT::GetCollisionObject(unsigned int /*nIndex*/, ECollisionObjectType & eType, float * pPosition, float * pDimensions) -{ - eType = CO_CYLINDER; - if (pPosition) - { - pPosition[0] = 0.0f; - pPosition[1] = 0.0f; - pPosition[2] = 0.0f; - } - if (pDimensions) - { - pDimensions[0] = 35.0f; - pDimensions[1] = 600.0f; - pDimensions[2] = 0.0f; - } -} - -namespace -{ - -constexpr float kPI = 3.14159265358979323846f; -constexpr float kTAU = 2.0f * kPI; -constexpr DWORD kTreeFVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1; - -std::string to_lower_str(std::string s) -{ - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); - return s; -} - -std::string basename_only(std::string path) -{ - for (char & c : path) - { - if (c == '\\') - c = '/'; - } - const size_t slash = path.find_last_of('/'); - return slash == std::string::npos ? path : path.substr(slash + 1); -} - -std::string replace_ext_dds(std::string path) -{ - const size_t dot = path.find_last_of('.'); - if (dot != std::string::npos) - path.resize(dot); - return path + ".dds"; -} - -std::string sibling_dds_path(const std::string & spt_file, const std::string & tex_ref) -{ - if (tex_ref.empty()) - return ""; - std::string spt_copy = spt_file; - std::string dir = CFileNameHelper::GetPath(spt_copy); - std::string name = replace_ext_dds(basename_only(tex_ref)); - return dir + name; -} - -bool is_conifer_species(const std::string & hint) -{ - const std::string h = to_lower_str(hint); - static const char * kw[] = { - "cedar", "cypress", "pine", "fir", "spruce", "conifer", "juniper", "christmastree" - }; - for (const char * k : kw) - { - if (h.find(k) != std::string::npos) - return true; - } - return false; -} - -bool is_palm_species(const std::string & hint) -{ - const std::string h = to_lower_str(hint); - static const char * kw[] = { "palm", "banana", "aloe", "fern", "joshua" }; - for (const char * k : kw) - { - if (h.find(k) != std::string::npos) - return true; - } - return false; -} - -uint32_t hash32(uint32_t s) -{ - s ^= s >> 16; - s *= 0x7feb352dU; - s ^= s >> 15; - s *= 0x846ca68bU; - s ^= s >> 16; - return s; -} - -float hash01(uint32_t s) -{ - return float(hash32(s) & 0x00FFFFFFU) / float(0x01000000U); -} - -uint32_t species_seed(const std::string & s) -{ - uint32_t h = 2166136261U; - for (unsigned char c : s) - { - h ^= c; - h *= 16777619U; - } - return h; -} - -struct UVRect -{ - float u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f; -}; - -std::vector foliage_rects(const std::string & species, const std::string & composite, bool atlas) -{ - if (!atlas) - return { { 0.0f, 0.0f, 1.0f, 1.0f } }; - const std::string s = to_lower_str(species); - const std::string c = to_lower_str(composite); - const bool fall = s.find("fall") != std::string::npos; - const bool winter = s.find("winter") != std::string::npos; - if (c.find("b1") != std::string::npos) - { - if (fall) - return { { 0.00f, 0.05f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } }; - return { - { 0.25f, 0.02f, 0.50f, 0.23f }, - { 0.25f, 0.18f, 0.50f, 0.36f }, - { 0.00f, 0.27f, 0.27f, 0.49f } - }; - } - if (c.find("b2") != std::string::npos) - { - if (fall) - return { { 0.00f, 0.00f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } }; - return { - { 0.50f, 0.38f, 0.75f, 0.63f }, - { 0.50f, 0.63f, 0.75f, 0.88f }, - { 0.00f, 0.38f, 0.25f, 0.62f } - }; - } - if (c.find("b3") != std::string::npos) - { - if (fall) - return { { 0.25f, 0.25f, 0.50f, 0.50f } }; - return { - { 0.00f, 0.25f, 0.25f, 0.50f }, - { 0.00f, 0.50f, 0.25f, 0.75f }, - { 0.25f, 0.50f, 0.50f, 0.75f } - }; - } - if (c.find("n1") != std::string::npos) - { - if (winter) - return { { 0.00f, 0.36f, 0.50f, 0.58f }, { 0.25f, 0.55f, 0.52f, 0.75f } }; - return { { 0.00f, 0.72f, 0.28f, 0.96f }, { 0.25f, 0.74f, 0.53f, 0.97f } }; - } - if (c.find("n2") != std::string::npos) - { - return { - { 0.00f, 0.48f, 0.27f, 0.75f }, - { 0.26f, 0.73f, 0.58f, 1.00f }, - { 0.75f, 0.48f, 1.00f, 0.80f } - }; - } - return { { 0.0f, 0.0f, 1.0f, 1.0f } }; -} - -struct TreeVertex -{ - TPosition position; - TNormal normal; - DWORD diffuse; - TTextureCoordinate texCoord; -}; - -struct TreeMeshBuilder -{ - std::vector verts; - std::vector indices; - DWORD color = 0xFFFFFFFFu; - - static TreeVertex make_vtx(const D3DXVECTOR3 & p, const D3DXVECTOR3 & n, DWORD col, float u, float v) - { - TreeVertex out{}; - out.position = TPosition(p.x, p.y, p.z); - out.normal = TNormal(n.x, n.y, n.z); - out.diffuse = col; - out.texCoord = TTextureCoordinate(u, v); - return out; - } - - void add_tube_quad( - const D3DXVECTOR3 & b0, - const D3DXVECTOR3 & b1, - const D3DXVECTOR3 & t1, - const D3DXVECTOR3 & t0, - const D3DXVECTOR3 & n0, - const D3DXVECTOR3 & n1, - float u0, - float u1, - float v0, - float v1) - { - if (verts.size() + 4 > 65530) - return; - const uint16_t base = static_cast(verts.size()); - verts.push_back(make_vtx(b0, n0, color, u0, v0)); - verts.push_back(make_vtx(b1, n1, color, u1, v0)); - verts.push_back(make_vtx(t1, n1, color, u1, v1)); - verts.push_back(make_vtx(t0, n0, color, u0, v1)); - indices.push_back(base + 0); - indices.push_back(base + 2); - indices.push_back(base + 1); - indices.push_back(base + 0); - indices.push_back(base + 3); - indices.push_back(base + 2); - } - - void add_leaf_quad_unindexed( - const D3DXVECTOR3 & a, - const D3DXVECTOR3 & b, - const D3DXVECTOR3 & c, - const D3DXVECTOR3 & d, - const UVRect & r, - bool flip_u) - { - const D3DXVECTOR3 ab = b - a; - const D3DXVECTOR3 ad = d - a; - D3DXVECTOR3 nn; - D3DXVec3Cross(&nn, &ab, &ad); - D3DXVec3Normalize(&nn, &nn); - // Bias leaf normals upward slightly for softer canopy lighting - nn.z = std::fabs(nn.z) * 0.5f + 0.5f; - D3DXVec3Normalize(&nn, &nn); - - const float l = flip_u ? r.u1 : r.u0; - const float rr = flip_u ? r.u0 : r.u1; - const TreeVertex v0 = make_vtx(a, nn, color, l, r.v1); - const TreeVertex v1 = make_vtx(b, nn, color, rr, r.v1); - const TreeVertex v2 = make_vtx(c, nn, color, rr, r.v0); - const TreeVertex v3 = make_vtx(d, nn, color, l, r.v0); - - verts.push_back(v0); - verts.push_back(v1); - verts.push_back(v2); - verts.push_back(v0); - verts.push_back(v2); - verts.push_back(v3); - } -}; - -// Z-up tube generator (from/to in Metin2 Z-up centimeters) -void add_tube_zup( - TreeMeshBuilder & m, - const D3DXVECTOR3 & from, - const D3DXVECTOR3 & to, - float r0, - float r1, - int seg, - float bark_repeat) -{ - D3DXVECTOR3 axis = to - from; - const float len = D3DXVec3Length(&axis); - if (len < 1e-3f) - return; - axis /= len; - - const D3DXVECTOR3 helper = (std::fabs(axis.z) > 0.9f) ? D3DXVECTOR3(1.0f, 0.0f, 0.0f) : D3DXVECTOR3(0.0f, 0.0f, 1.0f); - D3DXVECTOR3 u, w; - D3DXVec3Cross(&u, &axis, &helper); - D3DXVec3Normalize(&u, &u); - D3DXVec3Cross(&w, &axis, &u); - D3DXVec3Normalize(&w, &w); - - for (int i = 0; i < seg; ++i) - { - const float a0 = float(i) / float(seg) * kTAU; - const float a1 = float(i + 1) / float(seg) * kTAU; - const D3DXVECTOR3 n0 = u * std::cos(a0) + w * std::sin(a0); - const D3DXVECTOR3 n1 = u * std::cos(a1) + w * std::sin(a1); - m.add_tube_quad( - from + n0 * r0, - from + n1 * r0, - to + n1 * r1, - to + n0 * r1, - n0, - n1, - float(i) / float(seg), - float(i + 1) / float(seg), - bark_repeat, - 0.0f); - } -} - -void build_branches_zup( - TreeMeshBuilder & wood, - const std::string & species, - float H, - bool conifer, - bool palm) -{ - const float trunk_top = H * (palm ? 0.82f : (conifer ? 0.90f : 0.76f)); - const float trunk_r = H * (palm ? 0.028f : 0.035f); - const float H_m = H * 0.01f; - add_tube_zup( - wood, - D3DXVECTOR3(0.0f, 0.0f, 0.0f), - D3DXVECTOR3(0.0f, 0.0f, trunk_top), - trunk_r * 1.35f, - trunk_r * 0.42f, - 9, - H_m * 0.22f); - if (palm) - return; - - const int count = conifer ? 9 : 8; - const uint32_t seed = species_seed(species); - for (int i = 0; i < count; ++i) - { - const float f = (i + 1.0f) / (count + 1.0f); - const float z = H * (conifer ? (0.28f + f * 0.52f) : (0.32f + f * 0.34f)); - const float angle = kTAU * (f * 1.6180339f + hash01(seed + i * 17U)); - const float len = H * (conifer - ? (0.24f * (1.0f - f * 0.55f)) - : (0.18f + 0.08f * hash01(seed + i * 29U))); - const D3DXVECTOR3 from(0.0f, 0.0f, z); - const D3DXVECTOR3 to( - std::cos(angle) * len, - std::sin(angle) * len, - z + H * (conifer ? 0.06f : (0.10f + 0.06f * hash01(seed + i * 31U)))); - add_tube_zup( - wood, - from, - to, - trunk_r * (0.55f - 0.20f * f), - trunk_r * 0.12f, - 6, - H_m * 0.08f); - if (!conifer && (i % 2 == 0)) - { - const float side = angle + (hash01(seed + i * 37U) > 0.5f ? 0.65f : -0.65f); - const D3DXVECTOR3 tip = to + D3DXVECTOR3(std::cos(side), std::sin(side), 0.65f) * (len * 0.42f); - add_tube_zup(wood, to, tip, trunk_r * 0.16f, trunk_r * 0.05f, 5, H_m * 0.04f); - } - } -} - -void build_leaves_zup( - TreeMeshBuilder & leaves, - const std::string & species, - float H, - bool conifer, - bool palm, - const std::vector & rects) -{ - const uint32_t seed = species_seed(species); - const int count = palm ? 16 : 24; - for (int i = 0; i < count; ++i) - { - const float a = kTAU * (float(i) * 0.6180339f + hash01(seed + i * 101U) * 0.15f); - D3DXVECTOR3 center; - float width = 1.0f; - float height = 1.0f; - if (palm) - { - const float radial = H * (0.10f + 0.18f * hash01(seed + i * 103U)); - center = D3DXVECTOR3( - std::cos(a) * radial, - std::sin(a) * radial, - H * (0.78f + 0.12f * hash01(seed + i * 107U))); - width = H * 0.32f; - height = H * 0.18f; - } - else if (conifer) - { - const float zf = 0.30f + 0.62f * (float(i) + 0.5f) / float(count); - const float radial = H * 0.23f * (1.0f - zf * 0.70f) * - (0.35f + 0.65f * hash01(seed + i * 109U)); - center = D3DXVECTOR3(std::cos(a) * radial, std::sin(a) * radial, H * zf); - width = H * (0.18f + 0.10f * (1.0f - zf)); - height = H * 0.18f; - } - else - { - const float zf = hash01(seed + i * 109U); - const float zn = zf * 2.0f - 1.0f; - const float radial = H * 0.34f * std::sqrt(std::max(0.05f, 1.0f - zn * zn)) * - (0.25f + 0.75f * std::sqrt(hash01(seed + i * 113U))); - center = D3DXVECTOR3( - std::cos(a) * radial, - std::sin(a) * radial, - H * (0.58f + zf * 0.34f)); - width = H * (0.23f + 0.10f * hash01(seed + i * 127U)); - height = H * (0.15f + 0.08f * hash01(seed + i * 131U)); - } - - const D3DXVECTOR3 right(std::cos(a + kPI * 0.5f), std::sin(a + kPI * 0.5f), 0.0f); - const D3DXVECTOR3 up(0.0f, 0.0f, 1.0f); - const UVRect & uv = rects[size_t(i) % rects.size()]; - - auto add_card = [&](const D3DXVECTOR3 & r, bool flip) { - leaves.add_leaf_quad_unindexed( - center - r * (width * 0.5f) - up * (height * 0.5f), - center + r * (width * 0.5f) - up * (height * 0.5f), - center + r * (width * 0.5f) + up * (height * 0.5f), - center - r * (width * 0.5f) + up * (height * 0.5f), - uv, - flip); - }; - add_card(right, (i & 1) != 0); - const D3DXVECTOR3 crossed(std::cos(a), std::sin(a), 0.0f); - add_card(crossed, (i & 1) == 0); - } -} - -} // namespace - -decltype(CSpeedTreeWrapper::ms_bSelfShadowOn) CSpeedTreeWrapper::ms_bSelfShadowOn = true; -decltype(CSpeedTreeWrapper::ms_dwBranchVertexShader) CSpeedTreeWrapper::ms_dwBranchVertexShader = kTreeFVF; -decltype(CSpeedTreeWrapper::ms_dwLeafVertexShader) CSpeedTreeWrapper::ms_dwLeafVertexShader = kTreeFVF; - -CSpeedTreeWrapper::CSpeedTreeWrapper() - : m_pSpeedTree(new CSpeedTreeRT) - , m_pTextureInfo(NULL) - , m_bIsInstance(false) - , m_pInstanceOf(NULL) - , m_pGeometryCache(NULL) - , m_pBranchVertexBuffer(NULL) - , m_unBranchVertexCount(0) - , m_pBranchIndexBuffer(NULL) - , m_pBranchIndexCounts(NULL) - , m_pFrondVertexBuffer(NULL) - , m_unFrondVertexCount(0) - , m_pFrondIndexBuffer(NULL) - , m_pFrondIndexCounts(NULL) - , m_usNumLeafLods(0) - , m_pLeafVertexBuffer(NULL) - , m_pLeavesUpdatedByCpu(NULL) -{ - m_afPos[0] = m_afPos[1] = m_afPos[2] = 0.0f; - std::memset(m_afBoundingBox, 0, sizeof(m_afBoundingBox)); -} - -CSpeedTreeWrapper::~CSpeedTreeWrapper() -{ - if (!m_bIsInstance) - { - SAFE_RELEASE(m_pBranchVertexBuffer); - SAFE_RELEASE(m_pBranchIndexBuffer); - SAFE_DELETE_ARRAY(m_pBranchIndexCounts); - - SAFE_RELEASE(m_pFrondVertexBuffer); - SAFE_RELEASE(m_pFrondIndexBuffer); - SAFE_DELETE_ARRAY(m_pFrondIndexCounts); - - if (m_pLeafVertexBuffer) - { - for (unsigned short i = 0; i < m_usNumLeafLods; ++i) - SAFE_RELEASE(m_pLeafVertexBuffer[i]); - SAFE_DELETE_ARRAY(m_pLeafVertexBuffer); - } - SAFE_DELETE_ARRAY(m_pLeavesUpdatedByCpu); - SAFE_DELETE(m_pTextureInfo); - SAFE_DELETE(m_pGeometryCache); - } - - SAFE_DELETE(m_pSpeedTree); - Clear(); -} - -auto CSpeedTreeWrapper::SetVertexShaders(DWORD dwBranchVertexShader, DWORD dwLeafVertexShader) -> void -{ - ms_dwBranchVertexShader = dwBranchVertexShader; - ms_dwLeafVertexShader = dwLeafVertexShader; -} - -auto CSpeedTreeWrapper::GetPosition() -> const float * -{ - return m_afPos; -} - -auto CSpeedTreeWrapper::SetPosition(float x, float y, float z) -> void -{ - m_afPos[0] = x; - m_afPos[1] = y; - m_afPos[2] = z; - CGraphicObjectInstance::SetPosition(x, y, z); - CGraphicObjectInstance::Transform(); - CalculateBBox(); -} - -auto CSpeedTreeWrapper::GetBoundingSphere(D3DXVECTOR3 & v3Center, float & fRadius) -> bool -{ - const float fX = m_afBoundingBox[3] - m_afBoundingBox[0]; - const float fY = m_afBoundingBox[4] - m_afBoundingBox[1]; - const float fZ = m_afBoundingBox[5] - m_afBoundingBox[2]; - - v3Center.x = m_afPos[0]; - v3Center.y = m_afPos[1]; - v3Center.z = m_afPos[2] + fZ * 0.5f; - - fRadius = std::sqrt(fX * fX + fY * fY + fZ * fZ) * 0.5f * 0.9f; - return true; -} - -auto CSpeedTreeWrapper::CalculateBBox() -> void -{ - const float fX = m_afBoundingBox[3] - m_afBoundingBox[0]; - const float fY = m_afBoundingBox[4] - m_afBoundingBox[1]; - const float fZ = m_afBoundingBox[5] - m_afBoundingBox[2]; - - m_v3BBoxMin = D3DXVECTOR3(-fX * 0.5f, -fY * 0.5f, 0.0f); - m_v3BBoxMax = D3DXVECTOR3(+fX * 0.5f, +fY * 0.5f, fZ); - - m_v4TBBox[0] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMin.y, m_v3BBoxMin.z, 1.0f); - m_v4TBBox[1] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMax.y, m_v3BBoxMin.z, 1.0f); - m_v4TBBox[2] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMin.y, m_v3BBoxMin.z, 1.0f); - m_v4TBBox[3] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMax.y, m_v3BBoxMin.z, 1.0f); - m_v4TBBox[4] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMin.y, m_v3BBoxMax.z, 1.0f); - m_v4TBBox[5] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMax.y, m_v3BBoxMax.z, 1.0f); - m_v4TBBox[6] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMin.y, m_v3BBoxMax.z, 1.0f); - m_v4TBBox[7] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMax.y, m_v3BBoxMax.z, 1.0f); - - const D3DXMATRIX & c_rmatTransform = GetTransform(); - for (DWORD i = 0; i < 8; ++i) - { - D3DXVec4Transform(&m_v4TBBox[i], &m_v4TBBox[i], &c_rmatTransform); - if (0 == i) - { - m_v3TBBoxMin = D3DXVECTOR3(m_v4TBBox[i].x, m_v4TBBox[i].y, m_v4TBBox[i].z); - m_v3TBBoxMax = m_v3TBBoxMin; - } - else - { - m_v3TBBoxMin.x = std::min(m_v3TBBoxMin.x, m_v4TBBox[i].x); - m_v3TBBoxMax.x = std::max(m_v3TBBoxMax.x, m_v4TBBox[i].x); - m_v3TBBoxMin.y = std::min(m_v3TBBoxMin.y, m_v4TBBox[i].y); - m_v3TBBoxMax.y = std::max(m_v3TBBoxMax.y, m_v4TBBox[i].y); - m_v3TBBoxMin.z = std::min(m_v3TBBoxMin.z, m_v4TBBox[i].z); - m_v3TBBoxMax.z = std::max(m_v3TBBoxMax.z, m_v4TBBox[i].z); - } - } -} - -auto CSpeedTreeWrapper::OnUpdateCollisionData(const CStaticCollisionDataVector * /*pscdVector*/) -> void -{ - ClearCollision(); - D3DXMATRIX mat; - D3DXMatrixTranslation(&mat, m_afPos[0], m_afPos[1], m_afPos[2]); - - for (UINT i = 0; i < GetCollisionObjectCount(); ++i) - { - CSpeedTreeRT::ECollisionObjectType ObjectType = CSpeedTreeRT::CO_CYLINDER; - CStaticCollisionData CollisionData{}; - GetCollisionObject(i, ObjectType, reinterpret_cast(&CollisionData.v3Position), CollisionData.fDimensions); - if (ObjectType == CSpeedTreeRT::CO_BOX) - continue; - if (ObjectType == CSpeedTreeRT::CO_SPHERE) - CollisionData.dwType = COLLISION_TYPE_SPHERE; - else if (ObjectType == CSpeedTreeRT::CO_CYLINDER) - CollisionData.dwType = COLLISION_TYPE_CYLINDER; - AddCollision(&CollisionData, &mat); - } -} - -auto CSpeedTreeWrapper::GetCollisionObjectCount() -> UINT -{ - return 1; -} - -auto CSpeedTreeWrapper::GetCollisionObject( - unsigned int /*nIndex*/, - CSpeedTreeRT::ECollisionObjectType & eType, - float * pPosition, - float * pDimensions) -> void -{ - eType = CSpeedTreeRT::CO_CYLINDER; - if (pPosition) - { - pPosition[0] = 0.0f; - pPosition[1] = 0.0f; - pPosition[2] = 0.0f; - } - if (pDimensions) - { - pDimensions[0] = m_pGeometryCache ? m_pGeometryCache->trunk_radius : 35.0f; - pDimensions[1] = m_pGeometryCache ? m_pGeometryCache->trunk_height : 600.0f; - pDimensions[2] = 0.0f; - } -} - -auto CSpeedTreeWrapper::GetTreeSize(float & r_fSize, float & r_fVariance) -> void -{ - r_fSize = m_pGeometryCache ? m_pGeometryCache->height : 1000.0f; - r_fVariance = m_pGeometryCache ? m_pGeometryCache->variance : 0.0f; -} - -auto CSpeedTreeWrapper::LoadTree( - const char * pszSptFile, - const BYTE * c_pbBlock, - unsigned int uiBlockSize, - unsigned int /*nSeed*/, - float fSize, - float fSizeVariance) -> bool -{ - if (!IsNativeTerrainRenderEnabled()) - return false; - - const std::string spt_path = pszSptFile ? pszSptFile : ""; - std::string bytes; - if (c_pbBlock && uiBlockSize > 0) - { - bytes.assign(reinterpret_cast(c_pbBlock), uiBlockSize); - } - else if (!spt_path.empty()) - { - CMappedFile file; - LPCVOID pvData = NULL; - if (CEterPackManager::Instance().Get(file, spt_path.c_str(), &pvData) && pvData && file.Size() > 0) - bytes.assign(static_cast(pvData), file.Size()); - } - - fmt::SptInfo info; - if (!bytes.empty()) - fmt::sniff_spt(bytes, info); - - m_pTextureInfo = new CSpeedTreeRT::STextures; - for (const std::string & ref : info.texture_refs) - { - if (to_lower_str(ref).find("bark") != std::string::npos) - { - m_pTextureInfo->branch_tex = ref; - break; - } - } - if (m_pTextureInfo->branch_tex.empty() && !info.texture_refs.empty()) - m_pTextureInfo->branch_tex = info.texture_refs.front(); - m_pTextureInfo->composite_tex = info.composite_texture; - m_pTextureInfo->shadow_tex = info.self_shadow_texture; - - if (!m_pTextureInfo->branch_tex.empty()) - { - const std::string p = sibling_dds_path(spt_path, m_pTextureInfo->branch_tex); - LoadTexture(p.c_str(), m_BranchImageInstance); - } - if (!m_pTextureInfo->composite_tex.empty()) - { - const std::string p = sibling_dds_path(spt_path, m_pTextureInfo->composite_tex); - LoadTexture(p.c_str(), m_CompositeImageInstance); - } - if (!m_pTextureInfo->shadow_tex.empty()) - { - const std::string p = sibling_dds_path(spt_path, m_pTextureInfo->shadow_tex); - LoadTexture(p.c_str(), m_ShadowImageInstance); - } - - const float H = (fSize > 150.0f) ? fSize : 1000.0f; - const float R = H * 0.35f; - - m_afBoundingBox[0] = -R; - m_afBoundingBox[1] = -R; - m_afBoundingBox[2] = 0.0f; - m_afBoundingBox[3] = +R; - m_afBoundingBox[4] = +R; - m_afBoundingBox[5] = H; - - m_pGeometryCache = new CSpeedTreeRT::SGeometry; - m_pGeometryCache->species = spt_path; - m_pGeometryCache->composite_name = info.composite_texture; - m_pGeometryCache->height = H; - m_pGeometryCache->variance = (fSizeVariance >= 0.0f) ? fSizeVariance : 0.0f; - m_pGeometryCache->radius = R; - m_pGeometryCache->trunk_radius = H * 0.035f; - m_pGeometryCache->trunk_height = H * 0.75f; - - SetupBuffers(); - return true; -} - -auto CSpeedTreeWrapper::SetupBuffers() -> void -{ - SetupBranchBuffers(); - SetupFrondBuffers(); - SetupLeafBuffers(); -} - -auto CSpeedTreeWrapper::SetupBranchBuffers() -> void -{ - if (!m_pGeometryCache || !ms_lpd3dDevice) - return; - - const float H = m_pGeometryCache->height; - const bool conifer = is_conifer_species(m_pGeometryCache->species); - const bool palm = is_palm_species(m_pGeometryCache->species); - - TreeMeshBuilder wood; - wood.color = m_BranchImageInstance.IsEmpty() ? 0xFF4D3621u : 0xFFFFFFFFu; - build_branches_zup(wood, m_pGeometryCache->species, H, conifer, palm); - - if (wood.verts.empty() || wood.indices.empty()) - return; - - m_unBranchVertexCount = static_cast(wood.verts.size()); - if (FAILED(ms_lpd3dDevice->CreateVertexBuffer( - m_unBranchVertexCount * sizeof(TreeVertex), - D3DUSAGE_WRITEONLY, - kTreeFVF, - D3DPOOL_MANAGED, - &m_pBranchVertexBuffer))) - { - m_unBranchVertexCount = 0; - return; - } - - BYTE * pVB = NULL; - if (SUCCEEDED(m_pBranchVertexBuffer->Lock(0, 0, &pVB, 0))) - { - std::memcpy(pVB, wood.verts.data(), wood.verts.size() * sizeof(TreeVertex)); - m_pBranchVertexBuffer->Unlock(); - } - - m_pBranchIndexCounts = new unsigned short[1]; - m_pBranchIndexCounts[0] = static_cast(wood.indices.size()); - m_pGeometryCache->branch_index_count = static_cast(wood.indices.size()); - - if (FAILED(ms_lpd3dDevice->CreateIndexBuffer( - m_pBranchIndexCounts[0] * sizeof(uint16_t), - D3DUSAGE_WRITEONLY, - D3DFMT_INDEX16, - D3DPOOL_MANAGED, - &m_pBranchIndexBuffer))) - { - m_pBranchIndexCounts[0] = 0; - return; - } - - BYTE * pIB = NULL; - if (SUCCEEDED(m_pBranchIndexBuffer->Lock(0, 0, &pIB, 0))) - { - std::memcpy(pIB, wood.indices.data(), wood.indices.size() * sizeof(uint16_t)); - m_pBranchIndexBuffer->Unlock(); - } -} - -auto CSpeedTreeWrapper::SetupFrondBuffers() -> void -{ - m_unFrondVertexCount = 0; -} - -auto CSpeedTreeWrapper::SetupLeafBuffers() -> void -{ - if (!m_pGeometryCache || !ms_lpd3dDevice) - return; - - const float H = m_pGeometryCache->height; - const bool conifer = is_conifer_species(m_pGeometryCache->species); - const bool palm = is_palm_species(m_pGeometryCache->species); - const bool has_atlas = !m_CompositeImageInstance.IsEmpty(); - const std::vector rects = foliage_rects( - m_pGeometryCache->species, - m_pGeometryCache->composite_name, - has_atlas); - - TreeMeshBuilder leaves; - leaves.color = has_atlas ? 0xFFFFFFFFu : (conifer ? 0xFF1F471Cu : 0xFF2E6624u); - build_leaves_zup(leaves, m_pGeometryCache->species, H, conifer, palm, rects); - - if (leaves.verts.empty()) - return; - - m_usNumLeafLods = 1; - m_pLeafVertexBuffer = new LPDIRECT3DVERTEXBUFFER8[1]; - m_pLeafVertexBuffer[0] = NULL; - m_pLeavesUpdatedByCpu = new bool[1]; - m_pLeavesUpdatedByCpu[0] = true; - - m_pGeometryCache->leaf_vertex_count = static_cast(leaves.verts.size()); - - if (FAILED(ms_lpd3dDevice->CreateVertexBuffer( - leaves.verts.size() * sizeof(TreeVertex), - D3DUSAGE_WRITEONLY, - kTreeFVF, - D3DPOOL_MANAGED, - &m_pLeafVertexBuffer[0]))) - { - m_pGeometryCache->leaf_vertex_count = 0; - return; - } - - BYTE * pVB = NULL; - if (SUCCEEDED(m_pLeafVertexBuffer[0]->Lock(0, 0, &pVB, 0))) - { - std::memcpy(pVB, leaves.verts.data(), leaves.verts.size() * sizeof(TreeVertex)); - m_pLeafVertexBuffer[0]->Unlock(); - } -} - -auto CSpeedTreeWrapper::SetupBranchForTreeType() const -> void -{ - LPDIRECT3DTEXTURE8 lpd3dTexture = NULL; - if (!m_BranchImageInstance.IsEmpty()) - lpd3dTexture = const_cast(m_BranchImageInstance).GetTextureReference().GetD3DTexture(); - STATEMANAGER.SetTexture(0, lpd3dTexture); - STATEMANAGER.SetTexture(1, NULL); - - if (m_unBranchVertexCount > 0 && m_pBranchVertexBuffer && m_pBranchIndexBuffer) - { - STATEMANAGER.SetStreamSource(0, m_pBranchVertexBuffer, sizeof(TreeVertex)); - STATEMANAGER.SetIndices(m_pBranchIndexBuffer, 0); - } -} - -auto CSpeedTreeWrapper::RenderBranches() const -> void -{ - if (!m_pGeometryCache || m_unBranchVertexCount == 0 || !m_pBranchIndexCounts || m_pBranchIndexCounts[0] < 3) - return; - - PositionTree(); - STATEMANAGER.DrawIndexedPrimitive( - D3DPT_TRIANGLELIST, - 0, - m_unBranchVertexCount, - 0, - m_pBranchIndexCounts[0] / 3); -} - -auto CSpeedTreeWrapper::SetupFrondForTreeType() const -> void -{ -} - -auto CSpeedTreeWrapper::RenderFronds() const -> void -{ -} - -auto CSpeedTreeWrapper::SetupLeafForTreeType() const -> void -{ - LPDIRECT3DTEXTURE8 lpd3dTexture = NULL; - if (!m_CompositeImageInstance.IsEmpty()) - lpd3dTexture = const_cast(m_CompositeImageInstance).GetTextureReference().GetD3DTexture(); - STATEMANAGER.SetTexture(0, lpd3dTexture); - STATEMANAGER.SetTexture(1, NULL); - - if (m_pLeafVertexBuffer && m_pLeafVertexBuffer[0]) - STATEMANAGER.SetStreamSource(0, m_pLeafVertexBuffer[0], sizeof(TreeVertex)); -} - -auto CSpeedTreeWrapper::RenderLeaves() const -> void -{ - if (!m_pGeometryCache || m_pGeometryCache->leaf_vertex_count < 3 || !m_pLeafVertexBuffer || !m_pLeafVertexBuffer[0]) - return; - - PositionTree(); - STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, m_pGeometryCache->leaf_vertex_count / 3); -} - -auto CSpeedTreeWrapper::EndLeafForTreeType() -> void -{ -} - -auto CSpeedTreeWrapper::RenderBillboards() const -> void -{ -} - -auto CSpeedTreeWrapper::OnRender() -> void -{ - if (!IsNativeTerrainRenderEnabled()) - return; - - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, TRUE); - STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE); - STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0x00000060); - STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - STATEMANAGER.SetVertexShader(ms_dwBranchVertexShader); - SetupBranchForTreeType(); - RenderBranches(); - - STATEMANAGER.SetVertexShader(ms_dwLeafVertexShader); - STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); - SetupLeafForTreeType(); - RenderLeaves(); - EndLeafForTreeType(); - - STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING); - STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF); - STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); -} - -auto CSpeedTreeWrapper::OnRenderPCBlocker() -> void -{ - if (!IsNativeTerrainRenderEnabled()) - return; - - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR); - STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, TRUE); - STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE); - STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - STATEMANAGER.SaveRenderState(D3DRS_TEXTUREFACTOR, 0x80FFFFFF); - STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); - STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0x00000030); - STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - STATEMANAGER.SetVertexShader(ms_dwBranchVertexShader); - SetupBranchForTreeType(); - RenderBranches(); - - STATEMANAGER.SetVertexShader(ms_dwLeafVertexShader); - STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); - SetupLeafForTreeType(); - RenderLeaves(); - EndLeafForTreeType(); - - STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING); - STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE); - STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND); - STATEMANAGER.RestoreRenderState(D3DRS_DESTBLEND); - STATEMANAGER.RestoreRenderState(D3DRS_TEXTUREFACTOR); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); - STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF); - STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); -} - -auto CSpeedTreeWrapper::GetInstances(unsigned int & nCount) -> CSpeedTreeWrapper ** -{ - nCount = static_cast(m_vInstances.size()); - return nCount ? &m_vInstances[0] : NULL; -} - -auto CSpeedTreeWrapper::MakeInstance() -> CSpeedTreeWrapper * -{ - CSpeedTreeWrapper * pInstance = new CSpeedTreeWrapper; - pInstance->m_bIsInstance = true; - pInstance->m_cBranchMaterial = m_cBranchMaterial; - pInstance->m_cLeafMaterial = m_cLeafMaterial; - pInstance->m_cFrondMaterial = m_cFrondMaterial; - - if (!m_CompositeImageInstance.IsEmpty()) - pInstance->m_CompositeImageInstance.SetImagePointer(m_CompositeImageInstance.GetGraphicImagePointer()); - if (!m_BranchImageInstance.IsEmpty()) - pInstance->m_BranchImageInstance.SetImagePointer(m_BranchImageInstance.GetGraphicImagePointer()); - if (!m_ShadowImageInstance.IsEmpty()) - pInstance->m_ShadowImageInstance.SetImagePointer(m_ShadowImageInstance.GetGraphicImagePointer()); - - pInstance->m_pTextureInfo = m_pTextureInfo; - pInstance->m_pGeometryCache = m_pGeometryCache; - - pInstance->m_pBranchIndexBuffer = m_pBranchIndexBuffer; - pInstance->m_pBranchIndexCounts = m_pBranchIndexCounts; - pInstance->m_pBranchVertexBuffer = m_pBranchVertexBuffer; - pInstance->m_unBranchVertexCount = m_unBranchVertexCount; - - pInstance->m_pFrondIndexBuffer = m_pFrondIndexBuffer; - pInstance->m_pFrondIndexCounts = m_pFrondIndexCounts; - pInstance->m_pFrondVertexBuffer = m_pFrondVertexBuffer; - pInstance->m_unFrondVertexCount = m_unFrondVertexCount; - - pInstance->m_pLeafVertexBuffer = m_pLeafVertexBuffer; - pInstance->m_usNumLeafLods = m_usNumLeafLods; - pInstance->m_pLeavesUpdatedByCpu = m_pLeavesUpdatedByCpu; - - std::memcpy(pInstance->m_afPos, m_afPos, sizeof(m_afPos)); - std::memcpy(pInstance->m_afBoundingBox, m_afBoundingBox, sizeof(m_afBoundingBox)); - pInstance->m_pInstanceOf = this; - m_vInstances.push_back(pInstance); - return pInstance; -} - -auto CSpeedTreeWrapper::DeleteInstance(CSpeedTreeWrapper * pInstance) -> void -{ - if (!pInstance) - return; - auto it = std::find(m_vInstances.begin(), m_vInstances.end(), pInstance); - if (it != m_vInstances.end()) - m_vInstances.erase(it); - delete pInstance; -} - -auto CSpeedTreeWrapper::Advance() -> void -{ -} - -auto CSpeedTreeWrapper::GetBranchTexture() const -> LPDIRECT3DTEXTURE8 -{ - if (m_BranchImageInstance.IsEmpty()) - return NULL; - return const_cast(m_BranchImageInstance).GetTextureReference().GetD3DTexture(); -} - -auto CSpeedTreeWrapper::CleanUpMemory() -> void -{ -} - -auto CSpeedTreeWrapper::PositionTree() const -> void -{ - D3DXMATRIX matTranslation; - D3DXMatrixTranslation(&matTranslation, m_afPos[0], m_afPos[1], m_afPos[2]); - STATEMANAGER.SetTransform(D3DTS_WORLD, &matTranslation); -} - -auto CSpeedTreeWrapper::LoadTexture(const char * pFilename, CGraphicImageInstance & rImage) -> bool -{ - if (!pFilename || !pFilename[0]) - return false; - CResource * pResource = CResourceManager::Instance().GetResourcePointer(pFilename); - if (!pResource) - return false; - rImage.SetImagePointer(static_cast(pResource)); - return !rImage.IsEmpty(); -} - -auto CSpeedTreeWrapper::SetShaderConstants(const float * /*pMaterial*/) const -> void -{ -} diff --git a/extension/src/port/SpeedTreeLib/SpeedTreeConfig.h b/extension/src/port/SpeedTreeLib/SpeedTreeConfig.h new file mode 100644 index 00000000..9a0f86f3 --- /dev/null +++ b/extension/src/port/SpeedTreeLib/SpeedTreeConfig.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////// +// SpeedTreeRT runtime configuration #defines +// +// (c) 2003 IDV, Inc. +// +// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION *** +// +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Interactive Data Visualization and may +// not be copied or disclosed except in accordance with the terms of +// that agreement. +// +// Copyright (c) 2001-2003 IDV, Inc. +// All Rights Reserved. +// +// IDV, Inc. +// 1233 Washington St. Suite 610 +// Columbia, SC 29201 +// Voice: (803) 799-1699 +// Fax: (803) 931-0320 +// Web: http://www.idvinc.com + +#pragma once + +const int c_nNumWindMatrices = 4; +const int c_nNumInstancesPerModel = 10; +const float c_fForestSize = 200.0f; +const float c_fSpacingTolerance = 30.0f; +const int c_nMaxPlacementIterations = 500; +const int c_nDefaultAlphaTestValue = 84; +const float c_fNearLodFactor = 2.0f; +const float c_fFarLodFactor = 9.0f; +const float c_fBenchmarkPeriod = 1.0f; + +// vertex shader constant locations +const int c_nVertexShader_LeafLightingAdjustment = 70; +const int c_nVertexShader_Light = 71; +const int c_nVertexShader_Material = 74; +const int c_nVertexShader_TreePos = 52; +const int c_nVertexShader_CompoundMatrix = 0; +const int c_nVertexShader_WindMatrices = 54; +const int c_nVertexShader_LeafTables = 4; +const int c_nVertexShader_Fog = 85; + +// lighting +const float c_afLightPosition[4] = { -0.707f, 0.0f, 0.707f, 0.0f }; +const float c_afLightAmbient[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; +const float c_afLightDiffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; +const float c_afLightSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; +const float c_afLightGlobalAmbient[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + +// setup lighting (enable ONE of the two below) +#define WRAPPER_USE_STATIC_LIGHTING +//#define WRAPPER_USE_DYNAMIC_LIGHTING + +#if defined WRAPPER_USE_STATIC_LIGHTING && defined WRAPPER_USE_DYNAMIC_LIGHTING + #error Please define exactly one lighting mode +#endif + +// setup wind (enable ONE of the three below) +//#define WRAPPER_USE_GPU_WIND +//#define WRAPPER_USE_CPU_WIND +#define WRAPPER_USE_NO_WIND + +#if defined WRAPPER_USE_GPU_WIND && defined WRAPPER_USE_CPU_WIND + #error Please define exactly one lighting mode +#elif defined WRAPPER_USE_GPU_WIND && defined WRAPPER_USE_NO_WIND + #error Please define exactly one lighting mode +#elif defined WRAPPER_USE_CPU_WIND && defined WRAPPER_USE_NO_WIND + #error Please define exactly one lighting mode +#endif + +// leaf placement algorithm (enable ONE of the two below) +//#define WRAPPER_USE_GPU_LEAF_PLACEMENT +#define WRAPPER_USE_CPU_LEAF_PLACEMENT + +#if defined WRAPPER_USE_GPU_LEAF_PLACEMENT && defined WRAPPER_USE_CPU_LEAF_PLACEMENT + #error Please define exactly one leaf placement algorithm +#endif + +// texture coordinates (enable this define for DirectX-based engines) +#define WRAPPER_FLIP_T_TEXCOORD + +// up vector +//#define WRAPPER_UP_POS_Y +#define WRAPPER_UP_POS_Z + +#if defined WRAPPER_UP_POS_Y && defined WRAPPER_UP_POS_Z + #error Please define exactly one up vector +#endif + +// loading from STF or clones/instances? (enable ONE of the two below) +//#define WRAPPER_FOREST_FROM_STF +#define WRAPPER_FOREST_FROM_INSTANCES + +#if defined WRAPPER_FOREST_FROM_STF && defined WRAPPER_FOREST_FROM_INSTANCES + #error Please define exactly one loading mechanism +#endif + +// billboard modes +#define WRAPPER_BILLBOARD_MODE +//#define WRAPPER_RENDER_HORIZONTAL_BILLBOARD + +// render self-shadows +#define WRAPPER_RENDER_SELF_SHADOWS + +// use fog +#define WRAPPER_USE_FOG + +// derived constants +#ifdef WRAPPER_USE_GPU_WIND + #define BRANCHES_USE_SHADERS + #define FRONDS_USE_SHADERS + #define LEAVES_USE_SHADERS +#endif + +#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT + #define LEAVES_USE_SHADERS +#endif diff --git a/extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp b/extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp new file mode 100644 index 00000000..09ee939c --- /dev/null +++ b/extension/src/port/SpeedTreeLib/SpeedTreeForest.cpp @@ -0,0 +1,316 @@ +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest Class + +/////////////////////////////////////////////////////////////////////// +// Include Files +#include "StdAfx.h" + +#include +#include "../EterBase/Filename.h" +#include "../EterBase/MappedFile.h" +#include "../EterPack/EterPackManager.h" + +#include "SpeedTreeForest.h" +#include "SpeedTreeConfig.h" +#include + +using namespace std; + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest constructor + +CSpeedTreeForest::CSpeedTreeForest() : m_fWindStrength(0.0f) +{ + CSpeedTreeRT::SetNumWindMatrices(c_nNumWindMatrices); + + m_afForestExtents[0] = m_afForestExtents[1] = m_afForestExtents[2] = FLT_MAX; + m_afForestExtents[3] = m_afForestExtents[4] = m_afForestExtents[5] = -FLT_MAX; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest destructor + +CSpeedTreeForest::~CSpeedTreeForest() +{ + Clear(); +} + +void CSpeedTreeForest::Clear() +{ + TTreeMap::iterator itor = m_pMainTreeMap.begin(); + UINT uiCount; + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + for (UINT i = 0; i < uiCount; ++i) + delete ppInstances[i]; + + delete pMainTree; + } + + m_pMainTreeMap.clear(); +} + +CSpeedTreeWrapper * CSpeedTreeForest::GetMainTree(DWORD dwCRC) +{ + TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); + + if (itor == m_pMainTreeMap.end()) + return NULL; + + return itor->second; +} + +BOOL CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, const char * c_pszFileName) +{ + TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); + + CSpeedTreeWrapper * pTree; + + if (itor != m_pMainTreeMap.end()) + pTree = itor->second; + else + { + CMappedFile file; + LPCVOID c_pvData; + + // NOTE : 파일이 없을때는 return FALSE 아닌가요? - [levites] + if (!CEterPackManager::Instance().Get(file, c_pszFileName, &c_pvData)) + return FALSE; + + pTree = new CSpeedTreeWrapper; + + if (!pTree->LoadTree(c_pszFileName, (const BYTE *) c_pvData, file.Size())) + { + delete pTree; + return FALSE; + } + + m_pMainTreeMap.insert(std::map::value_type(dwCRC, pTree)); + + file.Destroy(); + } + + *ppMainTree = pTree; + return TRUE; +} + +CSpeedTreeWrapper* CSpeedTreeForest::CreateInstance(float x, float y, float z, DWORD dwTreeCRC, const char * c_szTreeName) +{ + CSpeedTreeWrapper * pMainTree; + if (!GetMainTree(dwTreeCRC, &pMainTree, c_szTreeName)) + return NULL; + + CSpeedTreeWrapper* pTreeInst = pMainTree->MakeInstance(); + pTreeInst->SetPosition(x, y, z); + pTreeInst->RegisterBoundingSphere(); + return pTreeInst; +} + +void CSpeedTreeForest::DeleteInstance(CSpeedTreeWrapper * pInstance) +{ + if (!pInstance) + return; + + CSpeedTreeWrapper * pParentTree = pInstance->InstanceOf(); + + if (!pParentTree) + return; + + pParentTree->DeleteInstance(pInstance); +} + +void CSpeedTreeForest::UpdateSystem(float fCurrentTime) +{ + // 업데이트 할 때 한번 + static float fLastTime = fCurrentTime; + float fElapsedTime = fCurrentTime - fLastTime; + CSpeedTreeRT::SetTime(fElapsedTime); + + m_fAccumTime += fElapsedTime; + SetupWindMatrices(m_fAccumTime); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest::AdjustExtents +void CSpeedTreeForest::AdjustExtents(float x, float y, float z) +{ + // min + m_afForestExtents[0] = __min(m_afForestExtents[0], x); + m_afForestExtents[1] = __min(m_afForestExtents[1], y); + m_afForestExtents[2] = __min(m_afForestExtents[2], z); + + // max + m_afForestExtents[3] = __max(m_afForestExtents[3], x); + m_afForestExtents[4] = __max(m_afForestExtents[4], y); + m_afForestExtents[5] = __max(m_afForestExtents[5], z); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest::SetWindStrength +void CSpeedTreeForest::SetWindStrength(float fStrength) +{ + if (m_fWindStrength == fStrength) + return; + + m_fWindStrength = fStrength; + + TTreeMap::iterator itor = m_pMainTreeMap.begin(); + UINT uiCount; + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + for (UINT i = 0; i < uiCount; ++i) + ppInstances[i]->GetSpeedTree()->SetWindStrength(m_fWindStrength); + } +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest::SetupWindMatrices +void CSpeedTreeForest::SetupWindMatrices(float fTimeInSecs) +{ + // matrix computational data + static float afMatrixTimes[c_nNumWindMatrices] = { 0.0f }; + static float afFrequencies[c_nNumWindMatrices][2] = + { + { 0.15f, 0.17f }, + { 0.25f, 0.15f }, + { 0.19f, 0.05f }, + { 0.15f, 0.22f } + }; + + // compute time since last call + static float fTimeOfLastCall = 0.0f; + float fTimeSinceLastCall = fTimeInSecs - fTimeOfLastCall; + fTimeOfLastCall = fTimeInSecs; + + // wind strength + static float fOldStrength = m_fWindStrength; + + // increment matrix times + for (int i = 0; i < c_nNumWindMatrices; ++i) + afMatrixTimes[i] += fTimeSinceLastCall; + + // compute maximum branch throw + float fBaseAngle = m_fWindStrength * 35.0f; + + // build rotation matrices + for (int j = 0; j < c_nNumWindMatrices; ++j) + { + // adjust time to prevent "jumping" + if (m_fWindStrength != 0.0f) + afMatrixTimes[j] = (afMatrixTimes[j] * fOldStrength) / m_fWindStrength; + + // compute percentages for each axis + float fBaseFreq = m_fWindStrength * 20.0f; + float fXPercent = sinf(fBaseFreq * afFrequencies[j % c_nNumWindMatrices][0] * afMatrixTimes[j]); + float fYPercent = cosf(fBaseFreq * afFrequencies[j % c_nNumWindMatrices][1] * afMatrixTimes[j]); + + // build compound rotation matrix (rotate on 'x' then on 'y') + const float c_fDeg2Rad = 57.2957795f; + float fSinX = sinf(fBaseAngle * fXPercent / c_fDeg2Rad); + float fSinY = sinf(fBaseAngle * fYPercent / c_fDeg2Rad); + float fCosX = cosf(fBaseAngle * fXPercent / c_fDeg2Rad); + float fCosY = cosf(fBaseAngle * fYPercent / c_fDeg2Rad); + + float afMatrix[16] = { 0.0f }; + afMatrix[0] = fCosY; + afMatrix[2] = -fSinY; + afMatrix[4] = fSinX * fSinY; + afMatrix[5] = fCosX; + afMatrix[6] = fSinX * fCosY; + afMatrix[8] = fSinY * fCosX; + afMatrix[9] = -fSinX; + afMatrix[10] = fCosX * fCosY; + afMatrix[15] = 1.0f; + + #ifdef WRAPPER_USE_CPU_WIND + CSpeedTreeRT::SetWindMatrix(j, afMatrix); + #endif + + #ifdef WRAPPER_USE_GPU_WIND + // graphics API specific + UploadWindMatrix(c_nVertexShader_WindMatrices + j * 4, afMatrix); + #endif + } + + // track wind strength + fOldStrength = m_fWindStrength; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForest::SetLodLimits +/* +void CSpeedTreeForest::SetLodLimits(void) +{ + // find tallest tree + float fTallest = -1.0f; + + TTreeMap::iterator itor = m_pMainTreeMap.begin(); + UINT uiCount; + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + float fHeight; + fHeight = pMainTree->GetBoundingBox()[5] - pMainTree->GetBoundingBox()[0]; + fTallest = __max(fHeight, fTallest); + + for (UINT i = 0; i < uiCount; ++i) + { + fHeight = ppInstances[i]->GetBoundingBox()[5] - ppInstances[i]->GetBoundingBox()[0]; + fTallest = __max(fHeight, fTallest); + } + } + + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + pMainTree->GetSpeedTree()->SetLodLimits(fTallest * c_fNearLodFactor, fTallest * c_fFarLodFactor); + + for (UINT i = 0; i < uiCount; ++i) + ppInstances[i]->GetSpeedTree()->SetLodLimits(fTallest * c_fNearLodFactor, fTallest * c_fFarLodFactor); + } +} +*/ +void CSpeedTreeForest::SetLight(const float * afDirection, const float * afAmbient, const float * afDiffuse) +{ + m_afLighting[0] = afDirection[0]; + m_afLighting[1] = afDirection[1]; + m_afLighting[2] = afDirection[2]; + m_afLighting[3] = 1.0f; + + m_afLighting[4] = afAmbient[0]; + m_afLighting[5] = afAmbient[1]; + m_afLighting[6] = afAmbient[2]; + m_afLighting[7] = afAmbient[3]; + + m_afLighting[8] = afDiffuse[0]; + m_afLighting[9] = afDiffuse[1]; + m_afLighting[10] = afDiffuse[2]; + m_afLighting[11] = afDiffuse[3]; +} + +void CSpeedTreeForest::SetFog(float fFogNear, float fFogFar) +{ + const float c_fFogLinearScale = (1.0f / (fFogFar - fFogNear)); + + m_afFog[0] = fFogNear; + m_afFog[1] = fFogFar; + m_afFog[2] = c_fFogLinearScale; + m_afFog[3] = 0.0f; +} diff --git a/extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp b/extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp new file mode 100644 index 00000000..f6ba6584 --- /dev/null +++ b/extension/src/port/SpeedTreeLib/SpeedTreeForestDirectX8.cpp @@ -0,0 +1,344 @@ +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8 Class +// +// (c) 2003 IDV, Inc. +// +// This class is provided to illustrate one way to incorporate +// SpeedTreeRT into an OpenGL application. All of the SpeedTreeRT +// calls that must be made on a per tree basis are done by this class. +// Calls that apply to all trees (i.e. static SpeedTreeRT functions) +// are made in the functions in main.cpp. +// +// +// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION *** +// +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Interactive Data Visualization and may +// not be copied or disclosed except in accordance with the terms of +// that agreement. +// +// Copyright (c) 2001-2003 IDV, Inc. +// All Rights Reserved. +// +// IDV, Inc. +// 1233 Washington St. Suite 610 +// Columbia, SC 29201 +// Voice: (803) 799-1699 +// Fax: (803) 931-0320 +// Web: http://www.idvinc.com + +#include "StdAfx.h" + +#include +#include +#include +#include + +#include "../EterBase/Timer.h" +#include "../EterLib/StateManager.h" +#include "../EterLib/Camera.h" + +#include "SpeedTreeForestDirectX8.h" +#include "SpeedTreeConfig.h" +#include "VertexShaders.h" + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8 + +CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8() : m_dwBranchVertexShader(0), m_dwLeafVertexShader(0) +{ +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8 + +CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8() +{ +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8::InitVertexShaders +bool CSpeedTreeForestDirectX8::InitVertexShaders(void) +{ + NANOBEGIN + // load the vertex shaders + if (!m_dwBranchVertexShader) + m_dwBranchVertexShader = LoadBranchShader(m_pDx); + + if (!m_dwLeafVertexShader) + m_dwLeafVertexShader = LoadLeafShader(m_pDx); + + if (m_dwBranchVertexShader && m_dwLeafVertexShader) + { + CSpeedTreeWrapper::SetVertexShaders(m_dwBranchVertexShader, m_dwLeafVertexShader); + return true; + } + + NANOEND + return false; +} + +bool CSpeedTreeForestDirectX8::SetRenderingDevice(LPDIRECT3DDEVICE8 lpDevice) +{ + m_pDx = lpDevice; + + if (!InitVertexShaders()) + return false; + + const float c_afLightPosition[4] = { -0.707f, -0.300f, 0.707f, 0.0f }; + const float c_afLightAmbient[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; + const float c_afLightDiffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + const float c_afLightSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + float afLight1[] = + { + c_afLightPosition[0], c_afLightPosition[1], c_afLightPosition[2], // pos + c_afLightDiffuse[0], c_afLightDiffuse[1], c_afLightDiffuse[2], // diffuse + c_afLightAmbient[0], c_afLightAmbient[1], c_afLightAmbient[2], // ambient + c_afLightSpecular[0], c_afLightSpecular[1], c_afLightSpecular[2], // specular + c_afLightPosition[3], // directional flag + 1.0f, 0.0f, 0.0f // attenuation (constant, linear, quadratic) + }; + + CSpeedTreeRT::SetNumWindMatrices(c_nNumWindMatrices); + + CSpeedTreeRT::SetLightAttributes(0, afLight1); + CSpeedTreeRT::SetLightState(0, true); + return true; +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8::UploadWindMatrix + +void CSpeedTreeForestDirectX8::UploadWindMatrix(UINT uiLocation, const float* pMatrix) const +{ + STATEMANAGER.SetVertexShaderConstant(uiLocation, pMatrix, 4); +} + +void CSpeedTreeForestDirectX8::UpdateCompundMatrix(const D3DXVECTOR3 & c_rEyeVec, const D3DXMATRIX & c_rmatView, const D3DXMATRIX & c_rmatProj) +{ + // setup composite matrix for shader + D3DXMATRIX matBlend; + D3DXMatrixIdentity(&matBlend); + + D3DXMATRIX matBlendShader; + D3DXMatrixMultiply(&matBlendShader, &c_rmatView, &c_rmatProj); + + float afDirection[3]; + afDirection[0] = matBlendShader.m[0][2]; + afDirection[1] = matBlendShader.m[1][2]; + afDirection[2] = matBlendShader.m[2][2]; + CSpeedTreeRT::SetCamera(c_rEyeVec, afDirection); + + D3DXMatrixTranspose(&matBlendShader, &matBlendShader); + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_CompoundMatrix, &matBlendShader, 4); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeForestDirectX8::Render + +void CSpeedTreeForestDirectX8::Render(unsigned long ulRenderBitVector) +{ + UpdateSystem(CTimer::Instance().GetCurrentSecond()); + + if (m_pMainTreeMap.empty()) + return; + + if (!(ulRenderBitVector & Forest_RenderToShadow) && !(ulRenderBitVector & Forest_RenderToMiniMap)) + UpdateCompundMatrix(CCameraManager::Instance().GetCurrentCamera()->GetEye(), ms_matView, ms_matProj); + + DWORD dwLightState = STATEMANAGER.GetRenderState(D3DRS_LIGHTING); + DWORD dwColorVertexState = STATEMANAGER.GetRenderState(D3DRS_COLORVERTEX); + DWORD dwFogVertexMode = STATEMANAGER.GetRenderState(D3DRS_FOGVERTEXMODE); + +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE); +#else + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, TRUE); +#endif + + TTreeMap::iterator itor; + UINT uiCount; + + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + for (UINT i = 0; i < uiCount; ++i) + { + ppInstances[i]->Advance(); + } + } + + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_Light, m_afLighting, 3); + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_Fog, m_afFog, 1); + + if (ulRenderBitVector & Forest_RenderToShadow) + { + //STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + } + else + { + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR); + STATEMANAGER.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR); + STATEMANAGER.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR); + + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP); + } + + STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, TRUE); + STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_CW); + + // set up fog if it is enabled + if (STATEMANAGER.GetRenderState(D3DRS_FOGENABLE)) + { + #ifdef WRAPPER_USE_GPU_WIND + STATEMANAGER.SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_NONE); // GPU needs to work on all cards + #endif + } + + // choose fixed function pipeline or custom shader for fronds and branches + STATEMANAGER.SetVertexShader(m_dwBranchVertexShader); + + // render branches + if (ulRenderBitVector & Forest_RenderBranches) + { + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + pMainTree->SetupBranchForTreeType(); + + for (UINT i = 0; i < uiCount; ++i) + if (ppInstances[i]->isShow()) + ppInstances[i]->RenderBranches(); + } + } + + // set render states + STATEMANAGER.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + // render fronds + if (ulRenderBitVector & Forest_RenderFronds) + { + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + pMainTree->SetupFrondForTreeType(); + + for (UINT i = 0; i < uiCount; ++i) + if (ppInstances[i]->isShow()) + ppInstances[i]->RenderFronds(); + } + } + + // render leaves + if (ulRenderBitVector & Forest_RenderLeaves) + { + STATEMANAGER.SetVertexShader(m_dwLeafVertexShader); + + if (STATEMANAGER.GetRenderState(D3DRS_FOGENABLE)) + { + #if defined WRAPPER_USE_GPU_WIND || defined WRAPPER_USE_GPU_LEAF_PLACEMENT + STATEMANAGER.SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_NONE); + #endif + } + + if (ulRenderBitVector & Forest_RenderToShadow || ulRenderBitVector & Forest_RenderToMiniMap) + { + STATEMANAGER.SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL); + STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0x00000000); + } + + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + pMainTree->SetupLeafForTreeType(); + + for (UINT i = 0; i < uiCount; ++i) + if (ppInstances[i]->isShow()) + ppInstances[i]->RenderLeaves(); + } + + while (itor != m_pMainTreeMap.end()) + (itor++)->second->EndLeafForTreeType(); + + if (ulRenderBitVector & Forest_RenderToShadow || ulRenderBitVector & Forest_RenderToMiniMap) + { + STATEMANAGER.SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF); + } + } + + // render billboards + #ifndef WRAPPER_NO_BILLBOARD_MODE + if (ulRenderBitVector & Forest_RenderBillboards) + { + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, FALSE); + + itor = m_pMainTreeMap.begin(); + + while (itor != m_pMainTreeMap.end()) + { + CSpeedTreeWrapper * pMainTree = (itor++)->second; + CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount); + + pMainTree->SetupBranchForTreeType(); + + for (UINT i = 0; i < uiCount; ++i) + if (ppInstances[i]->isShow()) + ppInstances[i]->RenderBillboards(); + } + } + #endif + + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLightState); + STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, dwColorVertexState); + STATEMANAGER.SetRenderState(D3DRS_FOGVERTEXMODE, dwFogVertexMode); + + // 셀프섀도우로 쓰는 TextureStage 1의 COLOROP와 ALPHAOP를 꺼줘야 다음 렌더링 할 놈들이 + // 제대로 나온다. (안그러면 검게 나올 가능성이..) + if (!(ulRenderBitVector & Forest_RenderToShadow)) + { + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + + STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); + STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); +} + diff --git a/extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp b/extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp new file mode 100644 index 00000000..a4c69017 --- /dev/null +++ b/extension/src/port/SpeedTreeLib/SpeedTreeWrapper.cpp @@ -0,0 +1,1485 @@ +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper Class +// +// (c) 2003 IDV, Inc. +// +// This class is provided to illustrate one way to incorporate +// SpeedTreeRT into an OpenGL application. All of the SpeedTreeRT +// calls that must be made on a per tree basis are done by this class. +// Calls that apply to all trees (i.e. static SpeedTreeRT functions) +// are made in the functions in main.cpp. +// +// +// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION *** +// +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Interactive Data Visualization and may +// not be copied or disclosed except in accordance with the terms of +// that agreement. +// +// Copyright (c) 2001-2003 IDV, Inc. +// All Rights Reserved. +// +// IDV, Inc. +// 1233 Washington St. Suite 610 +// Columbia, SC 29201 +// Voice: (803) 799-1699 +// Fax: (803) 931-0320 +// Web: http://www.idvinc.com +// + +#pragma warning(disable:4786) + +/////////////////////////////////////////////////////////////////////// +// Include Files +#include "StdAfx.h" + +#include +#include +#include "../EterBase/Debug.h" +#include "../EterBase/Timer.h" +#include "../EterBase/Filename.h" +#include "../EterLib/ResourceManager.h" +#include "../EterLib/Camera.h" +#include "../EterLib/StateManager.h" + +#include "SpeedTreeConfig.h" +#include "SpeedTreeForestDirectX8.h" +#include "SpeedTreeWrapper.h" +#include "VertexShaders.h" + +using namespace std; + +DWORD CSpeedTreeWrapper::ms_dwBranchVertexShader = 0; +DWORD CSpeedTreeWrapper::ms_dwLeafVertexShader = 0; +bool CSpeedTreeWrapper::ms_bSelfShadowOn = true; + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::CSpeedTreeWrapper +CSpeedTreeWrapper::CSpeedTreeWrapper() : +m_pSpeedTree(new CSpeedTreeRT), +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), +m_pLeavesUpdatedByCpu(NULL), +m_unBranchVertexCount(0), +m_unFrondVertexCount(0), +m_pTextureInfo(NULL) +{ + // set initial position + m_afPos[0] = m_afPos[1] = m_afPos[2] = 0.0f; + + m_pSpeedTree->SetWindStrength(1.0f); + m_pSpeedTree->SetLocalMatrices(0, 4); +} + +void CSpeedTreeWrapper::SetVertexShaders(DWORD dwBranchVertexShader, DWORD dwLeafVertexShader) +{ + ms_dwBranchVertexShader = dwBranchVertexShader; + ms_dwLeafVertexShader = dwLeafVertexShader; +} + +void CSpeedTreeWrapper::OnRenderPCBlocker() +{ + if (ms_dwBranchVertexShader == 0) + { + ms_dwBranchVertexShader = LoadBranchShader(ms_lpd3dDevice); + //LogBox("Vertex Shader not assigned. You must call CSpeedTreeWrapper::SetVertexShader for this"); + } + + if (ms_dwLeafVertexShader == 0) + { + ms_dwLeafVertexShader = LoadLeafShader(ms_lpd3dDevice); + //LogBox("Vertex Shader not assigned. You must call CSpeedTreeWrapper::SetVertexShader for this"); + } + + CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); + + // 하나만 렌더링 할 때는 LOD 사용하지 않음 + m_pSpeedTree->SetLodLevel(1.0f); + //Advance(); + + CSpeedTreeForestDirectX8::Instance().UpdateCompundMatrix(CCameraManager::Instance().GetCurrentCamera()->GetEye(), ms_matView, ms_matProj); + + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + + DWORD dwLighting = STATEMANAGER.GetRenderState(D3DRS_LIGHTING); + DWORD dwFogEnable = STATEMANAGER.GetRenderState(D3DRS_FOGENABLE); + DWORD dwAlphaBlendEnable = STATEMANAGER.GetRenderState(D3DRS_ALPHABLENDENABLE); + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE); + STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, TRUE); + STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_CW); + STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, FALSE); + + // choose fixed function pipeline or custom shader for fronds and branches + STATEMANAGER.SetVertexShader(ms_dwBranchVertexShader); + +// SetupBranchForTreeType(); + { + // update the branch geometry for CPU wind +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry); + + if (m_pGeometryCache->m_sBranches.m_usNumStrips > 0) + { + // update the vertex array + SFVFBranchVertex* pVertexBuffer = NULL; + m_pBranchVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); + for (UINT i = 0; i < m_unBranchVertexCount; ++i) + { + memcpy(&(pVertexBuffer[i].m_vPosition), &(m_pGeometryCache->m_sBranches.m_pCoords[i * 3]), 3 * sizeof(float)); + } + m_pBranchVertexBuffer->Unlock(); + } +#endif + + LPDIRECT3DTEXTURE8 lpd3dTexture; + + // set texture map + if ((lpd3dTexture = m_BranchImageInstance.GetTextureReference().GetD3DTexture())) + STATEMANAGER.SetTexture(0, lpd3dTexture); + + if (m_pGeometryCache->m_sBranches.m_usVertexCount > 0) + { + // activate the branch vertex buffer + STATEMANAGER.SetStreamSource(0, m_pBranchVertexBuffer, sizeof(SFVFBranchVertex)); + // set the index buffer + STATEMANAGER.SetIndices(m_pBranchIndexBuffer, 0); + } + } + + RenderBranches(); + + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + STATEMANAGER.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + +// SetupFrondForTreeType(); + { + // update the frond geometry for CPU wind +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry); + if (m_pGeometryCache->m_sFronds.m_usNumStrips > 0) + { + // update the vertex array + SFVFBranchVertex * pVertexBuffer = NULL; + m_pFrondVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); + for (UINT i = 0; i < m_unFrondVertexCount; ++i) + { + memcpy(&(pVertexBuffer[i].m_vPosition), &(m_pGeometryCache->m_sFronds.m_pCoords[i * 3]), 3 * sizeof(float)); + } + m_pFrondVertexBuffer->Unlock(); + } +#endif + + if (!m_CompositeImageInstance.IsEmpty()) + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + + if (m_pGeometryCache->m_sFronds.m_usVertexCount > 0) + { + // activate the frond vertex buffer + STATEMANAGER.SetStreamSource(0, m_pFrondVertexBuffer, sizeof(SFVFBranchVertex)); + // set the index buffer + STATEMANAGER.SetIndices(m_pFrondIndexBuffer, 0); + } + } + RenderFronds(); + + STATEMANAGER.SetVertexShader(ms_dwLeafVertexShader); + +// 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(); + EndLeafForTreeType(); + + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, FALSE); + RenderBillboards(); + + STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX); + STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); + STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, dwAlphaBlendEnable); + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLighting); + STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, dwFogEnable); + + STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); +} + +void CSpeedTreeWrapper::OnRender() +{ + if (ms_dwBranchVertexShader == 0) + { + ms_dwBranchVertexShader = LoadBranchShader(ms_lpd3dDevice); + //LogBox("Vertex Shader not assigned. You must call CSpeedTreeWrapper::SetVertexShader for this"); + } + + if (ms_dwLeafVertexShader == 0) + { + ms_dwLeafVertexShader = LoadLeafShader(ms_lpd3dDevice); + //LogBox("Vertex Shader not assigned. You must call CSpeedTreeWrapper::SetVertexShader for this"); + } + + CSpeedTreeForestDirectX8::Instance().UpdateSystem(ELTimer_GetMSec() / 1000.0f); + + // 하나만 렌더링 할 때는 LOD 사용하지 않음 + m_pSpeedTree->SetLodLevel(1.0f); + //Advance(); + + CSpeedTreeForestDirectX8::Instance().UpdateCompundMatrix(CCameraManager::Instance().GetCurrentCamera()->GetEye(), ms_matView, ms_matProj); + + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); + STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP); + STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP); + + STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE); + STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, TRUE); + STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_CW); + STATEMANAGER.SaveRenderState(D3DRS_FOGENABLE, FALSE); + + // choose fixed function pipeline or custom shader for fronds and branches + STATEMANAGER.SetVertexShader(ms_dwBranchVertexShader); + + SetupBranchForTreeType(); + RenderBranches(); + + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + STATEMANAGER.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + SetupFrondForTreeType(); + RenderFronds(); + + STATEMANAGER.SetVertexShader(ms_dwLeafVertexShader); + + SetupLeafForTreeType(); + RenderLeaves(); + EndLeafForTreeType(); + + STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE); + STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, FALSE); + RenderBillboards(); + + STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING); + STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE); + STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC); + STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE); + STATEMANAGER.RestoreRenderState(D3DRS_FOGENABLE); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::~CSpeedTreeWrapper + +CSpeedTreeWrapper::~CSpeedTreeWrapper() +{ + // if this is not an instance, clean up + if (!m_bIsInstance) + { + if (m_unBranchVertexCount > 0) + { + 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) + { + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_LeafGeometry, -1, -1, i); + + if (m_pGeometryCache->m_sLeaves0.m_usLeafCount > 0) + SAFE_RELEASE(m_pLeafVertexBuffer[i]); + } + + SAFE_DELETE_ARRAY(m_pLeavesUpdatedByCpu); + SAFE_DELETE_ARRAY(m_pLeafVertexBuffer); + + SAFE_DELETE(m_pTextureInfo); + + SAFE_DELETE(m_pGeometryCache); + } + + // always delete the speedtree + SAFE_DELETE(m_pSpeedTree); + + Clear(); +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::LoadTree +bool CSpeedTreeWrapper::LoadTree(const char * pszSptFile, const BYTE * c_pbBlock, unsigned int uiBlockSize, UINT nSeed, float fSize, float fSizeVariance) +{ + bool bSuccess = false; + + // directx, so allow for flipping of the texture coordinate +#ifdef WRAPPER_FLIP_T_TEXCOORD + m_pSpeedTree->SetTextureFlip(true); +#endif + + // load the tree file + if (!m_pSpeedTree->LoadTree(c_pbBlock, uiBlockSize)) + { + if (!m_pSpeedTree->LoadTree(pszSptFile)) + { + TraceError("SpeedTreeRT Error: %s", CSpeedTreeRT::GetCurrentError()); + return false; + } + } + + // override the lighting method stored in the spt file +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + m_pSpeedTree->SetBranchLightingMethod(CSpeedTreeRT::LIGHT_DYNAMIC); + m_pSpeedTree->SetLeafLightingMethod(CSpeedTreeRT::LIGHT_DYNAMIC); + m_pSpeedTree->SetFrondLightingMethod(CSpeedTreeRT::LIGHT_DYNAMIC); +#else + m_pSpeedTree->SetBranchLightingMethod(CSpeedTreeRT::LIGHT_STATIC); + m_pSpeedTree->SetLeafLightingMethod(CSpeedTreeRT::LIGHT_STATIC); + m_pSpeedTree->SetFrondLightingMethod(CSpeedTreeRT::LIGHT_STATIC); +#endif + + // set the wind method +#ifdef WRAPPER_USE_GPU_WIND + m_pSpeedTree->SetBranchWindMethod(CSpeedTreeRT::WIND_GPU); + m_pSpeedTree->SetLeafWindMethod(CSpeedTreeRT::WIND_GPU); + m_pSpeedTree->SetFrondWindMethod(CSpeedTreeRT::WIND_GPU); +#endif +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->SetBranchWindMethod(CSpeedTreeRT::WIND_CPU); + m_pSpeedTree->SetLeafWindMethod(CSpeedTreeRT::WIND_CPU); + m_pSpeedTree->SetFrondWindMethod(CSpeedTreeRT::WIND_CPU); +#endif +#ifdef WRAPPER_USE_NO_WIND + m_pSpeedTree->SetBranchWindMethod(CSpeedTreeRT::WIND_NONE); + m_pSpeedTree->SetLeafWindMethod(CSpeedTreeRT::WIND_NONE); + m_pSpeedTree->SetFrondWindMethod(CSpeedTreeRT::WIND_NONE); +#endif + + m_pSpeedTree->SetNumLeafRockingGroups(1); + + // override the size, if necessary + if (fSize >= 0.0f && fSizeVariance >= 0.0f) + m_pSpeedTree->SetTreeSize(fSize, fSizeVariance); + + // generate tree geometry + if (m_pSpeedTree->Compute(NULL, nSeed)) + { + // get the dimensions + m_pSpeedTree->GetBoundingBox(m_afBoundingBox); + + // make the leaves rock in the wind + m_pSpeedTree->SetLeafRockingState(true); + + // billboard setup +#ifdef WRAPPER_NO_BILLBOARD_MODE + CSpeedTreeRT::SetDropToBillboard(false); +#else + CSpeedTreeRT::SetDropToBillboard(true); +#endif + + // query & set materials + m_cBranchMaterial.Set(m_pSpeedTree->GetBranchMaterial()); + m_cFrondMaterial.Set(m_pSpeedTree->GetFrondMaterial()); + m_cLeafMaterial.Set(m_pSpeedTree->GetLeafMaterial()); + + // adjust lod distances + float fHeight = m_afBoundingBox[5] - m_afBoundingBox[2]; + m_pSpeedTree->SetLodLimits(fHeight * c_fNearLodFactor, fHeight * c_fFarLodFactor); + + // query textures + m_pTextureInfo = new CSpeedTreeRT::STextures; + m_pSpeedTree->GetTextures(*m_pTextureInfo); + + // load branch textures + auto vs1 = std::string(pszSptFile); + auto vs2 = std::string(m_pTextureInfo->m_pBranchTextureFilename); + LoadTexture((CFileNameHelper::GetPath(vs1) + CFileNameHelper::NoExtension(vs2) + ".dds").c_str(), m_BranchImageInstance); + +#ifdef WRAPPER_RENDER_SELF_SHADOWS + if (m_pTextureInfo->m_pSelfShadowFilename != NULL) + { + auto vss = std::string(m_pTextureInfo->m_pSelfShadowFilename); + LoadTexture((CFileNameHelper::GetPath(vs1) + CFileNameHelper::NoExtension(vss) + ".dds").c_str(), m_ShadowImageInstance); + } +#endif + if (m_pTextureInfo->m_pCompositeFilename) + { + auto vss = std::string(m_pTextureInfo->m_pCompositeFilename); + LoadTexture((CFileNameHelper::GetPath(vs1) + CFileNameHelper::NoExtension(vss) + ".dds").c_str(), m_CompositeImageInstance); + } + + // setup the index and vertex buffers + SetupBuffers(); + + // everything appeared to go well + bSuccess = true; + } + else // tree failed to compute + fprintf(stderr, "\nFatal Error, cannot compute tree [%s]\n\n", CSpeedTreeRT::GetCurrentError()); + + return bSuccess; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupBuffers + +void CSpeedTreeWrapper::SetupBuffers(void) +{ + // read all the geometry for highest LOD into the geometry cache (just a precaution, it's updated later) + m_pSpeedTree->SetLodLevel(1.0f); + + if (m_pGeometryCache == NULL) + m_pGeometryCache = new CSpeedTreeRT::SGeometry; + + m_pSpeedTree->GetGeometry(*m_pGeometryCache); + + // setup the buffers for each part + SetupBranchBuffers(); + SetupFrondBuffers(); + SetupLeafBuffers(); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupBranchBuffers + +void CSpeedTreeWrapper::SetupBranchBuffers(void) +{ + // reference to branch structure + CSpeedTreeRT::SGeometry::SIndexed* pBranches = &(m_pGeometryCache->m_sBranches); + m_unBranchVertexCount = pBranches->m_usVertexCount; // we asked for a contiguous strip + + // check if this tree has branches + if (m_unBranchVertexCount > 1) + { + // create the vertex buffer for storing branch vertices + SFVFBranchVertex * pVertexBuffer = NULL; + +#ifndef WRAPPER_USE_CPU_WIND + ms_lpd3dDevice->CreateVertexBuffer(m_unBranchVertexCount * sizeof(SFVFBranchVertex), D3DUSAGE_WRITEONLY, D3DFVF_SPEEDTREE_BRANCH_VERTEX, D3DPOOL_MANAGED, &m_pBranchVertexBuffer); + // fill the vertex buffer by interleaving SpeedTree data + m_pBranchVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), 0); +#else + ms_lpd3dDevice->CreateVertexBuffer(m_unBranchVertexCount * sizeof(SFVFBranchVertex), D3DUSAGE_DYNAMIC, D3DFVF_SPEEDTREE_BRANCH_VERTEX, D3DPOOL_SYSTEMMEM, &m_pBranchVertexBuffer); + // fill the vertex buffer by interleaving SpeedTree data + m_pBranchVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); +#endif + { + for (UINT i = 0; i < m_unBranchVertexCount; ++i) + { + // position + memcpy(&pVertexBuffer->m_vPosition, &(pBranches->m_pCoords[i * 3]), 3 * sizeof(float)); + + // normal or color +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + memcpy(&pVertexBuffer->m_vNormal, &(pBranches->m_pNormals[i * 3]), 3 * sizeof(float)); +#else + pVertexBuffer->m_dwDiffuseColor = pBranches->m_pColors[i]; +#endif + + // texcoords for layer 0 + pVertexBuffer->m_fTexCoords[0] = pBranches->m_pTexCoords0[i * 2]; + pVertexBuffer->m_fTexCoords[1] = pBranches->m_pTexCoords0[i * 2 + 1]; + + // texcoords for layer 1 (if enabled) +#ifdef WRAPPER_RENDER_SELF_SHADOWS + pVertexBuffer->m_fShadowCoords[0] = pBranches->m_pTexCoords1[i * 2]; + pVertexBuffer->m_fShadowCoords[1] = pBranches->m_pTexCoords1[i * 2 + 1]; +#endif + + // extra data for gpu wind +#ifdef WRAPPER_USE_GPU_WIND + pVertexBuffer->m_fWindIndex = 4.0f * pBranches->m_pWindMatrixIndices[i]; + pVertexBuffer->m_fWindWeight = pBranches->m_pWindWeights[i]; +#endif + + ++pVertexBuffer; + } + 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 + 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_MANAGED, &m_pBranchIndexBuffer); + + // 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(); + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupFrondBuffers + +void CSpeedTreeWrapper::SetupFrondBuffers(void) +{ + // reference to frond structure + CSpeedTreeRT::SGeometry::SIndexed* pFronds = &(m_pGeometryCache->m_sFronds); + m_unFrondVertexCount = pFronds->m_usVertexCount; // we asked for a contiguous strip + + // check if tree has fronds + if (m_unFrondVertexCount > 1) + { + // create the vertex buffer for storing frond vertices + SFVFBranchVertex * pVertexBuffer = NULL; +#ifndef WRAPPER_USE_CPU_WIND + ms_lpd3dDevice->CreateVertexBuffer(m_unFrondVertexCount * sizeof(SFVFBranchVertex), D3DUSAGE_WRITEONLY, D3DFVF_SPEEDTREE_BRANCH_VERTEX, D3DPOOL_MANAGED, &m_pFrondVertexBuffer); + // fill the vertex buffer by interleaving SpeedTree data + m_pFrondVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), 0); +#else + ms_lpd3dDevice->CreateVertexBuffer(m_unFrondVertexCount * sizeof(SFVFBranchVertex), D3DUSAGE_DYNAMIC, D3DFVF_SPEEDTREE_BRANCH_VERTEX, D3DPOOL_SYSTEMMEM, &m_pFrondVertexBuffer); + // fill the vertex buffer by interleaving SpeedTree data + m_pFrondVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); +#endif + for (UINT i = 0; i < m_unFrondVertexCount; ++i) + { + // position + memcpy(&pVertexBuffer->m_vPosition, &(pFronds->m_pCoords[i * 3]), 3 * sizeof(float)); + + // normal or color +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + memcpy(&pVertexBuffer->m_vNormal, &(pFronds->m_pNormals[i * 3]), 3 * sizeof(float)); +#else + pVertexBuffer->m_dwDiffuseColor = pFronds->m_pColors[i]; +#endif + + // texcoords for layer 0 + pVertexBuffer->m_fTexCoords[0] = pFronds->m_pTexCoords0[i * 2]; + pVertexBuffer->m_fTexCoords[1] = pFronds->m_pTexCoords0[i * 2 + 1]; + + // texcoords for layer 1 (if enabled) +#ifdef WRAPPER_RENDER_SELF_SHADOWS + pVertexBuffer->m_fShadowCoords[0] = pFronds->m_pTexCoords1[i * 2]; + pVertexBuffer->m_fShadowCoords[1] = pFronds->m_pTexCoords1[i * 2 + 1]; +#endif + + // extra data for gpu wind +#ifdef WRAPPER_USE_GPU_WIND + pVertexBuffer->m_fWindIndex = 4.0f * pFronds->m_pWindMatrixIndices[i]; + pVertexBuffer->m_fWindWeight = pFronds->m_pWindWeights[i]; +#endif + + ++pVertexBuffer; + } + 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) + { + // 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; + } + // go back to highest LOD + 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_MANAGED, &m_pFrondIndexBuffer); + + // 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(); + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupLeafBuffers + +void CSpeedTreeWrapper::SetupLeafBuffers(void) +{ + // set up constants + const short anVertexIndices[6] = { 0, 1, 2, 0, 2, 3 }; + //const int nNumLeafMaps = m_pTextureInfo->m_uiLeafTextureCount; + + // set up the leaf counts for each LOD + m_usNumLeafLods = m_pSpeedTree->GetNumLeafLodLevels(); + + // create array of vertex buffers (one for each LOD) + m_pLeafVertexBuffer = new LPDIRECT3DVERTEXBUFFER8[m_usNumLeafLods]; + + // create array of bools for CPU updating (so we don't update for each instance) + m_pLeavesUpdatedByCpu = new bool[m_usNumLeafLods]; + + // cycle through LODs + for (UINT unLod = 0; unLod < m_usNumLeafLods; ++unLod) + { + m_pLeavesUpdatedByCpu[unLod] = false; + m_pLeafVertexBuffer[unLod] = NULL; + + // if this LOD has no leaves, skip it + unsigned short usLeafCount = m_pGeometryCache->m_sLeaves0.m_usLeafCount; + + if (usLeafCount < 1) + continue; + + SFVFLeafVertex* pVertexBuffer = NULL; + // create the vertex buffer for storing leaf vertices +#ifndef WRAPPER_USE_CPU_LEAF_PLACEMENT + ms_lpd3dDevice->CreateVertexBuffer(usLeafCount * 6 * sizeof(SFVFLeafVertex), D3DUSAGE_WRITEONLY, D3DFVF_SPEEDTREE_LEAF_VERTEX, D3DPOOL_MANAGED, &m_pLeafVertexBuffer[unLod]); + // fill the vertex buffer by interleaving SpeedTree data + m_pLeafVertexBuffer[unLod]->Lock(0, 0, reinterpret_cast(&pVertexBuffer), 0); +#else + ms_lpd3dDevice->CreateVertexBuffer(usLeafCount * 6 * sizeof(SFVFLeafVertex), D3DUSAGE_DYNAMIC, D3DFVF_SPEEDTREE_LEAF_VERTEX, D3DPOOL_SYSTEMMEM, &m_pLeafVertexBuffer[unLod]); + // fill the vertex buffer by interleaving SpeedTree data + m_pLeafVertexBuffer[unLod]->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); +#endif + SFVFLeafVertex* pVertex = pVertexBuffer; + for (UINT unLeaf = 0; unLeaf < usLeafCount; ++unLeaf) + { + const CSpeedTreeRT::SGeometry::SLeaf* pLeaf = &(m_pGeometryCache->m_sLeaves0); + for (UINT unVert = 0; unVert < 6; ++unVert) // 6 verts == 2 triangles + { + // position + memcpy(pVertex->m_vPosition, &(pLeaf->m_pCenterCoords[unLeaf * 3]), 3 * sizeof(float)); + +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + // normal + memcpy(&pVertex->m_vNormal, &(pLeaf->m_pNormals[unLeaf * 3]), 3 * sizeof(float)); +#else + // color + pVertex->m_dwDiffuseColor = pLeaf->m_pColors[unLeaf]; +#endif + + // tex coord + memcpy(pVertex->m_fTexCoords, &(pLeaf->m_pLeafMapTexCoords[unLeaf][anVertexIndices[unVert] * 2]), 2 * sizeof(float)); + + // wind weights +#ifdef WRAPPER_USE_GPU_WIND + pVertex->m_fWindIndex = 4.0f * pLeaf->m_pWindMatrixIndices[unLeaf]; + pVertex->m_fWindWeight = pLeaf->m_pWindWeights[unLeaf]; +#endif + + // GPU placement data +#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT + pVertex->m_fLeafPlacementIndex = c_nVertexShader_LeafTables + pLeaf->m_pLeafClusterIndices[unLeaf] * 4.0f + anVertexIndices[unVert]; + pVertex->m_fLeafScalarValue = m_pSpeedTree->GetLeafLodSizeAdjustments()[unLod]; +#endif + + ++pVertex; + } + } + m_pLeafVertexBuffer[unLod]->Unlock(); + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::Advance + +void CSpeedTreeWrapper::Advance(void) +{ + // compute LOD level (based on distance from camera) + m_pSpeedTree->ComputeLodLevel(); + m_pSpeedTree->SetLodLevel(1.0f); + + // compute wind +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->ComputeWindEffects(true, true, true); +#endif +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::MakeInstance +CSpeedTreeWrapper * CSpeedTreeWrapper::MakeInstance() +{ + CSpeedTreeWrapper * pInstance = new CSpeedTreeWrapper; + + // make an instance of this object's SpeedTree + pInstance->m_bIsInstance = true; + pInstance->m_pSpeedTree = m_pSpeedTree->MakeInstance(); + + if (pInstance->m_pSpeedTree) + { + // use the same materials + pInstance->m_cBranchMaterial = m_cBranchMaterial; + pInstance->m_cLeafMaterial = m_cLeafMaterial; + pInstance->m_cFrondMaterial = m_cFrondMaterial; + pInstance->m_CompositeImageInstance.SetImagePointer(m_CompositeImageInstance.GetGraphicImagePointer()); + pInstance->m_BranchImageInstance.SetImagePointer(m_BranchImageInstance.GetGraphicImagePointer()); + + if (!m_ShadowImageInstance.IsEmpty()) + pInstance->m_ShadowImageInstance.SetImagePointer(m_ShadowImageInstance.GetGraphicImagePointer()); + + pInstance->m_pTextureInfo = m_pTextureInfo; + + // use the same geometry cache + pInstance->m_pGeometryCache = m_pGeometryCache; + + // use the same buffers + pInstance->m_pBranchIndexBuffer = m_pBranchIndexBuffer; + pInstance->m_pBranchIndexCounts = m_pBranchIndexCounts; + pInstance->m_pBranchVertexBuffer = m_pBranchVertexBuffer; + pInstance->m_unBranchVertexCount = m_unBranchVertexCount; + + pInstance->m_pFrondIndexBuffer = m_pFrondIndexBuffer; + pInstance->m_pFrondIndexCounts = m_pFrondIndexCounts; + pInstance->m_pFrondVertexBuffer = m_pFrondVertexBuffer; + pInstance->m_unFrondVertexCount = m_unFrondVertexCount; + + pInstance->m_pLeafVertexBuffer = m_pLeafVertexBuffer; + pInstance->m_usNumLeafLods = m_usNumLeafLods; + pInstance->m_pLeavesUpdatedByCpu = m_pLeavesUpdatedByCpu; + + // new stuff + memcpy(pInstance->m_afPos, m_afPos, 3 * sizeof(float)); + memcpy(pInstance->m_afBoundingBox, m_afBoundingBox, 6 * sizeof(float)); + pInstance->m_pInstanceOf = this; + m_vInstances.push_back(pInstance); + } + else + { + fprintf(stderr, "SpeedTreeRT Error: %s\n", m_pSpeedTree->GetCurrentError()); + delete pInstance; + pInstance = NULL; + } + + return pInstance; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::GetInstances + +CSpeedTreeWrapper ** CSpeedTreeWrapper::GetInstances(UINT& nCount) +{ + nCount = m_vInstances.size(); + if (nCount) + return &(m_vInstances[0]); + else + return NULL; +} + +void CSpeedTreeWrapper::DeleteInstance(CSpeedTreeWrapper * pInstance) +{ + std::vector::iterator itor = m_vInstances.begin(); + + while (itor != m_vInstances.end()) + { + if (*itor == pInstance) + { + itor = m_vInstances.erase(itor); + } + else + ++itor; + } + delete pInstance; +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupBranchForTreeType + +void CSpeedTreeWrapper::SetupBranchForTreeType(void) const +{ +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + // set lighting material + STATEMANAGER.SetMaterial(m_cBranchMaterial.Get()); + SetShaderConstants(m_pSpeedTree->GetBranchMaterial()); +#endif + + // update the branch geometry for CPU wind +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry); + + if (m_pGeometryCache->m_sBranches.m_usNumStrips > 0) + { + // update the vertex array + SFVFBranchVertex* pVertexBuffer = NULL; + m_pBranchVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); + for (UINT i = 0; i < m_unBranchVertexCount; ++i) + { + memcpy(&(pVertexBuffer[i].m_vPosition), &(m_pGeometryCache->m_sBranches.m_pCoords[i * 3]), 3 * sizeof(float)); + } + m_pBranchVertexBuffer->Unlock(); + } +#endif + + LPDIRECT3DTEXTURE8 lpd3dTexture; + + // set texture map + if ((lpd3dTexture = m_BranchImageInstance.GetTextureReference().GetD3DTexture())) + STATEMANAGER.SetTexture(0, lpd3dTexture); + + // bind shadow texture +#ifdef WRAPPER_RENDER_SELF_SHADOWS + if (ms_bSelfShadowOn && (lpd3dTexture = m_ShadowImageInstance.GetTextureReference().GetD3DTexture())) + STATEMANAGER.SetTexture(1, lpd3dTexture); + else + STATEMANAGER.SetTexture(1, NULL); +#endif + + if (m_pGeometryCache->m_sBranches.m_usVertexCount > 0) + { + // activate the branch vertex buffer + STATEMANAGER.SetStreamSource(0, m_pBranchVertexBuffer, sizeof(SFVFBranchVertex)); + // set the index buffer + STATEMANAGER.SetIndices(m_pBranchIndexBuffer, 0); + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::RenderBranches + +void CSpeedTreeWrapper::RenderBranches(void) const +{ + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BranchGeometry); + + if (m_pGeometryCache->m_fBranchAlphaTestValue) + { + 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) + { + 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); + } + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupFrondForTreeType + +void CSpeedTreeWrapper::SetupFrondForTreeType(void) const +{ +#ifdef SPEEDTREE_LIGHTING_DYNAMIC + // set lighting material + STATEMANAGER.SetMaterial(m_cFrondMaterial.Get()); + SetShaderConstants(m_pSpeedTree->GetFrondMaterial()); +#endif + + // update the frond geometry for CPU wind +#ifdef WRAPPER_USE_CPU_WIND + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry); + if (m_pGeometryCache->m_sFronds.m_usNumStrips > 0) + { + // update the vertex array + SFVFBranchVertex * pVertexBuffer = NULL; + m_pFrondVertexBuffer->Lock(0, 0, reinterpret_cast(&pVertexBuffer), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); + for (UINT i = 0; i < m_unFrondVertexCount; ++i) + { + memcpy(&(pVertexBuffer[i].m_vPosition), &(m_pGeometryCache->m_sFronds.m_pCoords[i * 3]), 3 * sizeof(float)); + } + m_pFrondVertexBuffer->Unlock(); + } +#endif + + if (!m_CompositeImageInstance.IsEmpty()) + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + + // bind shadow texture +#ifdef WRAPPER_RENDER_SELF_SHADOWS + LPDIRECT3DTEXTURE8 lpd3dTexture; + + if ((lpd3dTexture = m_ShadowImageInstance.GetTextureReference().GetD3DTexture())) + STATEMANAGER.SetTexture(1, lpd3dTexture); +#endif + + if (m_pGeometryCache->m_sFronds.m_usVertexCount > 0) + { + // activate the frond vertex buffer + STATEMANAGER.SetStreamSource(0, m_pFrondVertexBuffer, sizeof(SFVFBranchVertex)); + // set the index buffer + STATEMANAGER.SetIndices(m_pFrondIndexBuffer, 0); + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::RenderFronds + +void CSpeedTreeWrapper::RenderFronds(void) const +{ + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_FrondGeometry); + + if (m_pGeometryCache->m_fFrondAlphaTestValue > 0.0f) + { + 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) + { + 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); + } + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetupLeafForTreeType + +void CSpeedTreeWrapper::SetupLeafForTreeType(void) const +{ +#ifdef SPEEDTREE_LIGHTING_DYNAMIC + // set lighting material + STATEMANAGER.SetMaterial(m_cLeafMaterial.Get()); + SetShaderConstants(m_pSpeedTree->GetLeafMaterial()); +#endif + + // 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()); + + // bind shadow texture +#ifdef WRAPPER_RENDER_SELF_SHADOWS + STATEMANAGER.SetTexture(1, NULL); +#endif +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::UploadLeafTables + +#ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT +void CSpeedTreeWrapper::UploadLeafTables(UINT uiLocation) const +{ + // query leaf cluster table from RT + UINT uiEntryCount = 0; + const float * pTable = m_pSpeedTree->GetLeafBillboardTable(uiEntryCount); + + // upload for vertex shader use + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_LeafTables, pTable, uiEntryCount / 4); +} +#endif + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::RenderLeaves + +void CSpeedTreeWrapper::RenderLeaves(void) const +{ + // update leaf geometry + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_LeafGeometry); + + // update the LOD level vertex arrays we need +#if defined(WRAPPER_USE_GPU_LEAF_PLACEMENT) && defined(WRAPPER_USE_GPU_WIND) + // do nothing, needs no updates +#else +#if !defined WRAPPER_USE_NO_WIND || defined WRAPPER_USE_CPU_LEAF_PLACEMENT + // possibly need to update both leaf LOD's + for (UINT i = 0; i < 2; ++i) + { + // 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 defined WRAPPER_USE_GPU_LEAF_PLACEMENT + if (pLeaf->m_bIsActive && !m_pLeavesUpdatedByCpu[unLod]) + { + // update the centers + SFVFLeafVertex* pVertex = NULL; + m_pLeafVertexBuffer[unLod]->Lock(0, 0, reinterpret_cast(&pVertex), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK); + for (UINT unLeaf = 0; unLeaf < pLeaf->m_usLeafCount; ++unLeaf) + { + D3DXVECTOR3 vecCenter(&(pLeaf->m_pCenterCoords[unLeaf * 3])); + (pVertex++)->m_vPosition = vecCenter; // vertex 0 + (pVertex++)->m_vPosition = vecCenter; // vertex 1 + (pVertex++)->m_vPosition = vecCenter; // vertex 2 + (pVertex++)->m_vPosition = vecCenter; // vertex 0 + (pVertex++)->m_vPosition = vecCenter; // vertex 2 + (pVertex++)->m_vPosition = vecCenter; // vertex 3 + } + m_pLeafVertexBuffer[unLod]->Unlock(); + m_pLeavesUpdatedByCpu[unLod] = true; + } +#else + if (pLeaf->m_bIsActive && m_pLeafVertexBuffer[unLod]) + { + // update the vertex positions + SFVFLeafVertex * pVertex = NULL; + + const UINT VERTEX_NUM = 8192; + if (pLeaf->m_usLeafCount*3>=VERTEX_NUM) + return; + + D3DXVECTOR3 akPosition[VERTEX_NUM]; + D3DXVECTOR3*pkPosition=akPosition; + const float* center=pLeaf->m_pCenterCoords; + for (UINT unLeaf = 0; unLeaf < pLeaf->m_usLeafCount; ++unLeaf) + { + pkPosition[0].x=pLeaf->m_pLeafMapCoords[unLeaf][0]+center[0]; + pkPosition[0].y=pLeaf->m_pLeafMapCoords[unLeaf][1]+center[1]; + pkPosition[0].z=pLeaf->m_pLeafMapCoords[unLeaf][2]+center[2]; + pkPosition[1].x=pLeaf->m_pLeafMapCoords[unLeaf][4]+center[0]; + pkPosition[1].y=pLeaf->m_pLeafMapCoords[unLeaf][5]+center[1]; + pkPosition[1].z=pLeaf->m_pLeafMapCoords[unLeaf][6]+center[2]; + pkPosition[2].x=pLeaf->m_pLeafMapCoords[unLeaf][8]+center[0]; + pkPosition[2].y=pLeaf->m_pLeafMapCoords[unLeaf][9]+center[1]; + pkPosition[2].z=pLeaf->m_pLeafMapCoords[unLeaf][10]+center[2]; + pkPosition[3]=pkPosition[0]; + pkPosition[4]=pkPosition[2]; + pkPosition[5].x=pLeaf->m_pLeafMapCoords[unLeaf][12]+center[0]; + pkPosition[5].y=pLeaf->m_pLeafMapCoords[unLeaf][13]+center[1]; + pkPosition[5].z=pLeaf->m_pLeafMapCoords[unLeaf][14]+center[2]; + pkPosition+=6; + center+=3; + } + + if (SUCCEEDED( + m_pLeafVertexBuffer[unLod]->Lock(0, 0, reinterpret_cast(&pVertex), D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK) + )) + { + UINT uVtxCount=pLeaf->m_usLeafCount*6; + for (UINT i = 0; im_pLeafMapCoords[unLeaf]+0, sizeof(D3DXVECTOR3)); + memcpy(vec1, pLeaf->m_pLeafMapCoords[unLeaf]+4, sizeof(D3DXVECTOR3)); + memcpy(vec2, pLeaf->m_pLeafMapCoords[unLeaf]+8, sizeof(D3DXVECTOR3)); + memcpy(vec3, pLeaf->m_pLeafMapCoords[unLeaf]+12, sizeof(D3DXVECTOR3)); + memcpy(vecCenter, pLeaf->m_pCenterCoords + unLeaf * 3, sizeof(D3DXVECTOR3)); + vec0 += vecCenter; + vec1 += vecCenter; + vec2 += vecCenter; + vec3 += vecCenter; + + pVertex[0].m_vPosition = vec0; + pVertex[3].m_vPosition = vec0; + pVertex[1].m_vPosition = vec1; + pVertex[4].m_vPosition = vec2; + pVertex[2].m_vPosition = vec2; + pVertex[5].m_vPosition = vec3; + */ + /* + for (UINT unLeaf = 0; unLeaf < pLeaf->m_usLeafCount; ++unLeaf) + { + D3DXVECTOR3 vecCenter(&(pLeaf->m_pCenterCoords[unLeaf * 3])); + D3DXVECTOR3 vec0(&pLeaf->m_pLeafMapCoords[unLeaf][0]); + D3DXVECTOR3 vec1(&pLeaf->m_pLeafMapCoords[unLeaf][4]); + D3DXVECTOR3 vec2(&pLeaf->m_pLeafMapCoords[unLeaf][8]); + D3DXVECTOR3 vec3(&pLeaf->m_pLeafMapCoords[unLeaf][12]); + + (pVertex++)->m_vPosition = vecCenter + vec0; // vertex 0 + (pVertex++)->m_vPosition = vecCenter + vec1; // vertex 1 + (pVertex++)->m_vPosition = vecCenter + vec2; // vertex 2 + (pVertex++)->m_vPosition = vecCenter + vec0; // vertex 0 + (pVertex++)->m_vPosition = vecCenter + vec2; // vertex 2 + (pVertex++)->m_vPosition = vecCenter + vec3; // vertex 3 + } + */ + m_pLeafVertexBuffer[unLod]->Unlock(); + } + } +#endif + } +#endif +#endif + + PositionTree(); + + // render LODs, if needed + for (UINT unLeafLevel = 0; unLeafLevel < 2; ++unLeafLevel) + { + const CSpeedTreeRT::SGeometry::SLeaf* pLeaf = (unLeafLevel == 0) ? + &m_pGeometryCache->m_sLeaves0 : &m_pGeometryCache->m_sLeaves1; + + 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); + } + } +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::EndLeafForTreeType + +void CSpeedTreeWrapper::EndLeafForTreeType(void) +{ + // reset copy flags for CPU wind + for (UINT i = 0; i < m_usNumLeafLods; ++i) + m_pLeavesUpdatedByCpu[i] = false; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::RenderBillboards + +void CSpeedTreeWrapper::RenderBillboards(void) const +{ + // render billboards in immediate mode (as close as DirectX comes to immediate mode) +#ifdef WRAPPER_BILLBOARD_MODE + if (!m_CompositeImageInstance.IsEmpty()) + STATEMANAGER.SetTexture(0, m_CompositeImageInstance.GetTextureReference().GetD3DTexture()); + + PositionTree(); + + struct SBillboardVertex + { + float fX, fY, fZ; + float fU, fV; + }; + + m_pSpeedTree->GetGeometry(*m_pGeometryCache, SpeedTree_BillboardGeometry); + + if (m_pGeometryCache->m_sBillboard0.m_bIsActive) + { + const float* pCoords = m_pGeometryCache->m_sBillboard0.m_pCoords; + const float* pTexCoords = m_pGeometryCache->m_sBillboard0.m_pTexCoords; + SBillboardVertex sVertex[4] = + { + { pCoords[0], pCoords[1], pCoords[2], pTexCoords[0], pTexCoords[1] }, + { pCoords[3], pCoords[4], pCoords[5], pTexCoords[2], pTexCoords[3] }, + { pCoords[6], pCoords[7], pCoords[8], pTexCoords[4], pTexCoords[5] }, + { pCoords[9], pCoords[10], pCoords[11], pTexCoords[6], pTexCoords[7] }, + }; + + STATEMANAGER.SetVertexShader(D3DFVF_XYZ | D3DFVF_TEX1); + STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_sBillboard0.m_fAlphaTestValue)); + + ms_faceCount += 2; + STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, sVertex, sizeof(SBillboardVertex)); + } + + // if tree supports 360 degree billboards, render the second + if (m_pGeometryCache->m_sBillboard1.m_bIsActive) + { + const float* pCoords = m_pGeometryCache->m_sBillboard1.m_pCoords; + const float* pTexCoords = m_pGeometryCache->m_sBillboard1.m_pTexCoords; + SBillboardVertex sVertex[4] = + { + { pCoords[0], pCoords[1], pCoords[2], pTexCoords[0], pTexCoords[1] }, + { pCoords[3], pCoords[4], pCoords[5], pTexCoords[2], pTexCoords[3] }, + { pCoords[6], pCoords[7], pCoords[8], pTexCoords[4], pTexCoords[5] }, + { pCoords[9], pCoords[10], pCoords[11], pTexCoords[6], pTexCoords[7] }, + }; + STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_sBillboard1.m_fAlphaTestValue)); + + ms_faceCount += 2; + STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, sVertex, sizeof(SBillboardVertex)); + } + +#ifdef WRAPPER_RENDER_HORIZONTAL_BILLBOARD + // render horizontal billboard (if enabled) + if (m_pGeometryCache->m_sHorizontalBillboard.m_bIsActive) + { + const float* pCoords = m_pGeometryCache->m_sHorizontalBillboard.m_pCoords; + const float* pTexCoords = m_pGeometryCache->m_sHorizontalBillboard.m_pTexCoords; + SBillboardVertex sVertex[4] = + { + { pCoords[0], pCoords[1], pCoords[2], pTexCoords[0], pTexCoords[1] }, + { pCoords[3], pCoords[4], pCoords[5], pTexCoords[2], pTexCoords[3] }, + { pCoords[6], pCoords[7], pCoords[8], pTexCoords[4], pTexCoords[5] }, + { pCoords[9], pCoords[10], pCoords[11], pTexCoords[6], pTexCoords[7] }, + }; + STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, DWORD(m_pGeometryCache->m_sHorizontalBillboard.m_fAlphaTestValue)); + + ms_faceCount += 2; + STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, sVertex, sizeof(SBillboardVertex)); + } + +#endif +#endif +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::CleanUpMemory + +void CSpeedTreeWrapper::CleanUpMemory(void) +{ + if (!m_bIsInstance) + m_pSpeedTree->DeleteTransientData(); +} + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::PositionTree + +void CSpeedTreeWrapper::PositionTree(void) const +{ + D3DXVECTOR3 vecPosition = m_pSpeedTree->GetTreePosition(); + D3DXMATRIX matTranslation; + D3DXMatrixIdentity(&matTranslation); + D3DXMatrixTranslation(&matTranslation, vecPosition.x, vecPosition.y, vecPosition.z); + + // store translation for client-side transformation + STATEMANAGER.SetTransform(D3DTS_WORLD, &matTranslation); + + // store translation for use in vertex shader + D3DXVECTOR4 vecConstant(vecPosition[0], vecPosition[1], vecPosition[2], 0.0f); + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_TreePos, (float*)&vecConstant, 1); +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::LoadTexture + +bool CSpeedTreeWrapper::LoadTexture(const char * pFilename, CGraphicImageInstance & rImage) +{ + CResource * pResource = CResourceManager::Instance().GetResourcePointer(pFilename); + rImage.SetImagePointer(static_cast(pResource)); + + if (rImage.IsEmpty()) + return false; + + //TraceError("SpeedTreeWrapper::LoadTexture: %s", pFilename); + return true; +} + + +/////////////////////////////////////////////////////////////////////// +// CSpeedTreeWrapper::SetShaderConstants + +void CSpeedTreeWrapper::SetShaderConstants(const float* pMaterial) const +{ + const float afUsefulConstants[] = + { + m_pSpeedTree->GetLeafLightingAdjustment(), 0.0f, 0.0f, 0.0f, + }; + + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_LeafLightingAdjustment, afUsefulConstants, 1); + + const float afMaterial[] = + { + pMaterial[0], pMaterial[1], pMaterial[2], 1.0f, + pMaterial[3], pMaterial[4], pMaterial[5], 1.0f + }; + + STATEMANAGER.SetVertexShaderConstant(c_nVertexShader_Material, afMaterial, 2); +} + +void CSpeedTreeWrapper::SetPosition(float x, float y, float z) +{ + m_afPos[0] = x; + m_afPos[1] = y; + m_afPos[2] = z; + m_pSpeedTree->SetTreePosition(x, y, z); + CGraphicObjectInstance::SetPosition(x, y, z); +} + +bool CSpeedTreeWrapper::GetBoundingSphere(D3DXVECTOR3 & v3Center, float & fRadius) +{ + float fX, fY, fZ; + + fX = m_afBoundingBox[3] - m_afBoundingBox[0]; + fY = m_afBoundingBox[4] - m_afBoundingBox[1]; + fZ = m_afBoundingBox[5] - m_afBoundingBox[2]; + + v3Center.x = 0.0f; + v3Center.y = 0.0f; + v3Center.z = fZ * 0.5f; + + fRadius = sqrtf(fX * fX + fY * fY + fZ * fZ) * 0.5f * 0.9f; // 0.9f for reduce size + + D3DXVECTOR3 vec = m_pSpeedTree->GetTreePosition(); + + v3Center+=vec; + + return true; +} + +void CSpeedTreeWrapper::CalculateBBox() +{ + float fX, fY, fZ; + + fX = m_afBoundingBox[3] - m_afBoundingBox[0]; + fY = m_afBoundingBox[4] - m_afBoundingBox[1]; + fZ = m_afBoundingBox[5] - m_afBoundingBox[2]; + + m_v3BBoxMin.x = -fX / 2.0f; + m_v3BBoxMin.y = -fY / 2.0f; + m_v3BBoxMin.z = 0.0f; + m_v3BBoxMax.x = fX / 2.0f; + m_v3BBoxMax.y = fY / 2.0f; + m_v3BBoxMax.z = fZ; + + m_v4TBBox[0] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMin.y, m_v3BBoxMin.z, 1.0f); + m_v4TBBox[1] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMax.y, m_v3BBoxMin.z, 1.0f); + m_v4TBBox[2] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMin.y, m_v3BBoxMin.z, 1.0f); + m_v4TBBox[3] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMax.y, m_v3BBoxMin.z, 1.0f); + m_v4TBBox[4] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMin.y, m_v3BBoxMax.z, 1.0f); + m_v4TBBox[5] = D3DXVECTOR4(m_v3BBoxMin.x, m_v3BBoxMax.y, m_v3BBoxMax.z, 1.0f); + m_v4TBBox[6] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMin.y, m_v3BBoxMax.z, 1.0f); + m_v4TBBox[7] = D3DXVECTOR4(m_v3BBoxMax.x, m_v3BBoxMax.y, m_v3BBoxMax.z, 1.0f); + + const D3DXMATRIX & c_rmatTransform = GetTransform(); + + for (DWORD i = 0; i < 8; ++i) + { + D3DXVec4Transform(&m_v4TBBox[i], &m_v4TBBox[i], &c_rmatTransform); + if (0 == i) + { + m_v3TBBoxMin.x = m_v4TBBox[i].x; + m_v3TBBoxMin.y = m_v4TBBox[i].y; + m_v3TBBoxMin.z = m_v4TBBox[i].z; + m_v3TBBoxMax.x = m_v4TBBox[i].x; + m_v3TBBoxMax.y = m_v4TBBox[i].y; + m_v3TBBoxMax.z = m_v4TBBox[i].z; + } + else + { + if (m_v3TBBoxMin.x > m_v4TBBox[i].x) + m_v3TBBoxMin.x = m_v4TBBox[i].x; + if (m_v3TBBoxMax.x < m_v4TBBox[i].x) + m_v3TBBoxMax.x = m_v4TBBox[i].x; + if (m_v3TBBoxMin.y > m_v4TBBox[i].y) + m_v3TBBoxMin.y = m_v4TBBox[i].y; + if (m_v3TBBoxMax.y < m_v4TBBox[i].y) + m_v3TBBoxMax.y = m_v4TBBox[i].y; + if (m_v3TBBoxMin.z > m_v4TBBox[i].z) + m_v3TBBoxMin.z = m_v4TBBox[i].z; + if (m_v3TBBoxMax.z < m_v4TBBox[i].z) + m_v3TBBoxMax.z = m_v4TBBox[i].z; + } + } +} + +// collision detection routines +UINT CSpeedTreeWrapper::GetCollisionObjectCount() +{ + assert(m_pSpeedTree); + return m_pSpeedTree->GetCollisionObjectCount(); +} + +void CSpeedTreeWrapper::GetCollisionObject(UINT nIndex, CSpeedTreeRT::ECollisionObjectType& eType, float* pPosition, float* pDimensions) +{ + assert(m_pSpeedTree); + m_pSpeedTree->GetCollisionObject(nIndex, eType, pPosition, pDimensions); +} + + +const float * CSpeedTreeWrapper::GetPosition() +{ + return m_afPos; +} + +void CSpeedTreeWrapper::GetTreeSize(float & r_fSize, float & r_fVariance) +{ + m_pSpeedTree->GetTreeSize(r_fSize, r_fVariance); +} + +// pscdVector may be null +void CSpeedTreeWrapper::OnUpdateCollisionData(const CStaticCollisionDataVector * /*pscdVector*/) +{ + D3DXMATRIX mat; + D3DXMatrixTranslation(&mat, m_afPos[0], m_afPos[1], m_afPos[2]); + + ///// + for (UINT i = 0; i < GetCollisionObjectCount(); ++i) + { + CSpeedTreeRT::ECollisionObjectType ObjectType; + CStaticCollisionData CollisionData; + + GetCollisionObject(i, ObjectType, (float * )&CollisionData.v3Position, CollisionData.fDimensions); + + if (ObjectType == CSpeedTreeRT::CO_BOX) + continue; + + switch(ObjectType) + { + case CSpeedTreeRT::CO_SPHERE: + CollisionData.dwType = COLLISION_TYPE_SPHERE; + CollisionData.fDimensions[0] = CollisionData.fDimensions[0] /** fSizeRatio*/; + //AddCollision(&CollisionData); + break; + + case CSpeedTreeRT::CO_CYLINDER: + CollisionData.dwType = COLLISION_TYPE_CYLINDER; + CollisionData.fDimensions[0] = CollisionData.fDimensions[0] /** fSizeRatio*/; + CollisionData.fDimensions[1] = CollisionData.fDimensions[1] /** fSizeRatio*/; + //AddCollision(&CollisionData); + break; + + /*case CSpeedTreeRT::CO_BOX: + break;*/ + } + AddCollision(&CollisionData, &mat); + } +} diff --git a/extension/src/port/SpeedTreeLib/VertexShaders.h b/extension/src/port/SpeedTreeLib/VertexShaders.h new file mode 100644 index 00000000..6d5f2f9d --- /dev/null +++ b/extension/src/port/SpeedTreeLib/VertexShaders.h @@ -0,0 +1,332 @@ +/////////////////////////////////////////////////////////////////////// +// SpeedTreeRT DirectX Example +// +// (c) 2003 IDV, Inc. +// +// This example demonstrates how to render trees using SpeedTreeRT +// and DirectX. Techniques illustrated include ".spt" file parsing, +// static lighting, dynamic lighting, LOD implementation, cloning, +// instancing, and dynamic wind effects. +// +// +// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION *** +// +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Interactive Data Visualization and may +// not be copied or disclosed except in accordance with the terms of +// that agreement. +// +// Copyright (c) 2001-2003 IDV, Inc. +// All Rights Reserved. +// +// IDV, Inc. +// 1233 Washington St. Suite 610 +// Columbia, SC 29201 +// Voice: (803) 799-1699 +// Fax: (803) 931-0320 +// Web: http://www.idvinc.com + +/////////////////////////////////////////////////////////////////////// +// Includes + +#pragma once +#include "SpeedTreeConfig.h" +#include +#include + +/////////////////////////////////////////////////////////////////////// +// Branch & Frond Vertex Formats + +static DWORD D3DFVF_SPEEDTREE_BRANCH_VERTEX = + D3DFVF_XYZ | // always have the position + #ifdef WRAPPER_USE_DYNAMIC_LIGHTING // precomputed colors or geometric normals + D3DFVF_NORMAL | + #else + D3DFVF_DIFFUSE | + #endif + #ifdef WRAPPER_RENDER_SELF_SHADOWS + D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE2(1) // shadow texture coordinates + #else + D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0) // always have first texture layer coords + #endif + #ifdef WRAPPER_USE_GPU_WIND + | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE2(2) // GPU Only - wind weight and index passed in second texture layer + #endif + ; + +/////////////////////////////////////////////////////////////////////// +// FVF Branch Vertex Structure + +struct SFVFBranchVertex +{ + D3DXVECTOR3 m_vPosition; // Always Used +#ifdef WRAPPER_USE_DYNAMIC_LIGHTING + D3DXVECTOR3 m_vNormal; // Dynamic Lighting Only +#else + DWORD m_dwDiffuseColor; // Static Lighting Only +#endif + FLOAT m_fTexCoords[2]; // Always Used +#ifdef WRAPPER_RENDER_SELF_SHADOWS + FLOAT m_fShadowCoords[2]; // Texture coordinates for the shadows +#endif +#ifdef WRAPPER_USE_GPU_WIND + FLOAT m_fWindIndex; // GPU Only + FLOAT m_fWindWeight; +#endif +}; + + +/////////////////////////////////////////////////////////////////////// +// Branch/Frond Vertex Program + +static const char g_achSimpleVertexProgram[] = +{ + "vs.1.1\n" // identity shader version + + "mov oT0.xy, v7\n" // always pass texcoord0 through + + #ifdef WRAPPER_RENDER_SELF_SHADOWS + "mov oT1.xy, v8\n" // pass shadow texcoords through if enabled + #endif + + // retrieve and convert wind matrix index + "mov a0.x, v9.x\n" + + // perform wind interpolation + "m4x4 r1, v0, c[54+a0.x]\n" // compute full wind effect + "sub r2, r1, v0\n" // compute difference between full wind and none + "mov r3.x, v9.y\n" // mad can't access two v's at once, use r3.x as tmp + "mad r1, r2, r3.x, v0\n" // perform interpolation + + "add r2, c[52], r1\n" // translate to tree's position + "m4x4 oPos, r2, c[0]\n" // project to screen + + #ifdef WRAPPER_USE_FOG + "dp4 r1, r2, c[2]\n" // find distance to vertex + "sub r2.x, c[85].y, r1.z\n" // linear fogging + "mul oFog, r2.x, c[85].z\n" // write to fog register + #endif + + #ifdef WRAPPER_USE_STATIC_LIGHTING + "mov oD0, v5\n" // pass color through + #else + "mov r1, c[74]\n" // can only use one const register per instruction + "mul r5, c[73], r1\n" // diffuse values + + "mov r1, c[75]\n" // can only use one const register per instruction + "mul r4, c[72], r1\n" // ambient values + + "dp3 r2, v3, c[71]\n" // dot light direction with normal +// "max r2.x, r2.x, c[70].x\n" // limit it + "mad oD0, r2.x, r5, r4\n" // compute the final color + #endif +}; + + +/////////////////////////////////////////////////////////////////////// +// LoadBranchShader + +static DWORD LoadBranchShader(LPDIRECT3DDEVICE8 pDx) +{ + #ifndef WRAPPER_USE_GPU_WIND + return D3DFVF_SPEEDTREE_BRANCH_VERTEX; + #endif + + // branch shader declaration + DWORD pBranchShaderDecl[] = + { + D3DVSD_STREAM(0), + D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3), + #ifdef WRAPPER_USE_DYNAMIC_LIGHTING + D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3), + #else + D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR), + #endif + D3DVSD_REG(D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2), + #ifdef WRAPPER_RENDER_SELF_SHADOWS + D3DVSD_REG(D3DVSDE_TEXCOORD1, D3DVSDT_FLOAT2), + #endif + #ifdef WRAPPER_USE_GPU_WIND + D3DVSD_REG(D3DVSDE_TEXCOORD2, D3DVSDT_FLOAT2), + #endif + D3DVSD_END( ) + }; + + // assemble shader + DWORD dwShader; + LPD3DXBUFFER pCode, pError; + + if (D3DXAssembleShader(g_achSimpleVertexProgram, sizeof(g_achSimpleVertexProgram) - 1, 0, NULL, &pCode, &pError) == D3D_OK) + { + if (pDx->CreateVertexShader(pBranchShaderDecl, (DWORD*) pCode->GetBufferPointer( ), &dwShader, 0) != D3D_OK) + { + char szError[1024]; + sprintf(szError, "Failed to create branch vertex shader."); + MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + } + } + else + { + char szError[1024]; + sprintf(szError, "Failed to assemble branch vertex shader.\nThe error reported is [ %s ].\n", pError->GetBufferPointer( )); + MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + } + + if (pCode) + pCode->Release(); + + return dwShader; +} + +/////////////////////////////////////////////////////////////////////// +// Leaf Vertex Formats + +static DWORD D3DFVF_SPEEDTREE_LEAF_VERTEX = + D3DFVF_XYZ | // always have the position + #ifdef WRAPPER_USE_DYNAMIC_LIGHTING // precomputed colors or geometric normals + D3DFVF_NORMAL | + #else + D3DFVF_DIFFUSE | + #endif + D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0) // always have first texture layer coords + #if defined WRAPPER_USE_GPU_WIND || defined WRAPPER_USE_GPU_LEAF_PLACEMENT + | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE4(2) // GPU Only - wind weight and index passed in second texture layer + #endif + ; + + +/////////////////////////////////////////////////////////////////////// +// FVF Leaf Vertex Structure + +struct SFVFLeafVertex +{ + D3DXVECTOR3 m_vPosition; // Always Used + #ifdef WRAPPER_USE_DYNAMIC_LIGHTING + D3DXVECTOR3 m_vNormal; // Dynamic Lighting Only + #else + DWORD m_dwDiffuseColor; // Static Lighting Only + #endif + FLOAT m_fTexCoords[2]; // Always Used + #if defined WRAPPER_USE_GPU_WIND || defined WRAPPER_USE_GPU_LEAF_PLACEMENT + FLOAT m_fWindIndex; // Only used when GPU is involved + FLOAT m_fWindWeight; + FLOAT m_fLeafPlacementIndex; + FLOAT m_fLeafScalarValue; + #endif +}; + + +/////////////////////////////////////////////////////////////////////// +// Leaf Vertex Program + +static const char g_achLeafVertexProgram[] = +{ + "vs.1.1\n" // identity shader version + + "mov oT0.xy, v7\n" // always pass texcoord0 through + + #ifdef WRAPPER_USE_GPU_WIND + // retrieve and convert wind matrix index + "mov a0.x, v9.x\n" + + // perform wind interpolation + "m4x4 r1, v0, c[54+a0.x]\n" // compute full wind effect + "sub r2, r1, v0\n" // compute difference between full wind and none + "mov r3.x, v9.y\n" // mad can't access two v's at once, use r3.x as tmp + "mad r0, r2, r3.x, v0\n" // perform interpolation + #else + "mov r0, v0\n" // wind already handled, pass the vertex through + #endif + + #ifdef WRAPPER_USE_GPU_LEAF_PLACEMENT + "mov a0.x, v9.z\n" // place the leaves + "mul r1, c[a0.x], v9.w\n" + "add r0, r1, r0\n" + #endif + + "add r0, c[52], r0\n" // translate to tree's position + "m4x4 oPos, r0, c[0]\n" // project to screen + + #ifdef WRAPPER_USE_FOG + "dp4 r1, r0, c[2]\n" // find distance to vertex + "sub r2.x, c[85].y, r1.z\n" // + "mul oFog, r2.x, c[85].z\n" + #endif + + #ifdef WRAPPER_USE_STATIC_LIGHTING + "mov oD0, v5\n" // pass color through + #else + "mov r1, c[74]\n" // can only use one const register per instruction + "mul r5, c[73], r1\n" // diffuse values + + "mov r1, c[75]\n" // can only use one const register per instruction + "mul r4, c[72], r1\n" // ambient values + + "dp3 r2.x, v3, c[71]\n" // dot light direction with normal + "max r2.x, r2.x, c[70].x\n" // limit it + "mad oD0, r2.x, r5, r4\n" // compute the final color + #endif +}; + + +/////////////////////////////////////////////////////////////////////// +// LoadLeafShader + +static DWORD LoadLeafShader(LPDIRECT3DDEVICE8 pDx) +{ + DWORD dwShader = D3DFVF_SPEEDTREE_LEAF_VERTEX; + + #if defined WRAPPER_USE_GPU_LEAF_PLACEMENT || defined WRAPPER_USE_GPU_WIND + + // leaf shader declaration + DWORD pLeafShaderDecl[ ] = + { + D3DVSD_STREAM(0), + D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3), + #ifdef WRAPPER_USE_DYNAMIC_LIGHTING + D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3), + #else + D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR), + #endif + D3DVSD_REG(D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2), + D3DVSD_REG(D3DVSDE_TEXCOORD2, D3DVSDT_FLOAT4), + D3DVSD_END( ) + }; + + // assemble shader + LPD3DXBUFFER pCode, pError; + + if (D3DXAssembleShader(g_achLeafVertexProgram, sizeof(g_achLeafVertexProgram) - 1, 0, NULL, &pCode, &pError) == D3D_OK) + { + if (pDx->CreateVertexShader(pLeafShaderDecl, (DWORD*) pCode->GetBufferPointer( ), &dwShader, 0) != D3D_OK) + { + Tracef("Failed to create leaf vertex shader."); + /* + char szError[1024]; + sprintf(szError, "Failed to create leaf vertex shader."); + MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + */ + } + } + else + { + Tracef("Failed to assemble leaf vertex shader. The error reported is [ %s ].\n", pError->GetBufferPointer( )); + /* + char szError[1024]; + sprintf(szError, "Failed to assemble leaf vertex shader. The error reported is [ %s ].\n", pError->GetBufferPointer( )); + MessageBox(NULL, szError, "Vertex Shader Error", MB_ICONSTOP); + */ + } + + if (pCode) + pCode->Release( ); + + #else + + dwShader = D3DFVF_SPEEDTREE_LEAF_VERTEX; + + #endif + + return dwShader; +} + \ No newline at end of file diff --git a/extension/src/port/common/D3D8Types.h b/extension/src/port/common/D3D8Types.h index 93fd11b7..65a83fc3 100644 --- a/extension/src/port/common/D3D8Types.h +++ b/extension/src/port/common/D3D8Types.h @@ -503,6 +503,49 @@ typedef enum _D3DVERTEXBLENDFLAGS { #define D3DFVF_TEX8 0x800 #define D3DFVF_LASTBETA_UBYTE4 0x1000 #define D3DFVF_RESERVED2 0xE000 +// d3d8.h +#define D3D_OK S_OK + +// d3d8types.h vertex shader declaration tokens +typedef enum _D3DVSD_TOKENTYPE +{ + D3DVSD_TOKEN_NOP = 0, + D3DVSD_TOKEN_STREAM = 1, + D3DVSD_TOKEN_STREAMDATA = 2, + D3DVSD_TOKEN_TESSELLATOR = 3, + D3DVSD_TOKEN_CONSTMEM = 4, + D3DVSD_TOKEN_EXT = 5, + D3DVSD_TOKEN_END = 7, + D3DVSD_FORCE_DWORD = 0x7fffffff, +} D3DVSD_TOKENTYPE; +#define D3DVSD_TOKENTYPESHIFT 29 +#define D3DVSD_TOKENTYPEMASK (7 << D3DVSD_TOKENTYPESHIFT) +#define D3DVSD_DATATYPESHIFT 16 +#define D3DVSD_MAKETOKENTYPE(tokenType) ((tokenType << D3DVSD_TOKENTYPESHIFT) & D3DVSD_TOKENTYPEMASK) +#define D3DVSD_STREAM(_StreamNumber) (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAM) | (_StreamNumber)) +#define D3DVSD_REG(_VertexRegister, _Type) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAMDATA) | ((_Type) << D3DVSD_DATATYPESHIFT) | (_VertexRegister)) +#define D3DVSD_END() 0xFFFFFFFF +#define D3DVSDT_FLOAT1 0x00 +#define D3DVSDT_FLOAT2 0x01 +#define D3DVSDT_FLOAT3 0x02 +#define D3DVSDT_FLOAT4 0x03 +#define D3DVSDT_D3DCOLOR 0x04 +#define D3DVSDT_UBYTE4 0x05 +#define D3DVSDT_SHORT2 0x06 +#define D3DVSDT_SHORT4 0x07 +#define D3DVSDE_POSITION 0 +#define D3DVSDE_BLENDWEIGHT 1 +#define D3DVSDE_BLENDINDICES 2 +#define D3DVSDE_NORMAL 3 +#define D3DVSDE_PSIZE 4 +#define D3DVSDE_DIFFUSE 5 +#define D3DVSDE_SPECULAR 6 +#define D3DVSDE_TEXCOORD0 7 +#define D3DVSDE_TEXCOORD1 8 +#define D3DVSDE_TEXCOORD2 9 +#define D3DVSDE_TEXCOORD3 10 + #define D3DFVF_TEXTUREFORMAT2 0 #define D3DFVF_TEXTUREFORMAT1 3 #define D3DFVF_TEXTUREFORMAT3 1 @@ -777,6 +820,8 @@ struct IDirect3DDevice8 virtual HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) = 0; virtual HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) = 0; virtual HRESULT SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) = 0; + virtual HRESULT CreateVertexShader(const DWORD* pDeclaration, const DWORD* pFunction, DWORD* pHandle, DWORD Usage) = 0; + virtual HRESULT DeleteVertexShader(DWORD Handle) = 0; virtual HRESULT SetVertexShader(DWORD Handle) = 0; virtual HRESULT SetPixelShader(DWORD Handle) = 0; virtual HRESULT SetVertexShaderConstant(DWORD Register, const void* pConstantData, DWORD ConstantCount) = 0; diff --git a/extension/src/port/common/Win32Crt.cpp b/extension/src/port/common/Win32Crt.cpp index 8c99b6f8..1df44299 100644 --- a/extension/src/port/common/Win32Crt.cpp +++ b/extension/src/port/common/Win32Crt.cpp @@ -423,3 +423,9 @@ LPSTR CharPrevExA(WORD CodePage, LPCSTR lpStart, LPCSTR lpCurrentChar, DWORD dwF } #endif // !_WIN32 + +int MessageBox(HWND, LPCSTR lpText, LPCSTR lpCaption, UINT) +{ + std::fprintf(stderr, "[MessageBox] %s: %s\n", lpCaption ? lpCaption : "", lpText ? lpText : ""); + return IDOK; +} diff --git a/extension/src/port/common/Win32Crt.h b/extension/src/port/common/Win32Crt.h index 6aa3d093..2d733a61 100644 --- a/extension/src/port/common/Win32Crt.h +++ b/extension/src/port/common/Win32Crt.h @@ -72,6 +72,21 @@ BOOL FindNextFile(HANDLE handle, WIN32_FIND_DATA* data); BOOL FindClose(HANDLE handle); void Sleep(DWORD milliseconds); +// MSVC __min / __max +#ifndef __min +#define __min(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef __max +#define __max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +// user32 MessageBox (ANSI): no dialog on these hosts; writes caption and text to stderr, returns IDOK. +#define MB_OK 0x00000000L +#define MB_ICONHAND 0x00000010L +#define MB_ICONSTOP MB_ICONHAND +#define IDOK 1 +int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); + // winmm `timeGetTime`: milliseconds since system start, wrapping at 2^32. DWORD timeGetTime(); DWORD GetTickCount(); diff --git a/extension/src/port/common/shim/sdk/SpeedTreeRT.h b/extension/src/port/common/shim/sdk/SpeedTreeRT.h index f0f5982c..a7834e89 100644 --- a/extension/src/port/common/shim/sdk/SpeedTreeRT.h +++ b/extension/src/port/common/shim/sdk/SpeedTreeRT.h @@ -1,21 +1,463 @@ -#pragma once -// Shim for IDV SpeedTreeRT (40250 extern/include): the CSpeedTreeRT members that -// SpeedTreeLib headers name. Tree geometry and wind go through platform/, which defines them. +// Shim for IDV SpeedTreeRT 1.6.0 (40250 extern/include): the public section below is +// the SDK header verbatim. SpeedTreeRT.lib is closed Windows COFF, so the private section is replaced +// by one opaque pointer and the members SpeedTreeLib calls are implemented in +// platform/SpeedTreeLib/SpeedTreeRT.cpp. stands in for the size_t the SDK got from . +#include +/////////////////////////////////////////////////////////////////////// +// Name: SpeedTreeRT.h +// +// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION *** +// +// Copyright (c) 2001-2003 IDV, Inc. +// All Rights Reserved. +// +// IDV, Inc. +// 1233 Washington St. Suite 610 +// Columbia, SC 29201 +// Voice: (803) 799-1699 +// Fax: (803) 931-0320 +// Web: http://www.idvinc.com +// +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Interactive Data Visualization and may not +// be copied or disclosed except in accordance with the terms of that +// agreement. +// +// Release version 1.6.0 (December 19, 2003) -class CSpeedTreeRT +#pragma once + +// define static or dynamic library build +#ifdef SPEEDTREERT_DYNAMIC_LIB + #ifdef IDV_SPEEDTREERT_EXPORTS + #define BUILD_SPEED_TREE_RT_SET __declspec(dllexport) + #else + #define BUILD_SPEED_TREE_RT_SET __declspec(dllimport) + #endif +#else + #define BUILD_SPEED_TREE_RT_SET +#endif + +// big or little endian system +#ifdef __ppc__ + #define ST_BIG_ENDIAN +#else + #define ST_LITTLE_ENDIAN +#endif + +// Macintosh-specific +#ifdef __ppc__ +#pragma export on +#endif + +// forward refernces +class CIndexedGeometry; +class CTreeEngine; +class CLeafGeometry; +class CLightingEngine; +class CWindEngine; +class CTreeFileAccess; +class CSimpleBillboard; +class CFrondEngine; +struct STreeInstanceData; +struct SInstanceList; +struct SEmbeddedTexCoords; +struct SCollisionObjects; +class CProjectedShadow; + + +/////////////////////////////////////////////////////////////////////// +// class SpeedTreeRT +// +// In an effort to make the SpeedTreeRT.h header file dependency free +// and easy to include into almost any project, a number of steps have +// been taken: +// +// 1. No external header files need to be included by SpeedTreeRT.h +// or by the application before including it. +// +// 2. Most of the implementation of the class is hidden by pointers +// to the major sections of the library (the internal classes +// can then just be forward-referenced) +// +// 3. Where possible, basic C++ datatypes are used to define the +// member functions' parameters. +// +// Because almost all of the implementation details are hidden, none of +// the functions for CSpeedTreeRT are inlined. However, inlined functions +// were copiously used within the library. + +class BUILD_SPEED_TREE_RT_SET CSpeedTreeRT { public: - enum ECollisionObjectType - { - CO_SPHERE, - CO_CYLINDER, - CO_BOX - }; + /////////////////////////////////////////////////////////////////////// + // Enumerations - struct SGeometry; - struct STextures; + enum EWindMethod + { + WIND_GPU, WIND_CPU, WIND_NONE + }; - float GetLeafLightingAdjustment() const; - float SetWindStrength(float fNewStrength, float fOldStrength = -1.0f, float fFrequencyTimeOffset = -1.0f); - void GetCollisionObject(unsigned int nIndex, ECollisionObjectType& eType, float* pPosition, float* pDimensions); + enum ELodMethod + { + LOD_POP, LOD_SMOOTH, LOD_NONE = 3 + }; + + enum ELightingMethod + { + LIGHT_DYNAMIC, LIGHT_STATIC + }; + + enum EStaticLightingStyle + { + SLS_BASIC, SLS_USE_LIGHT_SOURCES, SLS_SIMULATE_SHADOWS + }; + + enum ECollisionObjectType + { + CO_SPHERE, CO_CYLINDER, CO_BOX + }; + + + /////////////////////////////////////////////////////////////////////// + // SGeometry bit vectors + // + // Passed into GetGeometry() in order to mask out unneeded geometric elements + + #define SpeedTree_BranchGeometry (1 << 0) + #define SpeedTree_FrondGeometry (1 << 1) + #define SpeedTree_LeafGeometry (1 << 2) + #define SpeedTree_BillboardGeometry (1 << 3) + #define SpeedTree_SimpleBillboardOverride (1 << 4) + #define SpeedTree_AllGeometry SpeedTree_BranchGeometry + SpeedTree_FrondGeometry + SpeedTree_LeafGeometry + SpeedTree_BillboardGeometry + + + /////////////////////////////////////////////////////////////////////// + // struct SGeometry declaration + + struct BUILD_SPEED_TREE_RT_SET SGeometry + { + SGeometry( ); + ~SGeometry( ); + + /////////////////////////////////////////////////////////////////////// + // struct SGeometry::SIndexed declaration + + struct BUILD_SPEED_TREE_RT_SET SIndexed + { + SIndexed( ); + ~SIndexed( ); + + // these values change depending on the active discrete LOD level + int m_nDiscreteLodLevel; // range: [0, GetNumBranch/FrondLodLevels( ) - 1], -1 if inactive + unsigned short m_usNumStrips; // total number of strips in current LOD + const unsigned short* m_pStripLengths; // lengths of strips in current LOD (m_usNumStrips in length) + const unsigned short** m_pStrips; // triangle strip indices (m_usNumStrips in length) + + // these values are shared across all discete LOD levels + unsigned short m_usVertexCount; // total vertex count in tables, referenced by all LOD levels + const unsigned long* m_pColors; // RGBA values for each leaf - static lighting only (m_usVertexCount in length) + const float* m_pNormals; // normals for each vertex (3 * m_usVertexCount in length) + const float* m_pBinormals; // binormals (bump mapping) for each vertex (3 * m_usVertexCount in length) + const float* m_pTangents; // tangents (bump mapping) for each vertex (3 * m_usVertexCount in length) + const float* m_pCoords; // coordinates for each vertex (3 * m_usVertexCount in length) + const float* m_pTexCoords0; // 1st layer (s,t) texcoords for each vertex (2 * m_usVertexCount in length) + const float* m_pTexCoords1; // 2nd layer (s,t) texcoords for each vertex (2 * m_usVertexCount in length) + const float* m_pWindWeights; // values from from 0.0 for rigid to 1.0 for flexible (m_usVertexCount in length) + const unsigned char* m_pWindMatrixIndices; // table of wind matrix indices (m_usVertexCount in length) + }; + + + /////////////////////////////////////////////////////////////////////// + // struct SGeometry::SLeaf declaration + + struct BUILD_SPEED_TREE_RT_SET SLeaf + { + SLeaf( ); + ~SLeaf( ); + + // active LOD level data + bool m_bIsActive; // flag indicating visibility + float m_fAlphaTestValue; // 0.0 to 255.0 alpha testing value, used for fading + int m_nDiscreteLodLevel; // range: [0, GetNumLeafLodLevels( ) - 1] + unsigned short m_usLeafCount; // number of leaves stored in this structure + + // tables for referencing the leaf cluster table + const unsigned char* m_pLeafMapIndices; // references which leaf texture map used for each leaf (m_usLeafCount in length) + const unsigned char* m_pLeafClusterIndices; // references which leaf cluster used for each leaf (m_usLeafCount in length) + const float* m_pCenterCoords; // (x,y,z) values for the centers of leaf clusters (3 * m_usLeafCount in length) + const float** m_pLeafMapTexCoords; // table of unique leaf cluster texcoords (m_usLeafCount in length) - each entry + // points to 4 pairs of (s,t) texcoords stored in one contiguous array + const float** m_pLeafMapCoords; // table of unique leaf cluster coordinates (m_usLeafCount in length) - each entry + // points to 4 sets of (x,y,z,0) coordinates stored in one contiguous array + // remaining vertex attributes + const unsigned long* m_pColors; // RGBA values for each leaf (m_usLeafCount in length) + const float* m_pNormals; // normals for each leaf (3 * m_usLeafCount in length) + const float* m_pBinormals; // binormals (bump mapping) for each leaf (3 * m_usLeafCount in length) + const float* m_pTangents; // tangents (bump mapping) for each leaf (3 * m_usLeafCount in length) + const float* m_pWindWeights; // values from from 0.0 for rigid to 1.0 for flexible (m_usLeafCount in length) + const unsigned char* m_pWindMatrixIndices; // table of wind matrix indices (m_usLeafCount in length) + }; + + + /////////////////////////////////////////////////////////////////////// + // struct SGeometry::SBillboard declaration + + struct BUILD_SPEED_TREE_RT_SET SBillboard + { + SBillboard( ); + ~SBillboard( ); + + bool m_bIsActive; // flag indicating visibility + const float* m_pTexCoords; // 4 pairs of (s,t) texcoords stored in one contiguous array + const float* m_pCoords; // 4 sets of (x,y,z) coordindates stored in one contiguous array + float m_fAlphaTestValue; // 0.0 to 255.0 alpha testing value, used for fading + }; + + /////////////////////////////////////////////////////////////////////// + // branch geometry + + SIndexed m_sBranches; // holds the branch vertices and index buffers for all + // of the discrete LOD levels + float m_fBranchAlphaTestValue; // 0.0 to 255.0 alpha testing value, used for fading + + /////////////////////////////////////////////////////////////////////// + // frond geometry + + SIndexed m_sFronds; // holds the frond vertices and index buffers for all + // of the discrete LOD levels + float m_fFrondAlphaTestValue; // 0.0 to 255.0 alpha testing value, used for fading + + /////////////////////////////////////////////////////////////////////// + // leaf geometry + + SLeaf m_sLeaves0; // holds the primary leaf geometry, alpha fades into + SLeaf m_sLeaves1; // m_sLeaves1 during LOD transitions + + /////////////////////////////////////////////////////////////////////// + // billboard geometry + + SBillboard m_sBillboard0; // holds the main simple billboard geometry, alpha fades + SBillboard m_sBillboard1; // into m_sBillboard1 in 360 degree mode + SBillboard m_sHorizontalBillboard; // optional horizontal billboard used for aerial views + }; + + + /////////////////////////////////////////////////////////////////////// + // struct SGeometry::STextures declaration + + struct BUILD_SPEED_TREE_RT_SET STextures + { + STextures( ); + ~STextures( ); + + // branches + const char* m_pBranchTextureFilename; // null-terminated string + + // leaves + unsigned int m_uiLeafTextureCount; // the number of char* elements in m_pLeafTextureFilenames + const char** m_pLeafTextureFilenames; // array of null-terminated strings m_uiLeafTextureCount in size + + // fronds + unsigned int m_uiFrondTextureCount; // the number of char* elements in m_pFrondTextureFilenames + const char** m_pFrondTextureFilenames; // array of null-terminated strings m_uiFrondTextureCount in size + + // composite + const char* m_pCompositeFilename; // null-terminated string + + // self-shadow + const char* m_pSelfShadowFilename; // null-terminated string + }; + + + /////////////////////////////////////////////////////////////////////// + // Constructor/Destructor + + CSpeedTreeRT( ); + ~CSpeedTreeRT( ); + + + /////////////////////////////////////////////////////////////////////// + // Memory allocation + +static void* operator new(size_t nSize); +static void* operator new[](size_t nSize); +static void operator delete(void* pRawMemory); +static void operator delete[](void* pRawMemory); + + + /////////////////////////////////////////////////////////////////////// + // Specifying a tree model + + bool Compute(const float* pTransform = 0, unsigned int nSeed = 1, bool bCompositeStrips = true); + CSpeedTreeRT* Clone(float x = 0.0f, float y = 0.0f, float z = 0.0f, unsigned int nSeed = 0) const; + const CSpeedTreeRT* InstanceOf(void) const; + CSpeedTreeRT* MakeInstance(void); + void DeleteTransientData(void); + + bool LoadTree(const char* pFilename); + bool LoadTree(const unsigned char* pBlock, unsigned int nNumBytes); + unsigned char* SaveTree(unsigned int& nNumBytes, bool bSaveLeaves = false) const; + + void GetTreeSize(float& fSize, float& fVariance) const; + void SetTreeSize(float fNewSize, float fNewVariance = 0.0f); + unsigned int GetSeed( ) const; + + const float* GetTreePosition(void) const; + void SetTreePosition(float x, float y, float z); + + void SetLeafTargetAlphaMask(unsigned char ucMask = 0x54); + + + /////////////////////////////////////////////////////////////////////// + // Lighting + + // lighting style + ELightingMethod GetBranchLightingMethod(void) const; + void SetBranchLightingMethod(ELightingMethod eMethod); + ELightingMethod GetLeafLightingMethod(void) const; + void SetLeafLightingMethod(ELightingMethod eMethod); + ELightingMethod GetFrondLightingMethod(void) const; + void SetFrondLightingMethod(ELightingMethod eMethod); + + EStaticLightingStyle GetStaticLightingStyle(void) const; + void SetStaticLightingStyle(EStaticLightingStyle eStyle); + float GetLeafLightingAdjustment( ) const; + void SetLeafLightingAdjustment(float fScalar); + + // global lighting state +static bool GetLightState(unsigned int nLightIndex); +static void SetLightState(unsigned int nLightIndex, bool bLightOn); +static const float* GetLightAttributes(unsigned int nLightIndex); +static void SetLightAttributes(unsigned int nLightIndex, const float* pLightAttributes); + + // branch material + const float* GetBranchMaterial(void) const; + void SetBranchMaterial(const float* pMaterial); + + // leaf material + const float* GetLeafMaterial(void) const; + void SetLeafMaterial(const float* pMaterial); + + // frond material + const float* GetFrondMaterial(void) const; + void SetFrondMaterial(const float* pMaterial); + + + /////////////////////////////////////////////////////////////////////// + // Camera + +static void GetCamera(float* pPosition, float* pDirection); +static void SetCamera(const float* pPosition, const float* pDirection); + + + /////////////////////////////////////////////////////////////////////// + // Wind + +static void SetTime(float fTime); + void ComputeWindEffects(bool bBranches, bool bLeaves, bool bFronds = true); + void ResetLeafWindState(void); + + bool GetLeafRockingState(void) const; + void SetLeafRockingState(bool bFlag); + void SetNumLeafRockingGroups(unsigned int nRockingGroups); + + EWindMethod GetLeafWindMethod(void) const; + void SetLeafWindMethod(EWindMethod eMethod); + EWindMethod GetBranchWindMethod(void) const; + void SetBranchWindMethod(EWindMethod eMethod); + EWindMethod GetFrondWindMethod(void) const; + void SetFrondWindMethod(EWindMethod eMethod); + + float GetWindStrength(void) const; + float SetWindStrength(float fNewStrength, float fOldStrength = -1.0f, float fFrequencyTimeOffset = -1.0f); + +static void SetNumWindMatrices(unsigned int nNumMatrices); +static void SetWindMatrix(unsigned int nMatrixIndex, const float* pMatrix); + void GetLocalMatrices(unsigned int& nStartingIndex, unsigned int& nMatrixSpan); + void SetLocalMatrices(unsigned int nStartingMatrix, unsigned int nMatrixSpan); + + + /////////////////////////////////////////////////////////////////////// + // LOD + + void ComputeLodLevel(void); + float GetLodLevel(void) const; + void SetLodLevel(float fLodLevel); +static void SetDropToBillboard(bool bFlag); + + void GetLodLimits(float& fNear, float& fFar) const; + void SetLodLimits(float fNear, float fFar); + + short GetDiscreteBranchLodLevel(float fLodLevel = -1.0f) const; + unsigned short GetDiscreteLeafLodLevel(float fLodLevel = -1.0f) const; + short GetDiscreteFrondLodLevel(float fLodLevel = -1.0f) const; + + unsigned short GetNumBranchLodLevels(void) const; + unsigned short GetNumLeafLodLevels(void) const; + unsigned short GetNumFrondLodLevels(void) const; + + + /////////////////////////////////////////////////////////////////////// + // Geometry + + void DeleteBranchGeometry(void); + void DeleteFrondGeometry(void); + unsigned char* GetFrondGeometryMapIndexes(int nLodLevel) const; + const float* GetLeafBillboardTable(unsigned int& nEntryCount) const; + const float* GetLeafLodSizeAdjustments(void); + void GetGeometry(SGeometry& sGeometry, unsigned long ulBitVector = SpeedTree_AllGeometry, short sOverrideBranchLodValue = -1, short sOverrideFrondLodValue = -1, short sOverrideLeafLodValue = -1); + + + /////////////////////////////////////////////////////////////////////// + // Textures + + void GetTextures(STextures& sTextures) const; + void SetLeafTextureCoords(unsigned int nLeafMapIndex, const float* pTexCoords); + void SetFrondTextureCoords(unsigned int nFrondMapIndex, const float* pTexCoords); +static bool GetTextureFlip(void); +static void SetTextureFlip(bool bFlag); + void SetBranchTextureFilename(const char* pFilename); + void SetLeafTextureFilename(unsigned int nLeafMapIndex, const char* pFilename); + void SetFrondTextureFilename(unsigned int nFrondMapIndex, const char* pFilename); + + + /////////////////////////////////////////////////////////////////////// + // Statistics & information + +static void Authorize(const char* pKey); +static bool IsAuthorized(void); +static const char* GetCurrentError(void); +static void ResetError(void); + + void GetBoundingBox(float* pBounds) const; + unsigned int GetLeafTriangleCount(float fLodLevel = -1.0f) const; + unsigned int GetBranchTriangleCount(float fLodLevel = -1.0f) const; + unsigned int GetFrondTriangleCount(float fLodLevel = -1.0f) const; + + + /////////////////////////////////////////////////////////////////////// + // Collision objects + + unsigned int GetCollisionObjectCount(void); + void GetCollisionObject(unsigned int nIndex, ECollisionObjectType& eType, float* pPosition, float* pDimensions); + + + /////////////////////////////////////////////////////////////////////// + // User Data + + const char* GetUserData(void) const; + +private: + // PORT: the SDK's private engine members (m_pEngine .. m_afHorizontalCoords) are opaque here. + struct SImpl; + SImpl* m_pImpl; + CSpeedTreeRT(const CSpeedTreeRT&) = delete; + CSpeedTreeRT& operator=(const CSpeedTreeRT&) = delete; }; + + +// Macintosh-specific +#ifdef __ppc__ +#pragma export off +#endif diff --git a/extension/src/port/common/shim/sdk/d3dx8.h b/extension/src/port/common/shim/sdk/d3dx8.h index fb1008e3..d6ed3222 100644 --- a/extension/src/port/common/shim/sdk/d3dx8.h +++ b/extension/src/port/common/shim/sdk/d3dx8.h @@ -3,6 +3,27 @@ // opaque, owned by platform/. #include "d3d8.h" +// d3dx8core.h ID3DXBuffer / D3DXAssembleShader. There is no D3D8 shader assembler here: +// D3DXAssembleShader fails with no code and no error buffer. +struct ID3DXBuffer +{ + virtual ~ID3DXBuffer() = default; + virtual ULONG Release() = 0; + virtual void* GetBufferPointer() = 0; + virtual DWORD GetBufferSize() = 0; +}; +typedef ID3DXBuffer* LPD3DXBUFFER; +inline HRESULT D3DXAssembleShader(const void*, UINT, DWORD, LPD3DXBUFFER* ppConstants, LPD3DXBUFFER* ppCompiledShader, LPD3DXBUFFER* ppCompilationErrors) +{ + if (ppConstants) + *ppConstants = nullptr; + if (ppCompiledShader) + *ppCompiledShader = nullptr; + if (ppCompilationErrors) + *ppCompilationErrors = nullptr; + return E_FAIL; +} + struct ID3DXMesh; typedef ID3DXMesh* LPD3DXMESH; struct ID3DXMatrixStack;