SpeedTreeLib: port 40250 wrapper/forest verbatim over a CSpeedTreeRT stand-in
Delete the platform proxy tree renderer and copy SpeedTreeWrapper, SpeedTreeForest, SpeedTreeForestDirectX8, SpeedTreeConfig.h and VertexShaders.h from 40250. The SDK SpeedTreeRT.h public API becomes the shim; the closed CSpeedTreeRT is reimplemented in platform/SpeedTreeLib/SpeedTreeRT.cpp (procedural geometry, static lighting, single LOD, DIVERGENT). Shims gain the D3D8 vertex shader declaration tokens, D3D_OK, CreateVertexShader/DeleteVertexShader, ID3DXBuffer/D3DXAssembleShader, MessageBox and __min/__max. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
86e602cb32
commit
d817bd7cab
@@ -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 <SpeedTreeRT.h>
|
||||
#include <spt.h>
|
||||
|
||||
#include "../EterLib/RenderCommands3D.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<char>(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<const char *> 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<UVRect> 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 <len> "__IdvSpt_02_", 1002, 2000 <len> <branch texture>, 2001 <float size>,
|
||||
// 2002 <float variance>.
|
||||
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<unsigned long>(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<float> vBranchCoords, vBranchNormals, vBranchTex0, vBranchTex1;
|
||||
std::vector<unsigned long> vBranchColors;
|
||||
std::vector<unsigned short> 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<SCluster> vClusters;
|
||||
std::vector<float> vLeafCenters, vLeafNormals, vLeafTexCoords, vLeafCoords;
|
||||
std::vector<unsigned long> vLeafColors;
|
||||
std::vector<unsigned char> vLeafMapIndices;
|
||||
std::vector<const float *> 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<const char *>(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<unsigned short> & 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<unsigned short> 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<unsigned short>(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<UVRect> 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<unsigned short>(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<STreeData> pTree = std::make_shared<STreeData>();
|
||||
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<STreeData>();
|
||||
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<unsigned short>(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<unsigned short>(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user