#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 { }