1098 lines
31 KiB
C++
1098 lines
31 KiB
C++
#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 <spt.h>
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<char>(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<UVRect> 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<TreeVertex> verts;
|
|
std::vector<uint16_t> 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<uint16_t>(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<UVRect> & 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<float *>(&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<const char *>(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<const char *>(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<unsigned int>(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<unsigned short>(wood.indices.size());
|
|
m_pGeometryCache->branch_index_count = static_cast<uint32_t>(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<UVRect> 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<uint32_t>(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<CGraphicImageInstance &>(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<CGraphicImageInstance &>(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<unsigned int>(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<CGraphicImageInstance &>(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<CGraphicImage *>(pResource));
|
|
return !rImage.IsEmpty();
|
|
}
|
|
|
|
auto CSpeedTreeWrapper::SetShaderConstants(const float * /*pMaterial*/) const -> void
|
|
{
|
|
}
|