Files
2026-06-01 12:46:52 +02:00

981 lines
31 KiB
C++

///////////////////////////////////////////////////////////////////////
// Name: TreeEngine.cpp
//
// *** INTERACTIVE DATA VISUALIZATION (IDV) PROPRIETARY INFORMATION ***
//
// Copyright (c) 2001-2004 IDV, Inc.
// All Rights Reserved.
//
// IDV, Inc.
// 1233 Washington St. Suite 610
// Columbia, SC 29201
// Voice: (803) 799-1699
// Fax: (803) 931-0320
// Web: http://www.idvinc.com
//
// This software is supplied under the terms of a license agreement or
// nondisclosure agreement with Interactive Data Visualization and may not
// be copied or disclosed except in accordance with the terms of that
// agreement.
#ifdef WIN32
#pragma warning(disable : 4786)
#endif
#include "Debug.h"
#include "TreeEngine.h"
#include "BillboardLeaf.h"
#include "WindEngine.h"
#include "LeafLod.h"
#include "IndexedGeometry.h"
#include <cfloat>
const char* g_pszHeader = "__IdvSpt_02_";
///////////////////////////////////////////////////////////////////////
// CTreeEngine::CTreeEngine definition
CTreeEngine::CTreeEngine(CIndexedGeometry* pBranchGeometry) :
m_fBillboardSize(1.0f),
m_pBranch(NULL),
m_fLod(1.0f),
m_fOverrideTreeSize(-1.0f),
m_fOverrideTreeVariance(0.0f),
m_nBranchesWeightLevel(1),
m_nNumBranchLodLevels(6),
m_pBranchGeometry(NULL),
m_fMinBranchVolumePercent(0.5f),
m_fMaxBranchVolumePercent(1.0f),
m_vLeafLods(NULL),
m_bParsedLeafLods(false),
m_bTransientDataIntact(true),
m_fLeafReductionPercentage(0.3f),
m_fBranchReductionFuzziness(0.0f),
m_fLargeBranchPercent(0.05f)
{
m_pBranchGeometry = pBranchGeometry;
SetWind(0.25f);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::Compute definition
void CTreeEngine::Compute(float fLeafSizeIncreaseFactor)
{
CIdvBranch::SetComputeLeaves(!m_bParsedLeafLods);
// allocate needed memory
m_pBranch = new CIdvBranch(NULL);
if (!m_bParsedLeafLods)
m_vLeafLods = new vector<CBillboardLeaf*>[m_sLeafInfo.m_nNumLeafLodLevels];
// setup branches for later computing
CIdvBranch::SetBranchLevelForWeighting(m_nBranchesWeightLevel);
CIdvBranch::ClearBranchInfo( );
CIdvBranch::SetLeafData(&m_sLeafInfo);
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
CIdvBranch::AddBranchInfo(m_vBranchInfo[i]);
// determine tree size using tree's seed
m_cRandom.Reseed(m_sTreeInfo.m_nSeed);
float fTreeSize = m_cRandom.GetUniform(m_sTreeInfo.m_fSize - m_sTreeInfo.m_fSizeVariance,
m_sTreeInfo.m_fSize + m_sTreeInfo.m_fSizeVariance);
srand(m_sTreeInfo.m_nFlareSeed);
// adjust relative leaf sizes
if (m_sTreeInfo.m_fSize > 0.0f)
for (unsigned int j = 0; j < m_sLeafInfo.m_vLeafTextures.size( ); ++j)
{
SIdvLeafTexture& sLeaf = m_sLeafInfo.m_vLeafTextures[j];
sLeaf.m_cSizeUsed[0] = m_sTreeInfo.m_fSize * sLeaf.m_cSize[0];
sLeaf.m_cSizeUsed[1] = m_sTreeInfo.m_fSize * sLeaf.m_cSize[1];
}
// compute the branches and leaves
m_cRandom.Reseed(m_sTreeInfo.m_nSeed);
m_pBranchGeometry->SetNumLodLevels(static_cast<unsigned short>(GetNumBranchLodLevels( )));
m_pBranchGeometry->ResetStripCounter(0);
m_pBranch->SetRoundRobinWindLevel(m_sTreeInfo.m_nSeed);
m_pBranch->Compute(m_sTreeInfo.m_nSeed,
fTreeSize,
0, // starting level, 0 = trunk
GetPosition( ), // grow tree from this position
0.0f, // percent of parent (0.0 since there's no parent for trunk)
CRotTransform( ), // identity rotation matrix
CVec3(0.0f, 0.0f, 1.0f), // perturbed z-axis (starts as straight z)
m_pBranchGeometry,
m_vBillboardLeaves,
1.0f, // wind weight
m_sTreeInfo.m_nSeed, // wind group
-1.0f); // parent radius (no parent, so -1)
// build the discrete branch levels-of-detail
BuildBranchLods( );
// if the leaf LODs were not already read from the file, build them here
if (!m_bParsedLeafLods)
BuildLeafLods(fLeafSizeIncreaseFactor);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::Clone definition
void CTreeEngine::Clone(CTreeEngine* pClone, const CVec3& cPos, unsigned int nSeed) const
{
// copy the branchinfo vector members
pClone->m_vBranchInfo.clear( );
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
{
//lint -esym(429,pInfo) { pInfo is not freed or returned from Clone(), but it
//is stored in pClone->m_vBranchInfo for later deletion }
SIdvBranchInfo* pInfo = new SIdvBranchInfo;
*pInfo = *m_vBranchInfo[i];
pClone->m_vBranchInfo.push_back(pInfo);
}
// copy the rest of the important member variables
pClone->SetPosition(cPos);
pClone->m_nBranchesWeightLevel = m_nBranchesWeightLevel;
pClone->m_sTreeInfo = m_sTreeInfo;
pClone->m_sLeafInfo = m_sLeafInfo;
pClone->m_sLeafInfo.m_pLeafTexCoords = NULL;
pClone->m_sLeafInfo.m_pLeafVertexes = NULL;
pClone->m_sLeafInfo.m_pTimeOffsets = NULL;
pClone->m_sWindInfo = m_sWindInfo;
pClone->SetBranchTexture(m_sTreeInfo.m_strBranchTextureFilename);
pClone->SetLeafTextures(GetLeafTextures( ));
pClone->m_fMinBranchVolumePercent = m_fMinBranchVolumePercent;
pClone->m_fMaxBranchVolumePercent = m_fMaxBranchVolumePercent;
// compute this new cloned tree based on the parameters set above
pClone->SetSeed(nSeed);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::OverrideTreeSize definition
void CTreeEngine::OverrideTreeSize(float fNewSize, float fNewVariance)
{
m_fOverrideTreeSize = fNewSize;
m_fOverrideTreeVariance = fNewVariance;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::Parse definition
bool CTreeEngine::Parse(CTreeFileAccess& cFile)
{
bool bSuccess = false;
int nToken = cFile.ParseToken( );
if (nToken != File_BeginFile)
throw(IdvFileError("missing begin_file token"));
string strHeader = cFile.ParseString( );
if (strHeader != string(g_pszHeader))
throw(IdvFileError("not a valid SpeedTree SPT file"));
nToken = cFile.ParseToken( );
do
{
switch (nToken)
{
case File_BeginTreeInfo:
ParseTreeInfo(cFile);
break;
case File_BeginGeneralLeafInfo:
m_sLeafInfo.Parse(cFile);
break;
case File_BeginWindInfo:
m_sWindInfo.Parse(cFile);
break;
default:
throw(IdvFileError("malformed SpeedTree SPT file"));
}
nToken = cFile.ParseToken( );
} while (nToken != File_EndFile);
if (!cFile.EndOfFile( ) && cFile.PeekToken( ) == File_BeginLeafCluster)
{
(void) cFile.ParseToken( );
ParseLeafCluster(cFile);
}
SetBranchTexture(m_sTreeInfo.m_strBranchTextureFilename);
SetLeafTextures(GetLeafTextures( ));
if (m_fOverrideTreeSize > -1.0f)
SetSize(m_fOverrideTreeSize, m_fOverrideTreeVariance);
bSuccess = true;
return bSuccess;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::Save definition
//
// Saves a binary description of the tree in the unsigned char memory block it
// returns. If this block is saved directly to a binary file, SpeedTreeCAD will
// read it. The block can also be embedded in a client-defined structure or
// memory block.
void CTreeEngine::Save(CTreeFileAccess& cFile, bool bSaveLeafLods) const
{
cFile.SaveToken(File_BeginFile);
// magic id header
cFile.SaveString(g_pszHeader);
// general tree info
cFile.SaveToken(File_BeginTreeInfo);
cFile.SaveToken(File_Tree_BranchTexture);
cFile.SaveString(m_sTreeInfo.m_strBranchTextureFilename);
cFile.SaveToken(File_Tree_Far);
cFile.SaveFloat(m_sTreeInfo.m_fFar);
cFile.SaveToken(File_Tree_LOD);
cFile.SaveBool(false); // m_bLOD is no longer used
cFile.SaveToken(File_Tree_Near);
cFile.SaveFloat(m_sTreeInfo.m_fNear);
cFile.SaveToken(File_Tree_RandomSeed);
cFile.SaveInt(m_sTreeInfo.m_nSeed);
cFile.SaveToken(File_Tree_Size);
cFile.SaveFloat(m_sTreeInfo.m_fSize);
cFile.SaveToken(File_Tree_RenderStyle); // no longer used
cFile.SaveInt(0);
cFile.SaveToken(File_Tree_Size_Variance);
cFile.SaveFloat(m_sTreeInfo.m_fSizeVariance);
SaveBranchInfo(cFile);
cFile.SaveToken(File_EndTreeInfo);
// leaf info
m_sLeafInfo.Save(cFile);
// wind info
m_sWindInfo.Save(cFile);
cFile.SaveToken(File_EndFile);
// leaf lod data
if (bSaveLeafLods)
SaveLeafCluster(cFile);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::FreeTransientData definition
//
// This function deletes memory that will not be needed once the tree is
// loaded and ready to render.
void CTreeEngine::FreeTransientData(void)
{
if (TransientDataIntact( ))
{
// will recursively delete all branches in the tree
delete m_pBranch;
m_pBranch = NULL;
// delete all of the discrete LOD leaves
for (int i = 0; i < m_sLeafInfo.m_nNumLeafLodLevels; ++i)
{
if (m_vLeafLods)
{
for (unsigned int j = 0; j < m_vLeafLods[i].size( ); ++j)
{
delete m_vLeafLods[i][j];
m_vLeafLods[i][j] = NULL;
}
m_vLeafLods[i].clear( );
}
}
delete[] m_vLeafLods;
m_vLeafLods = NULL;
// delete all associated branch info structures
for (unsigned int j = 0; j < m_vBranchInfo.size( ); ++j)
{
delete m_vBranchInfo[j];
m_vBranchInfo[j] = NULL;
}
m_vBranchInfo.clear( );
// make sure than functions that need this data know it's gone
m_bTransientDataIntact = false;
}
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetWind definition
void CTreeEngine::SetWind(float fStrength)
{
m_sWindInfo.m_fStrength = fStrength;
float fLeafFrequency = c_fStrengthToFrequencyRatio * fStrength * m_sWindInfo.m_cLeafFactors[1];
float fLeafThrow = c_fStrengthToThrowRatio * fStrength * m_sWindInfo.m_cLeafFactors[0];
m_sWindInfo.m_cLeafOscillation.Set(-fLeafThrow, fLeafThrow, fLeafFrequency);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::GetSize definition
void CTreeEngine::GetSize(float& fSize, float& fVariance)
{
fSize = m_sTreeInfo.m_fSize;
fVariance = m_sTreeInfo.m_fSizeVariance;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetSize definition
void CTreeEngine::SetSize(float fSize, float fVariance)
{
m_sTreeInfo.m_fSize = fSize;
m_sTreeInfo.m_fSizeVariance = fVariance;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ComputeLod definition
float CTreeEngine::ComputeLod(void)
{
float fDistance = m_cCameraPos.Distance(m_cPosition);
m_fLod = 1.0f - (fDistance - m_sTreeInfo.m_fNear) / (m_sTreeInfo.m_fFar - m_sTreeInfo.m_fNear);
if (m_fLod > 1.0f)
m_fLod = 1.0f;
else if (m_fLod < 0.0f)
m_fLod = 0.0f;
return m_fLod;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::GetLodLimits definition
void CTreeEngine::GetLodLimits(float& fNear, float& fFar) const
{
fNear = m_sTreeInfo.m_fNear;
fFar = m_sTreeInfo.m_fFar;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetLodLimits definition
void CTreeEngine::SetLodLimits(float fNear, float fFar)
{
m_sTreeInfo.m_fNear = fNear;
m_sTreeInfo.m_fFar = fFar;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseTreeInfo definition
void CTreeEngine::ParseTreeInfo(CTreeFileAccess& cFile)
{
int nToken = cFile.ParseToken( );
do
{
switch (nToken)
{
case File_Tree_BranchTexture:
m_sTreeInfo.m_strBranchTextureFilename = cFile.ParseString( );
m_sTreeInfo.m_strBranchTextureFilename = m_sTreeInfo.m_strBranchTextureFilename.NoPath( );
break;
case File_Tree_RandomSeed:
{
int nSeed = cFile.ParseInt( );
SetSeed(static_cast<unsigned int>(nSeed));
}
break;
case File_Tree_Near:
m_sTreeInfo.m_fNear = cFile.ParseFloat( );
break;
case File_Tree_Far:
m_sTreeInfo.m_fFar = cFile.ParseFloat( );
break;
case File_Tree_RenderStyle:
(void) cFile.ParseInt( );
break;
case File_Tree_LOD:
(void) cFile.ParseBool( ); // m_bLod is no longer used
break;
case File_Tree_Size:
m_sTreeInfo.m_fSize = cFile.ParseFloat( );
break;
case File_Tree_Size_Variance:
m_sTreeInfo.m_fSizeVariance = cFile.ParseFloat( );
break;
case File_BeginBranchInfo:
ParseBranchInfo(cFile);
break;
default:
throw(IdvFileError("malformed general tree information"));
}
nToken = cFile.ParseToken( );
} while (nToken != File_EndTreeInfo);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetSeed definition
//
// Seed values:
//
// 0 = pick a random seed
// 1 = use existing internal seed (from m_sTreeInfo.m_nSeed)
// >1 = use nSeed parameter as seed value
void CTreeEngine::SetSeed(unsigned int nSeed)
{
if (nSeed == 0)
{
// pick a random seed
const int c_nMaxSeed = 1000000;
m_cRandom.Reseed( );
m_sTreeInfo.m_nSeed = static_cast<int>(m_cRandom.GetUniform(2.0f, static_cast<float>(c_nMaxSeed)));
}
else if (nSeed == 1)
{
// do nothing
}
else
{
// use parameter as new seed
m_sTreeInfo.m_nSeed = nSeed;
}
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::BuildBranchLods definition
//
// BuildBranchLods() will start to remove branches from the tree in order
// of their relative volume. Smaller branches go first because they have
// less of a visual impact. Larger branches remain because they define
// the bulk of the tree.
void CTreeEngine::BuildBranchLods(void)
{
// build a linear vector of branch tree
vector<CIdvBranch*> vAllBranches;
if (m_pBranch &&
m_pBranchGeometry->GetVertexCount( ) > 0)
m_pBranch->BuildBranchVector(vAllBranches);
// compute total volume of all branches
float fTotalVolume = 0.0f;
float fLargestVolume = 0.0f;
unsigned int i;
for (i = 0; i < vAllBranches.size( ); ++i)
{
float fVolume = vAllBranches[i]->GetVolume( );
fTotalVolume += fVolume;
if (fVolume > fLargestVolume)
fLargestVolume = fVolume;
}
// implement fuzziness
CIdvRandom cRandom;
float fSavePercent = 1.0f - m_fLargeBranchPercent;
vector<CIdvBranch*> vSavedBranches;
for (i = 0; i < vAllBranches.size( ); ++i)
{
float fVolume = vAllBranches[i]->GetVolume( );
if (fVolume > fLargestVolume * fSavePercent)
{
vSavedBranches.push_back(vAllBranches[i]);
(void) vAllBranches.erase(vAllBranches.begin( ) + i--);
}
else
{
// compute a new, fuzzy volume
float fFuzziness = cRandom.GetUniform(0.0f, m_fBranchReductionFuzziness), fOneMinusFuzziness = 1.0f - fFuzziness;
fVolume = (fOneMinusFuzziness * fVolume) + (fFuzziness * fLargestVolume);
vAllBranches[i]->SetFuzzyVolume(fVolume);
}
}
// sort branches in descending order of volume and add insert save branches at the beginning
CIdvBranch::SortBranchVector(vAllBranches);
CIdvBranch::SortBranchVector(vSavedBranches);
for (i = 0; i < vSavedBranches.size( ); ++i)
(void) vAllBranches.insert(vAllBranches.begin( ), vSavedBranches[i]);
// for each LOD level, compute a target volume and use enough
// branches to reach that target
int nNumBranchLodLevels = GetNumBranchLodLevels( );
for (i = 0; i < (unsigned int) nNumBranchLodLevels; ++i)
{
if (i == 0)
m_pBranchGeometry->DeleteLodStrip(0);
// figure what part of the branch structure stays for lod level i, computed by volume
float fPercent = (nNumBranchLodLevels < 2) ? 1.0f : VecInterpolate(m_fMaxBranchVolumePercent, m_fMinBranchVolumePercent, i / float(nNumBranchLodLevels - 1));
float fTargetVolume = fPercent * fTotalVolume;
// which branches will contribute to lod level i
int nBranchCount = 0;
float fLodVolume = 0.0f;
for (unsigned int j = 0; j < vAllBranches.size( ) && fLodVolume < fTargetVolume; ++j)
{
fLodVolume += vAllBranches[j]->GetVolume( );
nBranchCount++;
}
// setup stripping container
m_pBranchGeometry->ResetStripCounter(static_cast<unsigned short>(i));
// compute the indexes for each branch in this lod
for (int k = 0; k < nBranchCount; ++k)
vAllBranches[k]->ComputeLod(i, m_pBranchGeometry);
}
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseBranchInfo definition
void CTreeEngine::ParseBranchInfo(CTreeFileAccess& cFile)
{
ClearBranchInfo( );
int nSize = cFile.ParseInt( );
for (int i = 0; i < nSize; ++i)
{
// Parse allocates a new SIdvBranchInfo structure
SIdvBranchInfo* pInfo = SIdvBranchInfo::Parse(cFile);
m_vBranchInfo.push_back(pInfo);
}
int nToken = cFile.ParseToken( );
if (nToken != File_EndBranchInfo)
throw(IdvFileError("malformed branch data"));
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveBranchInfo definition
void CTreeEngine::SaveBranchInfo(CTreeFileAccess& cFile) const
{
cFile.SaveToken(File_BeginBranchInfo);
int nSize = m_vBranchInfo.size( );
cFile.SaveInt(nSize);
for (int i = 0; i < nSize; ++i)
{
cFile.SaveToken(File_BeginBranchLevel);
m_vBranchInfo[i]->Save(cFile);
cFile.SaveToken(File_EndBranchLevel);
}
cFile.SaveToken(File_EndBranchInfo);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::BuildLeafLods definition
//
// Wrapper invocation of CLeafLodEngine - see LeafLod.h & .cpp for details
void CTreeEngine::BuildLeafLods(float fLeafSizeIncreaseFactor)
{
// find the largest leaf used in the tree
float fLargestLeaf = -1.0f;
for (unsigned int i = 0; i < m_sLeafInfo.m_vLeafTextures.size( ); ++i)
{
const SIdvLeafTexture& sTexture = m_sLeafInfo.m_vLeafTextures[i];
float fLargestSide = sTexture.m_cSizeUsed[0] > sTexture.m_cSizeUsed[1] ? sTexture.m_cSizeUsed[0] : sTexture.m_cSizeUsed[1];
if (fLargestSide * m_sLeafInfo.m_fSpacingTolerance > fLargestLeaf)
fLargestLeaf = fLargestSide * m_sLeafInfo.m_fSpacingTolerance;
}
// if no leaves, use an arbitrary value
if (fLargestLeaf == -1.0f)
fLargestLeaf = 10.0f;
// m_vBillboardLeaves contains all of the generated leaves, it will be the highest LOD
if (m_vLeafLods)
{
m_vLeafLods[0] = m_vBillboardLeaves;
for (int j = 1; j < m_sLeafInfo.m_nNumLeafLodLevels; ++j)
{
float fSizeMultiplier = 1.0f + fLeafSizeIncreaseFactor * float(j);
CLeafLodEngine cLod(m_sLeafInfo, fLargestLeaf * 10.0f, m_fLeafReductionPercentage, fSizeMultiplier);
m_vLeafLods[j] = cLod.ComputeNextLevel(m_vLeafLods[j - 1]);
}
}
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseLeafCluster definition
//
// SPT files may or may not have all of the leaf LODs stored - this function
// will retrieve the leaves is they're there.
void CTreeEngine::ParseLeafCluster(CTreeFileAccess& cFile)
{
int nLeafLod = 0;
int nNumLeafLodLevels = cFile.ParseInt( );
m_sLeafInfo.m_nNumLeafLodLevels = nNumLeafLodLevels;
delete[] m_vLeafLods;
m_vLeafLods = new vector<CBillboardLeaf*>[m_sLeafInfo.m_nNumLeafLodLevels];
int nToken = cFile.ParseToken( );
while (nToken != File_EndLeafCluster)
{
if (nLeafLod >= m_sLeafInfo.m_nNumLeafLodLevels)
throw(IdvFileError("too many leaf lod levels"));
if (nToken != File_BeginLeafLodLevel)
throw(IdvFileError("malformed leaf lod data"));
nToken = cFile.ParseToken( );
while (nToken != File_EndLeafLodLevel)
{
if (nToken != File_BeginBillboardLeaf)
throw(IdvFileError("malformed leaf lod data"));
m_vLeafLods[nLeafLod].push_back(CBillboardLeaf::Parse(cFile));
nToken = cFile.ParseToken( );
}
++nLeafLod;
nToken = cFile.ParseToken( );
}
m_bParsedLeafLods = true;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveLeafCluster
//
// SPT files may or may not have all of the leaf LODs stored - this function
// will save the leaves LODs if invoked.
void CTreeEngine::SaveLeafCluster(CTreeFileAccess& cFile) const
{
if (m_vLeafLods)
{
cFile.SaveToken(File_BeginLeafCluster);
cFile.SaveInt(m_sLeafInfo.m_nNumLeafLodLevels);
for (int i = 0; i < m_sLeafInfo.m_nNumLeafLodLevels; ++i)
{
cFile.SaveToken(File_BeginLeafLodLevel);
for (unsigned int j = 0; j < m_vLeafLods[i].size( ); ++j)
m_vLeafLods[i][j]->Save(cFile);
cFile.SaveToken(File_EndLeafLodLevel);
}
cFile.SaveToken(File_EndLeafCluster);
}
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveLodInfo
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::SaveLodInfo(CTreeFileAccess& cFile) const
{
cFile.SaveToken(File_BeginEngineLodInfo);
cFile.SaveToken(File_BranchNumLods);
cFile.SaveInt(m_nNumBranchLodLevels);
cFile.SaveToken(File_BranchMinVolumePercent);
cFile.SaveFloat(m_fMinBranchVolumePercent);
cFile.SaveToken(File_LeafReductionFactor);
cFile.SaveFloat(m_fLeafReductionPercentage);
cFile.SaveToken(File_LeafNumLods);
cFile.SaveInt(m_sLeafInfo.m_nNumLeafLodLevels);
cFile.SaveToken(File_BranchMaxVolumePercent);
cFile.SaveFloat(m_fMaxBranchVolumePercent);
cFile.SaveToken(File_BranchReductionFuzziness);
cFile.SaveFloat(m_fBranchReductionFuzziness);
cFile.SaveToken(File_LargeBranchPercent);
cFile.SaveFloat(m_fLargeBranchPercent);
cFile.SaveToken(File_EndEngineLodInfo);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseLodInfo
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::ParseLodInfo(CTreeFileAccess& cFile)
{
int nToken = cFile.ParseToken( );
do
{
switch (nToken)
{
case File_BranchNumLods:
m_nNumBranchLodLevels = cFile.ParseInt( );
break;
case File_BranchMinVolumePercent:
m_fMinBranchVolumePercent = cFile.ParseFloat( );
break;
case File_LeafReductionFactor:
m_fLeafReductionPercentage = cFile.ParseFloat( );
break;
case File_LeafNumLods:
m_sLeafInfo.m_nNumLeafLodLevels = cFile.ParseInt( );
break;
case File_BranchMaxVolumePercent:
m_fMaxBranchVolumePercent = cFile.ParseFloat( );
break;
case File_BranchReductionFuzziness:
m_fBranchReductionFuzziness = cFile.ParseFloat( );
break;
case File_LargeBranchPercent:
m_fLargeBranchPercent = cFile.ParseFloat( );
break;
default:
throw(IdvFileError("malformed engine lod data"));
}
nToken = cFile.ParseToken( );
} while (nToken != File_EndEngineLodInfo);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetBranchTextureFilename
void CTreeEngine::SetBranchTextureFilename(const char* pFilename)
{
m_sTreeInfo.m_strBranchTextureFilename = pFilename;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SetLeafTextureFilename
void CTreeEngine::SetLeafTextureFilename(unsigned int nLeafMapIndex, const char* pFilename)
{
st_assert(nLeafMapIndex < m_sLeafInfo.m_vLeafTextures.size( ));
m_sLeafInfo.m_vLeafTextures[nLeafMapIndex].m_strFilename = pFilename;
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveTextureControls
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::SaveTextureControls(CTreeFileAccess& cFile) const
{
cFile.SaveToken(File_BeginTextureControls);
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
{
cFile.SaveToken(File_TextureOffset);
cFile.SaveBool(m_vBranchInfo[i]->m_bRandomTCoordOffset);
cFile.SaveToken(File_TextureTwist);
cFile.SaveFloat(m_vBranchInfo[i]->m_fTwist);
}
cFile.SaveToken(File_EndTextureControls);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseTextureControls
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::ParseTextureControls(CTreeFileAccess& cFile)
{
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
{
if (cFile.ParseToken( ) != File_TextureOffset)
throw(IdvFileError("malformed texture controls"));
m_vBranchInfo[i]->m_bRandomTCoordOffset = cFile.ParseBool( );
if (cFile.ParseToken( ) != File_TextureTwist)
throw(IdvFileError("malformed texture controls"));
m_vBranchInfo[i]->m_fTwist = cFile.ParseFloat( );
}
if (cFile.ParseToken( ) != File_EndTextureControls)
throw(IdvFileError("malformed texture controls"));
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveFlareInfo
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::SaveFlareInfo(CTreeFileAccess& cFile) const
{
cFile.SaveToken(File_BeginFlareInfo);
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
{
cFile.SaveToken(File_FlareSegmentPackingExponent);
cFile.SaveFloat(m_vBranchInfo[i]->m_fSegmentPackingExponent);
cFile.SaveToken(File_FlareNumFlares);
cFile.SaveInt(m_vBranchInfo[i]->m_nNumFlares);
cFile.SaveToken(File_FlareBalance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fFlareBalance);
cFile.SaveToken(File_FlareRadialInfluence);
cFile.SaveFloat(m_vBranchInfo[i]->m_fRadialInfluence);
cFile.SaveToken(File_FlareRadialInfluenceVariance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fRadialInfluenceVariance);
cFile.SaveToken(File_FlareRadialExponent);
cFile.SaveFloat(m_vBranchInfo[i]->m_fRadialExponent);
cFile.SaveToken(File_FlareRadialDistance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fRadialDistance);
cFile.SaveToken(File_FlareRadialDistanceVariance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fRadialVariance);
cFile.SaveToken(File_FlareLengthDistance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fLengthDistance);
cFile.SaveToken(File_FlareLengthDistanceVariance);
cFile.SaveFloat(m_vBranchInfo[i]->m_fLengthVariance);
cFile.SaveToken(File_LengthExponent);
cFile.SaveFloat(m_vBranchInfo[i]->m_fLengthExponent);
}
cFile.SaveToken(File_EndFlareInfo);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseFlareInfo
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::ParseFlareInfo(CTreeFileAccess& cFile)
{
for (unsigned int i = 0; i < m_vBranchInfo.size( ); ++i)
{
if (cFile.ParseToken( ) != File_FlareSegmentPackingExponent)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fSegmentPackingExponent = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareNumFlares)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_nNumFlares = cFile.ParseInt( );
if (cFile.ParseToken( ) != File_FlareBalance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fFlareBalance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareRadialInfluence)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fRadialInfluence = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareRadialInfluenceVariance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fRadialInfluenceVariance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareRadialExponent)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fRadialExponent = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareRadialDistance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fRadialDistance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareRadialDistanceVariance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fRadialVariance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareLengthDistance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fLengthDistance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_FlareLengthDistanceVariance)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fLengthVariance = cFile.ParseFloat( );
if (cFile.ParseToken( ) != File_LengthExponent)
throw(IdvFileError("malformed flare info"));
m_vBranchInfo[i]->m_fLengthExponent = cFile.ParseFloat( );
}
if (cFile.ParseToken( ) != File_EndFlareInfo)
throw(IdvFileError("malformed flare info"));
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::SaveFlareSeed
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::SaveFlareSeed(CTreeFileAccess& cFile) const
{
cFile.SaveToken(File_FlareSeed);
cFile.SaveInt(m_sTreeInfo.m_nFlareSeed);
}
///////////////////////////////////////////////////////////////////////
// CTreeEngine::ParseFlareSeed
//
// This data must be saved separately at the end of the file to
// maintain backward compatibility.
void CTreeEngine::ParseFlareSeed(CTreeFileAccess& cFile)
{
m_sTreeInfo.m_nFlareSeed = cFile.ParseInt( );
}