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

3640 lines
120 KiB
C++

///////////////////////////////////////////////////////////////////////
// Name: SpeedTreeRT.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.
#include "Debug.h"
#include "LibGlobals_Source/IdvGlobals.h"
#include "SpeedTreeRT.h"
#include "IndexedGeometry.h"
#include "Instances.h"
#include "TreeEngine.h"
#include "WindEngine.h"
#include "SimpleBillboard.h"
#include "LightingEngine.h"
#include "LeafGeometry.h"
#include "FrondEngine.h"
#include "UpVector.h"
#include "ProjectedShadow.h"
#include <time.h>
#include <algorithm>
#include "FileAccess.h"
#define FULL_VERSION
#include "EvalCode.h"
#include <fstream>
#include "SFileNameEnDe.h"
#include <string>
#include "../../../../Internal/include/kfile/KStream.h"
using namespace std;
// static/global variables
vector<CSpeedTreeRT*> SInstanceList::m_vUniqueTrees;
bool CSpeedTreeRT::m_bDropToBillboard = false;
bool CSpeedTreeRT::m_bTextureFlip = false;
unsigned int CSpeedTreeRT::m_uiAllRefCount = 0;
static string g_strError;
static string g_strKey;
#define _NO_THREAD_ //쓰레드 로딩 사용 안 할때
#ifndef _NO_THREAD_
static XCriticalSection s_TreeLoadingLock;
#endif
// enumerations
enum ESpeedTreeInternalEvent
{
STIE_CLIENT_CHANGED_WIND,
STIE_CLIENT_CHANGED_CAMERA,
STIE_UNKNOWN
};
enum ESpeedTreeSizes
{
STS_MIN_BOX = 0, // (x, y, z) min of orthographic bounding box
STS_MAX_BOX = 3, // (x, y, z) max of orthographic bounding box
STS_BB_SIZE = 6, // width of simple billboard (last LOD)
STS_COUNT = 7
};
///////////////////////////////////////////////////////////////////////
// SEmbeddedTexCoords definition
//
// Used to store leaf map texture coordinates from a composite leaf map
// as well as billboard texture coordinates.
struct SEmbeddedTexCoords
{
SEmbeddedTexCoords( ) :
m_nNumLeafMaps(0),
m_pLeafTexCoords(NULL),
m_nNumBillboards(0),
m_pBillboardTexCoords(NULL),
m_nNumFrondMaps(0),
m_pFrondTexCoords(NULL)
{
m_afShadowTexCoords[0] = 1.0f;
m_afShadowTexCoords[1] = 1.0f;
m_afShadowTexCoords[2] = 0.0f;
m_afShadowTexCoords[3] = 1.0f;
m_afShadowTexCoords[4] = 0.0f;
m_afShadowTexCoords[5] = 0.0f;
m_afShadowTexCoords[6] = 1.0f;
m_afShadowTexCoords[7] = 0.0f;
}
~SEmbeddedTexCoords( )
{
delete[] m_pLeafTexCoords;
m_pLeafTexCoords = NULL;
delete[] m_pBillboardTexCoords;
m_pBillboardTexCoords = NULL;
delete[] m_pFrondTexCoords;
m_pFrondTexCoords = NULL;
}
const SEmbeddedTexCoords& operator=(const SEmbeddedTexCoords& sRight)
{
if (&sRight != this)
{
// copy leaf texture coordinates
if (sRight.m_pLeafTexCoords)
{
m_nNumLeafMaps = sRight.m_nNumLeafMaps;
m_pLeafTexCoords = new float[m_nNumLeafMaps * 8];
memcpy(m_pLeafTexCoords, sRight.m_pLeafTexCoords, m_nNumLeafMaps * 8 * sizeof(float));
}
// copy billboard texture coordinates
if (sRight.m_pBillboardTexCoords)
{
m_nNumBillboards = sRight.m_nNumBillboards;
m_pBillboardTexCoords = new float[m_nNumBillboards * 8];
memcpy(m_pBillboardTexCoords, sRight.m_pBillboardTexCoords, m_nNumBillboards * 8 * sizeof(float));
}
// copy frond texture coordinates
if (sRight.m_pFrondTexCoords)
{
m_nNumFrondMaps = sRight.m_nNumFrondMaps;
m_pFrondTexCoords = new float[m_nNumFrondMaps * 8];
memcpy(m_pFrondTexCoords, sRight.m_pFrondTexCoords, m_nNumFrondMaps * 8 * sizeof(float));
}
m_strCompositeFilename = sRight.m_strCompositeFilename;
memcpy(m_afShadowTexCoords, sRight.m_afShadowTexCoords, 8 * sizeof(float));
}
return *this;
}
int m_nNumLeafMaps; // number of leaf texture coordinate sets
float* m_pLeafTexCoords; // array of leaf texture coordinates
int m_nNumBillboards; // number of billboard texture coordinate sets
float* m_pBillboardTexCoords; // array of billboard texture coordinates
int m_nNumFrondMaps; // number of separate frond texture maps
float* m_pFrondTexCoords; // array of frond texture coordinates
string m_strCompositeFilename; // filename of composite map holding leaf, frond, and billboard textures
float m_afShadowTexCoords[8]; // self-shadow tex coords (may or may not index a composite map)
};
///////////////////////////////////////////////////////////////////////
// SShape definition
//
// Defines the shapes used for collision objects.
struct SShape
{
SShape( ) :
m_eType(CSpeedTreeRT::CO_SPHERE)
{
m_afPosition[0] = m_afPosition[1] = m_afPosition[2] = 0.0f;
m_afDimensions[0] = m_afDimensions[1] = m_afDimensions[2] = 0.0f;
}
CSpeedTreeRT::ECollisionObjectType m_eType; // CO_SPHERE, CO_CYLINDER, or CO_BOX
float m_afPosition[3]; // center of shape
float m_afDimensions[3]; // dimensions of shape (each type interprets differently)
};
///////////////////////////////////////////////////////////////////////
// SCollisionObjects definition
//
// A wrapper used to hide the STL vector type from the application.
struct SCollisionObjects
{
void TransformAll(CTransform& cTransform)
{
for (vector<SShape>::iterator i = m_vObjects.begin( ); i != m_vObjects.end( ); ++i)
{
// transform base point
CVec3 cPos(i->m_afPosition[0], i->m_afPosition[1], i->m_afPosition[2]);
cPos = cPos * cTransform;
i->m_afPosition[0] = cPos[0];
i->m_afPosition[1] = cPos[1];
i->m_afPosition[2] = cPos[2];
// transform the dimensions
if (i->m_eType == CSpeedTreeRT::CO_SPHERE)
{
// the sphere is always meant to be perfectly round,
// so we've chosen to scale the radius arbitrarily by
// the x-axis scale factor
i->m_afDimensions[0] *= cTransform.m_afData[0][0];
}
else if (i->m_eType == CSpeedTreeRT::CO_CYLINDER)
{
// the cylinder collision object was not meant to
// lie on anything but the up axis, so an arbitrary
// transform can wreak some havoc.
i->m_afDimensions[0] *= cTransform.m_afData[0][0];
i->m_afDimensions[1] *= cTransform.m_afData[0][0];
}
else if (i->m_eType == CSpeedTreeRT::CO_BOX)
{
i->m_afDimensions[0] *= cTransform.m_afData[0][0];
i->m_afDimensions[1] *= cTransform.m_afData[1][1];
i->m_afDimensions[2] *= cTransform.m_afData[2][2];
}
}
}
vector<SShape> m_vObjects; // collection of all collision objects in tree
};
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry constructor definition
CSpeedTreeRT::SGeometry::SGeometry( ) :
m_fBranchAlphaTestValue(-1.0f),
m_fFrondAlphaTestValue(-1.0f)
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry destructor definition
CSpeedTreeRT::SGeometry::~SGeometry( )
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SIndexed constructor definition
CSpeedTreeRT::SGeometry::SIndexed::SIndexed( ) :
m_usVertexCount(0),
m_nDiscreteLodLevel(-1),
m_pColors(NULL),
m_pNormals(NULL),
m_pBinormals(NULL),
m_pTangents(NULL),
m_pCoords(NULL),
m_pTexCoords0(NULL),
m_pTexCoords1(NULL),
m_pWindWeights(NULL),
m_pWindMatrixIndices(NULL),
m_usNumStrips(0),
m_pStripLengths(NULL),
m_pStrips(NULL)
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SIndexed destructor definition
CSpeedTreeRT::SGeometry::SIndexed::~SIndexed( )
{
// these tables are deleted in ~CIndexedGeometry( )
m_pColors = NULL;
m_pNormals = NULL;
m_pBinormals = NULL;
m_pTangents = NULL;
m_pCoords = NULL;
m_pTexCoords0 = NULL;
m_pTexCoords1 = NULL;
m_pWindWeights = NULL;
m_pWindMatrixIndices = NULL;
m_pStripLengths = NULL;
m_pStrips = NULL;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SLeaf constructor definition
CSpeedTreeRT::SGeometry::SLeaf::SLeaf( ) :
m_bIsActive(false),
m_fAlphaTestValue(-1.0f),
m_nDiscreteLodLevel(-1),
m_usLeafCount(0),
m_pLeafMapIndices(NULL),
m_pLeafClusterIndices(NULL),
m_pCenterCoords(NULL),
m_pColors(NULL),
m_pNormals(NULL),
m_pBinormals(NULL),
m_pTangents(NULL),
m_pLeafMapTexCoords(NULL),
m_pLeafMapCoords(NULL),
m_pWindWeights(NULL),
m_pWindMatrixIndices(NULL)
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SLeaf destructor definition
CSpeedTreeRT::SGeometry::SLeaf::~SLeaf( )
{
// this data is deleted in CLeafGeometry::SLodGeometry::~SLodGeometry( )
m_pLeafMapIndices = NULL;
m_pLeafClusterIndices = NULL;
m_pCenterCoords = NULL;
m_pColors = NULL;
m_pNormals = NULL;
m_pBinormals = NULL;
m_pTangents = NULL;
m_pLeafMapTexCoords = NULL;
m_pLeafMapCoords = NULL;
m_pWindWeights = NULL;
m_pWindMatrixIndices = NULL;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SBillboard constructor definition
CSpeedTreeRT::SGeometry::SBillboard::SBillboard( ) :
m_bIsActive(false),
m_pTexCoords(NULL),
m_pCoords(NULL),
m_fAlphaTestValue(-1.0f)
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SGeometry::SBillboard destructor definition
CSpeedTreeRT::SGeometry::SBillboard::~SBillboard( )
{
m_pTexCoords = NULL; // deleted in SEmbeddedTexCoords
m_pCoords = NULL; // deleted in CSimpleBillboard
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::STextures constructor definition
CSpeedTreeRT::STextures::STextures( ) :
m_pBranchTextureFilename(NULL),
m_uiLeafTextureCount(0),
m_pLeafTextureFilenames(NULL),
m_uiFrondTextureCount(0),
m_pFrondTextureFilenames(NULL),
m_pCompositeFilename(NULL),
m_pSelfShadowFilename(NULL)
{
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::STextures destructor definition
CSpeedTreeRT::STextures::~STextures( )
{
delete[] m_pLeafTextureFilenames;
m_pLeafTextureFilenames = NULL;
delete[] m_pFrondTextureFilenames;
m_pFrondTextureFilenames = NULL;
m_pBranchTextureFilename = NULL;
m_pCompositeFilename = NULL;
m_pSelfShadowFilename = NULL;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::CSpeedTreeRT definition
//
// This is the default constructor exposed to the application.
CSpeedTreeRT::CSpeedTreeRT( ) :
m_pBranchGeometry(NULL),
m_pEngine(NULL),
m_pLightingEngine(NULL),
m_pWindEngine(NULL),
m_pSimpleBillboard(NULL),
m_pLeafGeometry(NULL),
m_eLeafLodMethod(LOD_SMOOTH),
m_fLeafLodTransitionRadius(0.07f),
m_fLeafLodCurveExponent(0.7f),
m_fLeafTransitionFactor(0.0f),
m_pLeafLodSizeFactors(NULL),
m_pInstanceRefCount(NULL),
m_pInstanceData(NULL),
m_pTreeSizes(NULL),
m_ucTargetAlphaValue(0x54),
m_bTreeComputed(false),
m_fLeafSizeIncreaseFactor(0.5f),
m_pEmbeddedTexCoords(NULL),
m_pCollisionObjects(NULL),
m_pFrondEngine(NULL),
m_nBranchWindLevel(-1),
m_nFrondLevel(-1),
m_usNumFrondLodLevels(0),
m_pProjectedShadow(NULL),
m_pUserData(NULL),
m_bHorizontalBillboard(false),
m_b360Billboard(false)
{
#ifndef _NO_THREAD_
THREAD_SYNCRONIZE( s_TreeLoadingLock );
#endif
try
{
m_pWindEngine = new CWindEngine;
m_pBranchGeometry = new CIndexedGeometry(m_pWindEngine);
m_pEngine = new CTreeEngine(m_pBranchGeometry);
m_pLightingEngine = new CLightingEngine;
m_pLeafGeometry = new CLeafGeometry(m_pWindEngine);
m_pSimpleBillboard = new CSimpleBillboard;
m_pTreeSizes = new float[STS_COUNT];
m_pInstanceRefCount = new unsigned int;
*m_pInstanceRefCount = 1;
m_pInstanceList = new SInstanceList;
// fronds
m_pFrondEngine = new CFrondEngine;
m_pFrondGeometry = new CIndexedGeometry(m_pWindEngine, true);
// add to global tree list
SInstanceList::m_vUniqueTrees.push_back(this);
m_uiAllRefCount++;
CIdvBranch::SetLightingEngine(m_pLightingEngine);
// setup initial tree extents
m_pTreeSizes[STS_MIN_BOX + 0] = 0.0f;
m_pTreeSizes[STS_MIN_BOX + 1] = 0.0f;
m_pTreeSizes[STS_MIN_BOX + 2] = 0.0f;
m_pTreeSizes[STS_MAX_BOX + 0] = 1.0f;
m_pTreeSizes[STS_MAX_BOX + 1] = 1.0f;
m_pTreeSizes[STS_MAX_BOX + 2] = 1.0f;
m_pTreeSizes[STS_BB_SIZE] = 1.0f;
// zero out horizontal coords
for (int i = 0; i < 12; ++i)
m_afHorizontalCoords[i] = 0.0f;
}
SpeedTreeCatch("CSpeedTreeRT::CSpeedTreeRT( )")
SpeedTreeCatchAll("CSpeedTreeRT::CSpeedTreeRT( )");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::CSpeedTreeRT definition
//
// This is the constructor used for instancing that is hidden from
// the application.
CSpeedTreeRT::CSpeedTreeRT(const CSpeedTreeRT* pOrig) :
// general
m_pEngine(pOrig->m_pEngine),
m_pBranchGeometry(pOrig->m_pBranchGeometry),
m_pLeafGeometry(pOrig->m_pLeafGeometry),
m_pLightingEngine(pOrig->m_pLightingEngine),
m_pWindEngine(pOrig->m_pWindEngine),
m_pSimpleBillboard(pOrig->m_pSimpleBillboard),
m_pFrondEngine(pOrig->m_pFrondEngine),
m_pFrondGeometry(pOrig->m_pFrondGeometry),
m_nBranchWindLevel(pOrig->m_nBranchWindLevel),
m_nFrondLevel(pOrig->m_nFrondLevel),
m_usNumFrondLodLevels(pOrig->m_usNumFrondLodLevels),
// leaf lod
m_eLeafLodMethod(pOrig->m_eLeafLodMethod),
m_fLeafLodTransitionRadius(pOrig->m_fLeafLodTransitionRadius),
m_fLeafLodCurveExponent(pOrig->m_fLeafLodCurveExponent),
m_fLeafSizeIncreaseFactor(pOrig->m_fLeafSizeIncreaseFactor),
m_fLeafTransitionFactor(pOrig->m_fLeafTransitionFactor),
m_pLeafLodSizeFactors(pOrig->m_pLeafLodSizeFactors),
// instancing
m_pInstanceRefCount(pOrig->m_pInstanceRefCount),
m_pInstanceList(pOrig->m_pInstanceList),
// other
m_pTreeSizes(pOrig->m_pTreeSizes),
m_ucTargetAlphaValue(pOrig->m_ucTargetAlphaValue),
m_bTreeComputed(pOrig->m_bTreeComputed),
// texcoords
m_pEmbeddedTexCoords(pOrig->m_pEmbeddedTexCoords),
m_b360Billboard(pOrig->m_b360Billboard),
m_bHorizontalBillboard(pOrig->m_bHorizontalBillboard),
// collision objects
m_pCollisionObjects(pOrig->m_pCollisionObjects),
// shadow projection
m_pProjectedShadow(pOrig->m_pProjectedShadow),
// user data
m_pUserData(pOrig->m_pUserData)
{
try
{
st_assert(pOrig);
st_assert(pOrig->m_pEngine);
st_assert(m_pInstanceRefCount);
// add to parent's instance list
m_pInstanceList->m_vInstances.push_back(this);
// copy horizontal billboard coordinates
memcpy(m_afHorizontalCoords, pOrig->m_afHorizontalCoords, 12 * sizeof(float));
// reference counting for instances
(*m_pInstanceRefCount)++;
m_pInstanceData = new STreeInstanceData;
m_pInstanceData->m_pParent = pOrig;
m_pInstanceData->m_cPosition = pOrig->m_pEngine->GetPosition( );
m_pInstanceData->m_fLodLevel = pOrig->GetLodLevel( );
m_uiAllRefCount++;
}
SpeedTreeCatch("CSpeedTreeRT::CSpeedTreeRT(CSpeedTreeRT*)")
SpeedTreeCatchAll("CSpeedTreeRT::CSpeedTreeRT(CSpeedTreeRT*)");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::~CSpeedTreeRT definition
CSpeedTreeRT::~CSpeedTreeRT( )
{
// delete tree from global tree list (if not an instance)
if (!m_pInstanceData)
{
#ifndef _NO_THREAD_
THREAD_SYNCRONIZE( s_TreeLoadingLock );
#endif
{
vector<CSpeedTreeRT*>::iterator iFind = find(SInstanceList::m_vUniqueTrees.begin( ), SInstanceList::m_vUniqueTrees.end( ), this);
// there shouldn't be any reason why this won't be found, but we're being careful
if (iFind != SInstanceList::m_vUniqueTrees.end( ))
(void) SInstanceList::m_vUniqueTrees.erase(iFind);
}
}
// instance reference counting
st_assert(m_pInstanceRefCount);
(*m_pInstanceRefCount)--;
// if this tree was an instance
if (m_pInstanceData)
{
// find the instance in the parent's instance list and delete it
st_assert(m_pInstanceList);
vector<CSpeedTreeRT*>::iterator iFind = find(m_pInstanceList->m_vInstances.begin( ), m_pInstanceList->m_vInstances.end( ), this);
st_assert(iFind != m_pInstanceList->m_vInstances.end( ));
(void) m_pInstanceList->m_vInstances.erase(iFind);
// delete the data
delete m_pInstanceData;
m_pInstanceData = NULL;
}
// if this is the last class to hold these pointers, they should be deleted
if (*m_pInstanceRefCount == 0)
{
DeleteTransientData( );
delete m_pBranchGeometry;
delete m_pLightingEngine;
delete m_pWindEngine;
delete m_pLeafGeometry;
delete m_pSimpleBillboard;
delete m_pEngine;
delete m_pInstanceRefCount;
delete m_pInstanceList;
delete[] m_pTreeSizes;
delete m_pCollisionObjects;
delete m_pEmbeddedTexCoords;
delete m_pFrondEngine;
delete m_pFrondGeometry;
delete m_pLeafLodSizeFactors;
delete m_pProjectedShadow;
delete[] m_pUserData;
}
// null the pointers because delete has been called on this
// instance even though memory may not necessarily be freed.
//lint -save -e672
m_pBranchGeometry = NULL;
m_pLightingEngine = NULL;
m_pWindEngine = NULL;
m_pLeafGeometry = NULL;
m_pSimpleBillboard = NULL;
m_pEngine = NULL;
m_pInstanceRefCount = NULL;
m_pTreeSizes = NULL;
m_pInstanceList = NULL;
m_pCollisionObjects = NULL;
m_pEmbeddedTexCoords = NULL;
m_pFrondEngine = NULL;
m_pFrondGeometry = NULL;
m_pLeafLodSizeFactors = NULL;
m_pProjectedShadow = NULL;
m_pUserData = NULL;
//lint -restore
if (!--m_uiAllRefCount)
CIdvBezierSpline::ClearCache( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::operator new definition
//
// These operators ensure that the memory allocation is handled using the
// C run-time (CRT) library linked by the DLL. This enables the DLL to be
// used by applications using a different CRT.
void* CSpeedTreeRT::operator new(size_t nSize)
{
return ::operator new(nSize);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::operator delete definition
//
// These operators ensure that the memory allocation is handled using the
// C run-time (CRT) library linked by the DLL. This enables the DLL to be
// used by applications using a different CRT.
void CSpeedTreeRT::operator delete(void* pRawMemory)
{
::operator delete(pRawMemory);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::operator new[] definition
//
// These operators ensure that the memory allocation is handled using the
// C run-time (CRT) library linked by the DLL. This enables the DLL to be
// used by applications using a different CRT.
void* CSpeedTreeRT::operator new[](size_t nSize)
{
return ::new char[nSize];
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::operator delete[] definition
//
// These operators ensure that the memory allocation is handled using the
// C run-time (CRT) library linked by the DLL. This enables the DLL to be
// used by applications using a different CRT.
void CSpeedTreeRT::operator delete[](void* pRawMemory)
{
::delete[] pRawMemory;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::Compute definition
bool CSpeedTreeRT::Compute(const float* pTransform, unsigned int nSeed, bool bCompositeStrips)
{
try
{
if (!m_bTreeComputed)
{
// compute tree's geometry
CIdvBranch::SetFrondEngine(m_pFrondEngine);
m_pEngine->SetSeed(nSeed);
m_pEngine->Compute(m_fLeafSizeIncreaseFactor);
m_pFrondEngine->Compute(m_pFrondGeometry, m_pLightingEngine);
m_usNumFrondLodLevels = m_pFrondGeometry->GetNumLodLevels( );
m_pEngine->InitTables( );
// compute static lighting
if (m_pLightingEngine->GetLeafLightingMethod( ) == CSpeedTreeRT::LIGHT_STATIC)
ComputeLeafStaticLighting( );
// prep wind and leaves
const SIdvWindInfo& sWindInfo = m_pEngine->GetWindData( );
m_pWindEngine->Init(sWindInfo);
m_pLeafGeometry->Init(static_cast<unsigned short>(m_pEngine->GetNumLeafLodLevels( )), m_pEngine->GetAllLeaves( ), m_pEngine->GetLeafInfo( ));
// move embedeed texcoords (from file) into leaf geometry class and fronds
int i;
if (m_pEmbeddedTexCoords)
{
for (i = 0; i < m_pEmbeddedTexCoords->m_nNumLeafMaps; ++i)
m_pLeafGeometry->SetTextureCoords(i, &(m_pEmbeddedTexCoords->m_pLeafTexCoords[i * 8]));
for (i = 0; i < m_pEmbeddedTexCoords->m_nNumFrondMaps; ++i)
m_pFrondEngine->SetTextureCoords(m_pFrondGeometry, i, &(m_pEmbeddedTexCoords->m_pFrondTexCoords[i * 8]), m_bTextureFlip);
}
// for each branch LOD level, convert all strips to single strip using
// degenerate triangles
if (bCompositeStrips)
{
m_pBranchGeometry->CombineStrips( );
m_pFrondGeometry->CombineStrips( );
}
// apply optional transformation
if (pTransform)
{
CTransform cTransform;
memcpy((void*) cTransform.m_afData, pTransform, 16 * sizeof(float));
m_pBranchGeometry->Transform(cTransform);
m_pLeafGeometry->Transform(cTransform);
m_pFrondGeometry->Transform(cTransform);
if (m_pCollisionObjects)
m_pCollisionObjects->TransformAll(cTransform);
}
// retrieve extents from branch and leaf geometry classes
CRegion cExtents;
m_pBranchGeometry->ComputeExtents(cExtents);
m_pLeafGeometry->ComputeExtents(cExtents);
m_pFrondGeometry->ComputeExtents(cExtents);
memcpy(m_pTreeSizes + STS_MIN_BOX, cExtents.m_cMin.m_afData, 3 * sizeof(float));
memcpy(m_pTreeSizes + STS_MAX_BOX, cExtents.m_cMax.m_afData, 3 * sizeof(float));
// figure out how wide the billboard should be
CVec3 cOrigin(0.0f, 0.0f, 0.0f);
//lint -save -e666
#ifdef UPVECTOR_POS_Y
float fDimension = cOrigin.Distance(CVec3(m_pTreeSizes[0], m_pTreeSizes[2], 0.0f));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[0], m_pTreeSizes[5], 0.0f)));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[3], m_pTreeSizes[2], 0.0f)));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[3], m_pTreeSizes[5], 0.0f)));
#else
float fDimension = cOrigin.Distance(CVec3(m_pTreeSizes[0], m_pTreeSizes[1], 0.0f));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[0], m_pTreeSizes[4], 0.0f)));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[3], m_pTreeSizes[1], 0.0f)));
fDimension = __max(fDimension, cOrigin.Distance(CVec3(m_pTreeSizes[3], m_pTreeSizes[4], 0.0f)));
#endif
//lint -restore
m_pTreeSizes[STS_BB_SIZE] = fDimension * 2.0f;
// compute the self-shadow coordinates
if (m_pProjectedShadow)
ComputeSelfShadowTexCoords( );
// setup the horizontal billboard
SetupHorizontalBillboard( );
// some operations are not valid once the geometry has been computed
m_bTreeComputed = true;
}
else
SetError("Compute() called more than once for single tree model (ignored)");
}
SpeedTreeCatch("CSpeedTreeRT::Compute")
SpeedTreeCatchAll("CSpeedTreeRT::Compute");
return m_bTreeComputed;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::Clone definition
CSpeedTreeRT* CSpeedTreeRT::Clone(float x, float y, float z, unsigned int nSeed) const
{
CSpeedTreeRT* pClone = NULL;
try
{
if (m_pEngine->TransientDataIntact( ))
{
// allocate the clone
pClone = new CSpeedTreeRT;
// copy all of the appropriate data
*pClone->m_pLightingEngine = *m_pLightingEngine;
*pClone->m_pWindEngine = *m_pWindEngine;
*pClone->m_pFrondEngine = *m_pFrondEngine;
pClone->m_pBranchGeometry->EnableVertexWeighting(m_pBranchGeometry->IsVertexWeightingEnabled( ));
pClone->m_pBranchGeometry->EnableManualLighting(m_pLightingEngine->GetBranchLightingMethod( ) == LIGHT_STATIC);
pClone->m_pBranchGeometry->SetWindMethod(m_pWindEngine->GetBranchWindMethod( ));
pClone->m_pFrondGeometry->EnableVertexWeighting(m_pFrondGeometry->IsVertexWeightingEnabled( ));
pClone->m_pFrondGeometry->EnableManualLighting(m_pLightingEngine->GetFrondLightingMethod( ) == LIGHT_STATIC);
pClone->m_pFrondGeometry->SetWindMethod(m_pWindEngine->GetFrondWindMethod( ));
pClone->m_pLeafGeometry->EnableVertexWeighting(m_pLeafGeometry->IsVertexWeightingEnabled( ));
pClone->m_pLeafGeometry->EnableManualLighting(m_pLightingEngine->GetLeafLightingMethod( ) == LIGHT_STATIC);
pClone->m_ucTargetAlphaValue = m_ucTargetAlphaValue;
m_pEngine->Clone(pClone->m_pEngine, CVec3(x, y, z), nSeed);
// copy embedded coordinate if they exist
if (m_pEmbeddedTexCoords)
{
pClone->m_pEmbeddedTexCoords = new SEmbeddedTexCoords;
*pClone->m_pEmbeddedTexCoords = *m_pEmbeddedTexCoords;
}
// copy projected shadow if it exists
if (m_pProjectedShadow)
{
pClone->m_pProjectedShadow = new CProjectedShadow;
*pClone->m_pProjectedShadow = *m_pProjectedShadow;
}
// copy collision objects if they exist
if (m_pCollisionObjects)
{
pClone->m_pCollisionObjects = new SCollisionObjects;
*pClone->m_pCollisionObjects = *m_pCollisionObjects;
}
// copy m_pLeafLodSizeFactors if allocated
if (m_pLeafLodSizeFactors)
{
pClone->m_pLeafLodSizeFactors = new float[GetNumLeafLodLevels( )];
memcpy(pClone->m_pLeafLodSizeFactors, m_pLeafLodSizeFactors, GetNumLeafLodLevels( ) * sizeof(float));
}
// copy user data if it exists
if (m_pUserData)
pClone->m_pUserData = CopyUserData(m_pUserData);
pClone->m_eLeafLodMethod = m_eLeafLodMethod;
pClone->m_fLeafLodTransitionRadius = m_fLeafLodTransitionRadius;
pClone->m_fLeafLodCurveExponent = m_fLeafLodCurveExponent;
pClone->m_fLeafSizeIncreaseFactor = m_fLeafSizeIncreaseFactor;
pClone->m_fLeafTransitionFactor = m_fLeafTransitionFactor;
pClone->m_usNumFrondLodLevels = m_usNumFrondLodLevels;
pClone->m_b360Billboard = m_b360Billboard;
pClone->m_bHorizontalBillboard = m_bHorizontalBillboard;
// copy horizontal billboard coordinates
memcpy(pClone->m_afHorizontalCoords, m_afHorizontalCoords, 12 * sizeof(float));
st_assert(pClone);
st_assert(pClone->m_pEngine);
}
else
SetError("cannot Clone() after calling DeleteTransientData()");
}
SpeedTreeCatch("CSpeedTreeRT::Clone")
SpeedTreeCatchAll("CSpeedTreeRT::Clone");
return pClone;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::InstanceOf definition
const CSpeedTreeRT* CSpeedTreeRT::InstanceOf(void) const
{
const CSpeedTreeRT* pParent = NULL;
if (m_pInstanceData)
pParent = m_pInstanceData->m_pParent;
return pParent;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::MakeInstance definition
CSpeedTreeRT* CSpeedTreeRT::MakeInstance(void)
{
CSpeedTreeRT* pInstance = NULL;
try
{
// we can only make instances if the transient data is intact
if (m_pEngine->TransientDataIntact( ))
{
pInstance = new CSpeedTreeRT(this);
}
else
SetError("cannot MakeInstance() after calling DeleteTransientData()");
}
SpeedTreeCatch("CSpeedTreeRT::MakeInstance")
SpeedTreeCatchAll("CSpeedTreeRT::MakeInstance");
return pInstance;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::DeleteTransientData definition
void CSpeedTreeRT::DeleteTransientData(void)
{
if (m_pEngine->TransientDataIntact( ))
m_pEngine->FreeTransientData( );
else
SetError("DeleteTransientData() called with no intact transient data");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::LoadTree definition
bool CSpeedTreeRT::LoadTree( KStream * pStream )
{
bool bSuccess = false;
unsigned char* pMemory = NULL;
try
{
if (pStream)
{
int nNumBytes = pStream->GetLength();
int nErrorCode = pStream->Seek( 0, KStream::seekSet );
if (nNumBytes > 0 &&
nErrorCode >= 0)
{
// read all of the file into memory, then pass into other Parse() function
pMemory = new unsigned char[nNumBytes];
int nBytesRead = pStream->Read( pMemory, nNumBytes );
if (nBytesRead == nNumBytes)
{
bSuccess = LoadTree(pMemory, nNumBytes);
delete[] pMemory;
pMemory = NULL;
}
else
throw(exception(IdvFormatString("only read %d of %d from %s [%s]", nBytesRead, nNumBytes, "KStream Load", strerror(errno)).c_str( )));
}
else
throw(exception(IdvFormatString("file seek failed on '%s' [%s]", "KStream Load", strerror(errno)).c_str( )));
}
}
catch (exception& cException)
{
pMemory = NULL;
SetError(IdvFormatString("CSpeedTreeRT::Load Tree - %s", cException.what( )).c_str( ));
}
catch (...)
{
pMemory = NULL;
SetError(IdvFormatString("CSpeedTreeRT::LoadTree - threw an unknown system exception").c_str( ));
}
return bSuccess;
}
bool CSpeedTreeRT::LoadTree(const char* pFilename)
{
#include "EvalTest.h"
bool bSuccess = false;
unsigned char* pMemory = NULL;
try
{
if (pFilename)
{
FILE* pFile = fopen(pFilename, "rb");
if (pFile)
{
fseek(pFile, 0L, SEEK_END);
int nNumBytes = ftell(pFile);
int nErrorCode = fseek(pFile, 0, SEEK_SET);
if (nNumBytes > 0 &&
nErrorCode >= 0)
{
// read all of the file into memory, then pass into other Parse() function
pMemory = new unsigned char[nNumBytes];
int nBytesRead = fread(pMemory, 1, nNumBytes, pFile);
if (nBytesRead == nNumBytes)
{
bSuccess = LoadTree(pMemory, nNumBytes);
delete[] pMemory;
pMemory = NULL;
}
else
throw(exception(IdvFormatString("only read %d of %d from %s [%s]", nBytesRead, nNumBytes, pFilename, strerror(errno)).c_str( )));
// in our experience, fclose doesn't always give accurate return values
(void) fclose(pFile);
}
else
throw(exception(IdvFormatString("file seek failed on '%s' [%s]", pFilename, strerror(errno)).c_str( )));
}
else
throw(exception(IdvFormatString("failed to load file '%s' [%s]", pFilename, strerror(errno)).c_str( )));
}
}
catch (exception& cException)
{
pMemory = NULL;
SetError(IdvFormatString("CSpeedTreeRT::Load Tree - %s", cException.what( )).c_str( ));
}
catch (...)
{
pMemory = NULL;
SetError(IdvFormatString("CSpeedTreeRT::LoadTree - threw an unknown system exception").c_str( ));
}
return bSuccess;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::LoadTree definition
bool CSpeedTreeRT::LoadTree(const unsigned char* pBlock, unsigned int nNumBytes)
{
//lint -e537 { it's OK that this file is included twice }
#include "EvalTest.h"
bool bSuccess = false;
try
{
CTreeFileAccess cFile(pBlock, nNumBytes);
if (m_pEngine->Parse(cFile))
{
if (!cFile.EndOfFile( ))
{
// New spt data is saved after all original format to
// maintain backward compatibility
bool bEndOfFile = false;
int nToken = cFile.ParseToken( );
do
{
switch (nToken)
{
case File_BeginLightingInfo:
m_pLightingEngine->Parse(cFile);
break;
case File_BeginLodInfo:
ParseLodInfo(&cFile);
break;
case File_BeginNewWindInfo:
ParseWindInfo(&cFile);
break;
case File_BeginTextureCoordInfo:
ParseTextureCoordInfo(&cFile);
break;
case File_BeginCollisionInfo:
ParseCollisionObjects(&cFile);
break;
case File_BeginFrondInfo:
m_pFrondEngine->Parse(cFile);
break;
case File_BeginTextureControls:
m_pEngine->ParseTextureControls(cFile);
break;
case File_BeginFlareInfo:
m_pEngine->ParseFlareInfo(cFile);
break;
case File_FlareSeed:
m_pEngine->ParseFlareSeed(cFile);
break;
case File_LeafTransitionFactor:
m_fLeafTransitionFactor = cFile.ParseFloat( );
break;
case File_BeginShadowProjectionInfo:
ParseShadowProjectionInfo(&cFile);
break;
case File_BeginUserData:
ParseUserData(&cFile);
break;
case File_BeginSupplementalTexCoordInfo:
ParseSupplementalTexCoordInfo(&cFile);
break;
default:
bEndOfFile = true;
}
if (cFile.EndOfFile( ))
bEndOfFile = true;
else
nToken = cFile.ParseToken( );
} while (!bEndOfFile);
}
// update all lighting data
SetBranchLightingMethod(m_pLightingEngine->GetBranchLightingMethod( ));
SetLeafLightingMethod(m_pLightingEngine->GetLeafLightingMethod( ));
SetFrondLightingMethod(m_pLightingEngine->GetFrondLightingMethod( ));
// extract other data for later queries
m_nBranchWindLevel = m_pEngine->GetBranchLevelForWeighting( );
m_nFrondLevel = m_pFrondEngine->GetLevel( );
// handle obsolete smooth_2 setting
if (m_eLeafLodMethod == 2)
{
m_eLeafLodMethod = CSpeedTreeRT::LOD_SMOOTH;
m_fLeafTransitionFactor = 0.5f;
}
bSuccess = true;
}
}
SpeedTreeCatch("CSpeedTreeRT::LoadTree")
SpeedTreeCatchAll("CSpeedTreeRT::LoadTree");
return bSuccess;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SaveTree definition
unsigned char* CSpeedTreeRT::SaveTree(unsigned int& nNumBytes, bool bSaveLeaves) const
{
unsigned char* pData = NULL;
try
{
if (m_pEngine->TransientDataIntact( ))
{
CTreeFileAccess cFile;
// save original spt data
m_pEngine->Save(cFile, bSaveLeaves);
// save lighting info
m_pLightingEngine->Save(cFile);
// save lod info
cFile.SaveToken(File_BeginLodInfo);
cFile.SaveToken(File_LeafTransitionMethod);
cFile.SaveInt(static_cast<int>(m_eLeafLodMethod));
cFile.SaveToken(File_LeafTransitionRadius);
cFile.SaveFloat(m_fLeafLodTransitionRadius);
cFile.SaveToken(File_LeafCurveExponent);
cFile.SaveFloat(m_fLeafLodCurveExponent);
cFile.SaveToken(File_LeafSizeIncreaseFactor);
cFile.SaveFloat(m_fLeafSizeIncreaseFactor);
m_pEngine->SaveLodInfo(cFile);
cFile.SaveToken(File_EndLodInfo);
// save wind info
cFile.SaveToken(File_BeginNewWindInfo);
cFile.SaveToken(File_WindLevel);
cFile.SaveInt(m_pEngine->GetBranchLevelForWeighting( ));
cFile.SaveToken(File_EndNewWindInfo);
// save texture coord info
if (m_pEmbeddedTexCoords)
SaveTextureCoords(&cFile);
// save collision objects
if (m_pCollisionObjects)
SaveCollisionObjects(&cFile);
// save frond info
m_pFrondEngine->Save(cFile);
// save extras
m_pEngine->SaveTextureControls(cFile);
m_pEngine->SaveFlareInfo(cFile);
m_pEngine->SaveFlareSeed(cFile);
cFile.SaveToken(File_LeafTransitionFactor);
cFile.SaveFloat(m_fLeafTransitionFactor);
// save projected shadows
if (m_pProjectedShadow)
m_pProjectedShadow->Save(cFile);
// save user data
SaveUserData(&cFile);
// save supplemental texcoord info
if (m_pEmbeddedTexCoords)
SaveSupplementalTexCoordInfo(&cFile);
// create spt formatted data
pData = cFile.ConvertMemoryToArray(nNumBytes);
}
else
SetError("cannot Save() after DeleteTransientData() is called");
}
SpeedTreeCatch("CSpeedTreeRT::SaveTree")
SpeedTreeCatchAll("CSpeedTreeRT::SaveTree");
return pData;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetTreeSize definition
void CSpeedTreeRT::GetTreeSize(float& fSize, float& fVariance) const
{
m_pEngine->GetSize(fSize, fVariance);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetTreeSize definition
void CSpeedTreeRT::SetTreeSize(float fNewSize, float fNewVariance)
{
try
{
if (m_pEngine->TransientDataIntact( ))
{
if (fNewSize > 0.0f)
m_pEngine->SetSize(fNewSize, fNewVariance);
else
SetError("SetTreeSize() is only valid for size values greater than 0.0");
}
else
SetError("SetTreeSize() has no effect after DeleteTransientData() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetTreeSize")
SpeedTreeCatchAll("CSpeedTreeRT::SetTreeSize");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetSeed definition
unsigned int CSpeedTreeRT::GetSeed(void) const
{
unsigned int nSeed = 0;
try
{
if (m_pEngine->TransientDataIntact( ))
nSeed = m_pEngine->GetSeed( );
else
SetError("SetTreeSize() has no effect after DeleteTransientData() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetTreeSize")
SpeedTreeCatchAll("CSpeedTreeRT::SetTreeSize");
return nSeed;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetTreePosition definition
const float* CSpeedTreeRT::GetTreePosition(void) const
{
const float* pPos = NULL;
if (m_pInstanceData)
pPos = m_pInstanceData->m_cPosition.m_afData;
else
pPos = m_pEngine->GetPosition( ).m_afData;
return pPos;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetTreePosition definition
void CSpeedTreeRT::SetTreePosition(float x, float y, float z)
{
if (m_pInstanceData)
m_pInstanceData->m_cPosition.Set(x, y, z);
else
m_pEngine->SetPosition(CVec3(x, y, z));
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafTargetAlphaMask definition
void CSpeedTreeRT::SetLeafTargetAlphaMask(unsigned char ucMask)
{
m_ucTargetAlphaValue = ucMask;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBranchLightingMethod definition
CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetBranchLightingMethod(void) const
{
return m_pLightingEngine->GetBranchLightingMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetBranchLightingMethod definition
void CSpeedTreeRT::SetBranchLightingMethod(ELightingMethod eMethod)
{
try
{
if (!m_bTreeComputed)
{
m_pBranchGeometry->EnableManualLighting(eMethod == LIGHT_STATIC);
m_pLightingEngine->SetBranchLightingMethod(eMethod);
}
else
SetError("SetBranchLightingMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetBranchLightingMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetBranchLightingMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafLightingMethod definition
CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetLeafLightingMethod(void) const
{
return m_pLightingEngine->GetLeafLightingMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafLightingMethod definition
void CSpeedTreeRT::SetLeafLightingMethod(ELightingMethod eMethod)
{
try
{
if (!m_bTreeComputed)
{
m_pLightingEngine->SetLeafLightingMethod(eMethod);
m_pLeafGeometry->EnableManualLighting(eMethod == LIGHT_STATIC);
}
else
SetError("SetLeafLightingMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafLightingMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafLightingMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetStaticLightingStyle definition
CSpeedTreeRT::EStaticLightingStyle CSpeedTreeRT::GetStaticLightingStyle(void) const
{
return m_pLightingEngine->GetStaticLightingStyle( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetStaticLightingStyle definition
void CSpeedTreeRT::SetStaticLightingStyle(EStaticLightingStyle eStyle)
{
try
{
if (!m_bTreeComputed)
{
st_assert(m_pLightingEngine);
m_pLightingEngine->SetStaticLightingStyle(eStyle);
}
else
SetError("SetStaticLightingStyle() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetStaticLightingStyle")
SpeedTreeCatchAll("CSpeedTreeRT::SetStaticLightingStyle");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafLightingAdjustment definition
float CSpeedTreeRT::GetLeafLightingAdjustment( ) const
{
return m_pLightingEngine->GetLeafLightingAdjustment( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafLightingAdjustment definition
void CSpeedTreeRT::SetLeafLightingAdjustment(float fScalar)
{
try
{
if (!m_bTreeComputed)
m_pLightingEngine->SetLeafLightingAdjustment(fScalar);
else
SetError("SetLeafLightingAdjustment() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafLightingAdjustment")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafLightingAdjustment");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondLightingMethod definition
CSpeedTreeRT::ELightingMethod CSpeedTreeRT::GetFrondLightingMethod(void) const
{
return m_pLightingEngine->GetFrondLightingMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetFrondLightingMethod definition
void CSpeedTreeRT::SetFrondLightingMethod(ELightingMethod eMethod)
{
try
{
if (!m_bTreeComputed)
{
m_pFrondGeometry->EnableManualLighting(eMethod == LIGHT_STATIC);
m_pLightingEngine->SetFrondLightingMethod(eMethod);
}
else
SetError("SetFrondLightingMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetFrondLightingMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetFrondLightingMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLightState definition
bool CSpeedTreeRT::GetLightState(unsigned int nLightIndex)
{
return CLightingEngine::GetLightState(nLightIndex);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLightState definition
void CSpeedTreeRT::SetLightState(unsigned int nLightIndex, bool bLightState)
{
CLightingEngine::SetLightState(nLightIndex, bLightState);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLightAttributes definition
const float* CSpeedTreeRT::GetLightAttributes(unsigned int nLightIndex)
{
return CLightingEngine::GetLightAttributes(nLightIndex);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLightAttributes definition
void CSpeedTreeRT::SetLightAttributes(unsigned int nLightIndex, const float* pAttributes)
{
try
{
CLightingEngine::SetLightAttributes(nLightIndex, pAttributes);
}
SpeedTreeCatch("CSpeedTreeRT::SetLightAttributes")
SpeedTreeCatchAll("CSpeedTreeRT::SetLightAttributes");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBranchMaterial definition
const float* CSpeedTreeRT::GetBranchMaterial(void) const
{
return m_pLightingEngine->GetBranchMaterial( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetBranchMaterial definition
void CSpeedTreeRT::SetBranchMaterial(const float* pBranchMaterial)
{
try
{
if (!m_bTreeComputed)
m_pLightingEngine->SetBranchMaterial(pBranchMaterial);
else
SetError("SetBranchMaterial() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetBranchMaterial")
SpeedTreeCatchAll("CSpeedTreeRT::SetBranchMaterial");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafMaterial definition
const float* CSpeedTreeRT::GetLeafMaterial(void) const
{
return m_pLightingEngine->GetLeafMaterial( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafMaterial definition
void CSpeedTreeRT::SetLeafMaterial(const float* pLeafMaterial)
{
try
{
if (!m_bTreeComputed)
m_pLightingEngine->SetLeafMaterial(pLeafMaterial);
else
SetError("SetLeafMaterial() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafMaterial")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafMaterial");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondMaterial definition
const float* CSpeedTreeRT::GetFrondMaterial (void) const
{
return m_pLightingEngine->GetFrondMaterial( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetFrondMaterial definition
void CSpeedTreeRT::SetFrondMaterial(const float* pLeafMaterial)
{
try
{
if (!m_bTreeComputed)
m_pLightingEngine->SetFrondMaterial(pLeafMaterial);
else
SetError("SetFrondMaterial() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetFrondMaterial")
SpeedTreeCatchAll("CSpeedTreeRT::SetFrondMaterial");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetCamera definition
void CSpeedTreeRT::GetCamera(float* pPosition, float* pDirection)
{
if (pPosition && pDirection)
{
CVec3 cCameraPos, cCameraDirection;
CTreeEngine::GetCamera(cCameraPos, cCameraDirection);
memcpy(pPosition, cCameraPos.m_afData, 3 * sizeof(float));
memcpy(pDirection, cCameraDirection.m_afData, 3 * sizeof(float));
}
else
SetError("GetCamera() requires non-NULL position and direction values");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetCamera definition
void CSpeedTreeRT::SetCamera(const float *pPosition, const float* pDirection)
{
try
{
if (pPosition && pDirection)
{
CVec3 cOldPosition, cOldDirection;
CTreeEngine::GetCamera(cOldPosition, cOldDirection);
CVec3 cNewPosition(pPosition[0], pPosition[1], pPosition[2]);
CVec3 cNewDirection(pDirection[0], pDirection[1], pDirection[2]);
// only want to recompute billboards if necessary
if (cOldPosition != cNewPosition ||
cOldDirection != cNewDirection)
{
CTreeEngine::SetCamera(cNewPosition, cNewDirection);
NotifyAllTreesOfEvent(STIE_CLIENT_CHANGED_CAMERA);
CSimpleBillboard::ComputeUnitBillboard(cNewDirection.m_afData);
NotifyAllTreesOfEvent(STIE_CLIENT_CHANGED_CAMERA);
}
}
else
SetError("SetCamera() requires non-NULL position and direction values");
}
SpeedTreeCatch("CSpeedTreeRT::SetCamera")
SpeedTreeCatchAll("CSpeedTreeRT::SetCamera");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetTime definition
void CSpeedTreeRT::SetTime(float fTime)
{
try
{
CWindEngine::SetTime(fTime);
NotifyAllTreesOfEvent(STIE_CLIENT_CHANGED_WIND);
}
SpeedTreeCatch("CSpeedTreeRT::SetTime")
SpeedTreeCatchAll("CSpeedTreeRT::SetTime");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ComputeWindEffects definition
void CSpeedTreeRT::ComputeWindEffects(bool bBranches, bool bLeaves, bool bFronds)
{
try
{
float fHighestLod = -1.0f;
// branches
if (bBranches &&
m_pWindEngine->GetBranchWindMethod( ) == CSpeedTreeRT::WIND_CPU)
{
if (!m_pInstanceData && *m_pInstanceRefCount > 1)
{
// If this tree has instances of itself, then compute the highest LOD
// among itself and its instances. There's no need for more wind
// computations than the highest LOD because all of the LODs share
// the same vertexes.
fHighestLod = m_pEngine->GetLod( );
for (vector<CSpeedTreeRT*>::iterator i = m_pInstanceList->m_vInstances.begin( );
i != m_pInstanceList->m_vInstances.end( ); ++i)
{
float fInstanceLodLevel = (*i)->GetLodLevel( );
if (fInstanceLodLevel > fHighestLod)
fHighestLod = fInstanceLodLevel;
}
(void) m_pBranchGeometry->ComputeWindEffect(GetDiscreteBranchLodLevel(fHighestLod));
}
else
(void) m_pBranchGeometry->ComputeWindEffect(GetDiscreteBranchLodLevel(GetLodLevel( )));
}
// fronds
if (bFronds &&
m_pWindEngine->GetFrondWindMethod( ) == CSpeedTreeRT::WIND_CPU)
{
if (!m_pInstanceData && *m_pInstanceRefCount > 1)
{
// If this tree has instances of itself, then compute the highest LOD
// among itself and its instances. There's no need for more wind
// computations than the highest LOD because all of the LODs share
// the same vertexes.
if (fHighestLod == -1.0f) // we don't need to make this calc if it was
{ // already done for the branches
fHighestLod = m_pEngine->GetLod( );
for (vector<CSpeedTreeRT*>::iterator i = m_pInstanceList->m_vInstances.begin( );
i != m_pInstanceList->m_vInstances.end( ); ++i)
{
float fInstanceLodLevel = (*i)->GetLodLevel( );
if (fInstanceLodLevel > fHighestLod)
fHighestLod = fInstanceLodLevel;
}
}
(void) m_pFrondGeometry->ComputeWindEffect(GetDiscreteFrondLodLevel(fHighestLod));
}
else
(void) m_pFrondGeometry->ComputeWindEffect(GetDiscreteFrondLodLevel(GetLodLevel( )));
}
// leaves
if (bLeaves &&
m_pWindEngine->GetLeafWindMethod( ) == CSpeedTreeRT::WIND_CPU)
{
m_pLeafGeometry->Invalidate( );
}
}
SpeedTreeCatch("CSpeedTreeRT::ComputeWindEffects")
SpeedTreeCatchAll("CSpeedTreeRT::ComputeWindEffects");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ResetLeafWindState definition
void CSpeedTreeRT::ResetLeafWindState( )
{
m_pWindEngine->ResetLeafWindState( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafRockingState definition
bool CSpeedTreeRT::GetLeafRockingState(void) const
{
return m_pWindEngine->GetLeafRockingState( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafRockingState definition
void CSpeedTreeRT::SetLeafRockingState(bool bFlag)
{
m_pWindEngine->SetLeafRockingState(bFlag);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetNumLeafRockingGroups definition
void CSpeedTreeRT::SetNumLeafRockingGroups(unsigned int nRockingGroups)
{
try
{
if (!m_bTreeComputed)
{
if (nRockingGroups == 0)
nRockingGroups = 1;
m_pEngine->SetNumLeafRockingGroups(nRockingGroups);
}
else
SetError("SetNumLeafRockingGroups() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafRockingState")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafRockingState");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafWindMethod definition
CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetLeafWindMethod(void) const
{
return m_pWindEngine->GetLeafWindMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafWindMethod definition
void CSpeedTreeRT::SetLeafWindMethod(EWindMethod eMethod)
{
try
{
if (!m_bTreeComputed)
{
m_pWindEngine->SetLeafWindMethod(eMethod);
m_pLeafGeometry->EnableVertexWeighting(eMethod != WIND_NONE);
}
else
SetError("SetLeafWindMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafWindMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafWindMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBranchWindMethod definition
CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetBranchWindMethod(void) const
{
return m_pWindEngine->GetBranchWindMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetBranchWindMethod definition
void CSpeedTreeRT::SetBranchWindMethod(EWindMethod eMethod)
{
try
{
// Because the wind engine is optimized to only calculate the wind
// effect for the highest visible LOD, the highest overall LOD may
// not be calcuated. When the wind is turned off, some of the tree
// will be positioned according to the last wind calculation and
// some of it may not be. We make a call here to force it to
// calculate the entire tree's geometry for the last wind value.
if (!m_bTreeComputed)
{
if (eMethod == WIND_NONE &&
m_pWindEngine->GetBranchWindMethod( ) == WIND_CPU)
{
m_pBranchGeometry->Invalidate( );
(void) m_pBranchGeometry->ComputeWindEffect(0);
}
m_pWindEngine->SetBranchWindMethod(eMethod);
m_pBranchGeometry->EnableVertexWeighting(eMethod != WIND_NONE);
m_pBranchGeometry->SetWindMethod(eMethod);
}
else
SetError("SetBranchWindMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetBranchWindMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetBranchWindMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondWindMethod definition
CSpeedTreeRT::EWindMethod CSpeedTreeRT::GetFrondWindMethod(void) const
{
return m_pWindEngine->GetFrondWindMethod( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetFrondWindMethod definition
void CSpeedTreeRT::SetFrondWindMethod(EWindMethod eMethod)
{
try
{
// Because the wind engine is optimized to only calculate the wind
// effect for the highest visible LOD, the highest overall LOD may
// not be calcuated. When the wind is turned off, some of the tree
// will be positioned according to the last wind calculation and
// some of it may not be. We make a call here to force it to
// calculate the entire tree's geometry for the last wind value.
if (!m_bTreeComputed)
{
if (eMethod == WIND_NONE &&
m_pWindEngine->GetFrondWindMethod( ) == WIND_CPU)
{
m_pFrondGeometry->Invalidate( );
(void) m_pFrondGeometry->ComputeWindEffect(0);
}
m_pWindEngine->SetFrondWindMethod(eMethod);
m_pFrondGeometry->EnableVertexWeighting(eMethod != WIND_NONE);
m_pFrondGeometry->SetWindMethod(eMethod);
}
else
SetError("SetFrondWindMethod() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetFrondWindMethod")
SpeedTreeCatchAll("CSpeedTreeRT::SetFrondWindMethod");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetWindStrength definition
float CSpeedTreeRT::GetWindStrength(void) const
{
return m_pWindEngine->GetWindStrength( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetWindStrength definition
float CSpeedTreeRT::SetWindStrength(float fNewStrength, float fOldStrength, float fFrequencyTimeOffset)
{
float fNewFrequencyTimeShift = 0.0f;
try
{
if (fNewStrength >= 0.0f)
{
if (fOldStrength == -1.0f)
fOldStrength = m_pWindEngine->GetWindStrength( );
if (fFrequencyTimeOffset == -1.0f)
fFrequencyTimeOffset = m_pWindEngine->GetFrequencyTimeShift( );
fNewFrequencyTimeShift = m_pWindEngine->SetWindStrength(fNewStrength, fOldStrength, fFrequencyTimeOffset);
// invalidate geometry if CPU-based wind
if (GetBranchWindMethod( ) == WIND_CPU &&
m_pBranchGeometry)
m_pBranchGeometry->Invalidate( );
if (GetFrondWindMethod( ) == WIND_CPU &&
m_pFrondGeometry)
m_pFrondGeometry->Invalidate( );
if (GetLeafWindMethod( ) == WIND_CPU ||
m_pWindEngine->GetLeafRockingState( ))
m_pLeafGeometry->Invalidate( );
}
else
SetError("SetWindStrength() expects new wind strength >= 0.0");
}
SpeedTreeCatch("CSpeedTreeRT::SetWindStrength")
SpeedTreeCatchAll("CSpeedTreeRT::SetWindStrength");
return fNewFrequencyTimeShift;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetNumWindMatrices definition
void CSpeedTreeRT::SetNumWindMatrices(int nNumMatrices)
{
try
{
CWindEngine::SetNumWindMatrices(static_cast<unsigned short>(nNumMatrices));
}
SpeedTreeCatch("CSpeedTreeRT::SetNumWindMatrices")
SpeedTreeCatchAll("CSpeedTreeRT::SetNumWindMatrices");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetWindMatrix definition
void CSpeedTreeRT::SetWindMatrix(unsigned int nMatrixIndex, const float* pMatrix)
{
st_assert(pMatrix);
st_assert(nMatrixIndex >= 0 && nMatrixIndex < CWindEngine::GetNumWindMatrices( ));
try
{
if (!pMatrix)
SetError("SetWindMatrix() requires a non-NULL matrix pointer");
else if (nMatrixIndex >= CWindEngine::GetNumWindMatrices( ))
SetError("SetWindMatrix() - matrix index out of range");
else
{
CWindEngine::SetWindMatrix(static_cast<unsigned short>(nMatrixIndex), pMatrix);
NotifyAllTreesOfEvent(STIE_CLIENT_CHANGED_WIND);
}
}
SpeedTreeCatch("CSpeedTreeRT::SetWindMatrix")
SpeedTreeCatchAll("CSpeedTreeRT::SetWindMatrix");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLocalMatrices definition
void CSpeedTreeRT::GetLocalMatrices(unsigned int& nStartingMatrix, unsigned int& nMatrixSpan)
{
m_pWindEngine->GetLocalMatrices(nStartingMatrix, nMatrixSpan);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLocalMatrices definition
void CSpeedTreeRT::SetLocalMatrices(unsigned int nStartingMatrix, unsigned int nMatrixSpan)
{
try
{
if (!m_bTreeComputed)
{
if (nStartingMatrix + nMatrixSpan - 1 < m_pWindEngine->GetNumWindMatrices( ))
m_pWindEngine->SetLocalMatrices(nStartingMatrix, nMatrixSpan);
else
SetError("SetLocalMatrices() parameters exceed available wind matrices");
}
else
SetError("SetLocalMatrices() has no effect after Compute() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLocalMatrices")
SpeedTreeCatchAll("CSpeedTreeRT::SetLocalMatrices");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ComputeLodLevel definition
void CSpeedTreeRT::ComputeLodLevel(void)
{
try
{
if (m_pInstanceData)
{
// must activate instance's attributes, make the computation,
// then restore the parent's attributes
CVec3 cOldPosition = m_pEngine->GetPosition( );
float fOldLodLevel = m_pEngine->GetLod( );
// compute
m_pEngine->SetPosition(m_pInstanceData->m_cPosition);
m_pInstanceData->m_fLodLevel = m_pEngine->ComputeLod( );
// restore
m_pEngine->SetLod(fOldLodLevel);
m_pEngine->SetPosition(cOldPosition);
}
else
(void) m_pEngine->ComputeLod( );
}
SpeedTreeCatch("CSpeedTreeRT::ComputeLodLevel")
SpeedTreeCatchAll("CSpeedTreeRT::ComputeLodLevel");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLodLevel definition
float CSpeedTreeRT::GetLodLevel(void) const
{
float fLevel = -1.0f; // default to invalid value
if (m_pInstanceData)
fLevel = m_pInstanceData->m_fLodLevel;
else
fLevel = m_pEngine->GetLod( );
return fLevel;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLodLevel definition
void CSpeedTreeRT::SetLodLevel(float fLevel)
{
if (fLevel >= 0.0f && fLevel <= 1.0f)
{
if (m_pInstanceData)
m_pInstanceData->m_fLodLevel = fLevel;
else
m_pEngine->SetLod(fLevel);
}
else
SetError("SetLodLevel() expects a value in the range of 0.0 to 1.0");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetDropToBillboard definition
void CSpeedTreeRT::SetDropToBillboard(bool bFlag)
{
m_bDropToBillboard = bFlag;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLodLimits definition
void CSpeedTreeRT::GetLodLimits(float& fNear, float& fFar) const
{
m_pEngine->GetLodLimits(fNear, fFar);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLodLimits definition
void CSpeedTreeRT::SetLodLimits(float fNear, float fFar)
{
m_pEngine->SetLodLimits(fNear, fFar);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetDiscreteBranchLodLevel definition
short CSpeedTreeRT::GetDiscreteBranchLodLevel(float fLodLevel) const
{
short sLevel = 0;
// -1.0f passed in means to use the current interval value
if (fLodLevel == -1.0f)
fLodLevel = GetLodLevel( );
int nNumLodLevels = GetNumBranchLodLevels( );
sLevel = static_cast<short>((1.0f - fLodLevel) * nNumLodLevels);
if (sLevel == nNumLodLevels)
sLevel--;
st_assert(sLevel >= 0 && sLevel < GetNumBranchLodLevels( ));
return sLevel;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetDiscreteFrondLodLevel definition
short CSpeedTreeRT::GetDiscreteFrondLodLevel(float fLodLevel) const
{
short sLevel = 0;
// -1.0f passed in means to use the current interval value
if (fLodLevel == -1.0f)
fLodLevel = GetLodLevel( );
int nNumLodLevels = GetNumFrondLodLevels( );
sLevel = static_cast<short>((1.0f - fLodLevel) * nNumLodLevels);
if (sLevel == nNumLodLevels)
sLevel--;
st_assert(sLevel >= 0 && sLevel < GetNumFrondLodLevels( ));
return sLevel;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetDiscreteLeafLodLevel definition
unsigned short CSpeedTreeRT::GetDiscreteLeafLodLevel(float fLodLevel) const
{
unsigned short nLevel = 0;
// -1.0f passed in means to use the current interval value
if (fLodLevel == -1.0f)
fLodLevel = GetLodLevel( );
if (m_bDropToBillboard && m_pEmbeddedTexCoords && m_pEmbeddedTexCoords->m_nNumBillboards > 0)
{
int nNumLodLevels = GetNumLeafLodLevels( ) + 1;
nLevel = static_cast<unsigned short>((1.0f - fLodLevel) * nNumLodLevels);
if (nLevel == nNumLodLevels)
nLevel--;
st_assert(nLevel >= 0 && nLevel < nNumLodLevels);
}
else
{
int nNumLodLevels = GetNumLeafLodLevels( );
nLevel = static_cast<unsigned short>((1.0f - fLodLevel) * nNumLodLevels);
if (nLevel == nNumLodLevels)
nLevel--;
st_assert(nLevel >= 0 && nLevel < nNumLodLevels);
}
return nLevel;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetNumBranchLodLevels definition
unsigned short CSpeedTreeRT::GetNumBranchLodLevels(void) const
{
return static_cast<unsigned short>(m_pEngine->GetNumBranchLodLevels( ));
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetNumLeafLodLevels definition
unsigned short CSpeedTreeRT::GetNumLeafLodLevels(void) const
{
return static_cast<unsigned short>(m_pEngine->GetNumLeafLodLevels( ));
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetNumFrondLodLevels definition
unsigned short CSpeedTreeRT::GetNumFrondLodLevels(void) const
{
return m_usNumFrondLodLevels;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::DeleteBranchGeometry definition
void CSpeedTreeRT::DeleteBranchGeometry(void)
{
try
{
if (m_bTreeComputed &&
m_pBranchGeometry &&
!m_pInstanceData &&
*m_pInstanceRefCount == 1)
{
delete m_pBranchGeometry;
m_pBranchGeometry = NULL;
}
}
SpeedTreeCatch("CSpeedTreeRT::DeleteBranchGeometry")
SpeedTreeCatchAll("CSpeedTreeRT::DeleteBranchGeometry");
}
///////////////////////////////////////////////////////////////////////
// function RoundToNearestInt definition
inline int RoundToNearestInt(float fValue)
{
int nIntValue = int(fValue);
float fDiff = fValue - nIntValue;
if (fDiff >= 0.5f)
++nIntValue;
return nIntValue;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafBillboardTable definition
const float* CSpeedTreeRT::GetLeafBillboardTable(unsigned int& nEntryCount) const
{
const float* pBillboardTable = NULL;
try
{
pBillboardTable = m_pLeafGeometry->GetLeafBillboardTable(nEntryCount);
}
SpeedTreeCatch("CSpeedTreeRT::GetLeafBillboardTable")
SpeedTreeCatchAll("CSpeedTreeRT::GetLeafBillboardTable");
return pBillboardTable;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::Authorize definition
void CSpeedTreeRT::Authorize(const char* pKey)
{
g_strKey = pKey;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::IsAuthorized definition
bool CSpeedTreeRT::IsAuthorized(void)
{
#ifdef FULL_VERSION
return true;
#else
string strFailure;
return EvalIsValid(g_strKey, strFailure);
#endif
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetCurrentError definition
const char* CSpeedTreeRT::GetCurrentError(void)
{
return g_strError.c_str( );
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ResetError definition
void CSpeedTreeRT::ResetError(void)
{
g_strError = "";
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBoundingBox definition
void CSpeedTreeRT::GetBoundingBox(float* pBounds) const
{
if (pBounds)
memcpy(pBounds, m_pTreeSizes, 6 * sizeof(float));
else
SetError("GetBoundingBox() expects a non-NULL parameter");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafTriangleCount definition
unsigned int CSpeedTreeRT::GetLeafTriangleCount(float fLodLevel) const
{
int nTriangleCount = 0;
int nDiscreteLevel = GetDiscreteLeafLodLevel(fLodLevel);
if (m_eLeafLodMethod == LOD_NONE)
nDiscreteLevel = 0;
else if (nDiscreteLevel != -1)
nTriangleCount = m_pLeafGeometry->GetTriangleCount(nDiscreteLevel);
return nTriangleCount;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBranchTriangleCount definition
unsigned int CSpeedTreeRT::GetBranchTriangleCount(float fLodLevel) const
{
unsigned int nTriangleCount = 0;
try
{
int nDiscreteLevel = GetDiscreteBranchLodLevel(fLodLevel);
if (nDiscreteLevel != -1 &&
m_pBranchGeometry)
nTriangleCount = m_pBranchGeometry->GetTriangleCount(static_cast<unsigned short>(nDiscreteLevel));
}
SpeedTreeCatch("CSpeedTreeRT::GetBranchTriangleCount")
SpeedTreeCatchAll("CSpeedTreeRT::GetBranchTriangleCount");
return nTriangleCount;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondTriangleCount definition
unsigned int CSpeedTreeRT::GetFrondTriangleCount(float fLodLevel) const
{
unsigned int nTriangleCount = 0;
try
{
int nDiscreteLevel = GetDiscreteFrondLodLevel(fLodLevel);
if (nDiscreteLevel != -1 &&
m_pFrondGeometry)
nTriangleCount = m_pFrondGeometry->GetTriangleCount(static_cast<unsigned short>(nDiscreteLevel));
}
SpeedTreeCatch("CSpeedTreeRT::GetFrondTriangleCount")
SpeedTreeCatchAll("CSpeedTreeRT::GetFrondTriangleCount");
return nTriangleCount;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ComputeLeafStaticLighting definition
void CSpeedTreeRT::ComputeLeafStaticLighting(void)
{
try
{
float afBoundingBox[6];
GetBoundingBox(afBoundingBox);
CVec3 cTreeCenter((afBoundingBox[0] + afBoundingBox[3]) * 0.5f,
(afBoundingBox[1] + afBoundingBox[4]) * 0.5f,
(afBoundingBox[2] + afBoundingBox[5]) * 0.5f);
vector<CBillboardLeaf*>* pAllLeaves = m_pEngine->GetAllLeaves( );
m_pLightingEngine->ComputeLeafStaticLighting(cTreeCenter, pAllLeaves, GetNumLeafLodLevels( ));
}
SpeedTreeCatch("CSpeedTreeRT::ComputeLeafStaticLighting")
SpeedTreeCatchAll("CSpeedTreeRT::ComputeLeafStaticLighting");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::NotifyAllTreesOfEvent definition
void CSpeedTreeRT::NotifyAllTreesOfEvent(int nMessage)
{
#ifndef _NO_THREAD_
THREAD_SYNCRONIZE( s_TreeLoadingLock );
#endif
try
{
for (vector<CSpeedTreeRT*>::iterator i = SInstanceList::m_vUniqueTrees.begin( ); i != SInstanceList::m_vUniqueTrees.end( ); ++i)
{
if (nMessage == STIE_CLIENT_CHANGED_WIND)
{
if ((*i)->GetBranchWindMethod( ) == WIND_CPU)
(*i)->m_pBranchGeometry->Invalidate( );
if ((*i)->GetFrondWindMethod( ) == WIND_CPU)
(*i)->m_pFrondGeometry->Invalidate( );
if ((*i)->GetLeafWindMethod( ) == WIND_CPU ||
(*i)->m_pWindEngine->GetLeafRockingState( ))
(*i)->m_pLeafGeometry->Invalidate( );
}
else if (nMessage == STIE_CLIENT_CHANGED_CAMERA) // client changed camera
{
(*i)->m_pLeafGeometry->Invalidate( );
(*i)->m_pSimpleBillboard->Invalidate( );
}
}
}
SpeedTreeCatch("CSpeedTreeRT::NotifyAllTreesOfEvent")
SpeedTreeCatchAll("CSpeedTreeRT::NotifyAllTreesOfEvent");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetError definition
void CSpeedTreeRT::SetError(const char* pError)
{
g_strError = pError;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseLodInfo definition
void CSpeedTreeRT::ParseLodInfo(CTreeFileAccess* pFile)
{
int nToken = pFile->ParseToken( );
do
{
switch (nToken)
{
case File_LeafTransitionMethod:
m_eLeafLodMethod = static_cast<ELodMethod>(pFile->ParseInt( ));
break;
case File_LeafTransitionRadius:
m_fLeafLodTransitionRadius = pFile->ParseFloat();
break;
case File_LeafCurveExponent:
m_fLeafLodCurveExponent = pFile->ParseFloat( );
break;
case File_LeafSizeIncreaseFactor:
m_fLeafSizeIncreaseFactor = pFile->ParseFloat( );
break;
case File_BeginEngineLodInfo:
m_pEngine->ParseLodInfo(*pFile);
break;
default:
throw(IdvFileError("malformed lod info"));
}
if (pFile->EndOfFile( ))
throw(IdvFileError("premature end of file reached parsing new lod info"));
else
nToken = pFile->ParseToken( );
} while (nToken != File_EndLodInfo);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseWindInfo definition
void CSpeedTreeRT::ParseWindInfo(CTreeFileAccess* pFile)
{
int nToken = pFile->ParseToken( );
do
{
switch (nToken)
{
case File_WindLevel:
m_pEngine->SetBranchLevelForWeighting(pFile->ParseInt( ));
break;
default:
throw(IdvFileError("malformed new wind info"));
}
if (pFile->EndOfFile( ))
throw(IdvFileError("premature end of file reached parsing new wind info"));
else
nToken = pFile->ParseToken( );
} while (nToken != File_EndNewWindInfo);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseTextureCoordInfo definition
void CSpeedTreeRT::ParseTextureCoordInfo(CTreeFileAccess* pFile)
{
m_pEmbeddedTexCoords = new SEmbeddedTexCoords;
int nToken = pFile->ParseToken( );
do
{
switch (nToken)
{
case File_LeafTextureCoords:
{
m_pEmbeddedTexCoords->m_nNumLeafMaps = pFile->ParseInt( );
if (m_pEmbeddedTexCoords->m_nNumLeafMaps > 0)
{
m_pEmbeddedTexCoords->m_pLeafTexCoords = new float[m_pEmbeddedTexCoords->m_nNumLeafMaps * 8];
for (int i = 0; i < m_pEmbeddedTexCoords->m_nNumLeafMaps; ++i)
{
for (int j = 0; j < 8; ++j)
m_pEmbeddedTexCoords->m_pLeafTexCoords[i * 8 + j] = pFile->ParseFloat( );
}
}
}
break;
case File_BillboardTextureCoords:
{
m_pEmbeddedTexCoords->m_nNumBillboards = pFile->ParseInt( );
if (m_pEmbeddedTexCoords->m_nNumBillboards > 0)
{
m_pEmbeddedTexCoords->m_pBillboardTexCoords = new float[m_pEmbeddedTexCoords->m_nNumBillboards * 8];
for (int i = 0; i < m_pEmbeddedTexCoords->m_nNumBillboards; ++i)
{
for (int j = 0; j < 8; ++j)
{
m_pEmbeddedTexCoords->m_pBillboardTexCoords[i * 8 + j] = pFile->ParseFloat( );
if (j % 2 && m_bTextureFlip) // flip T coordinates if necessary
m_pEmbeddedTexCoords->m_pBillboardTexCoords[i * 8 + j] = -m_pEmbeddedTexCoords->m_pBillboardTexCoords[i * 8 + j];
}
}
}
}
break;
case File_FrondTextureCoords:
{
m_pEmbeddedTexCoords->m_nNumFrondMaps = pFile->ParseInt( );
if (m_pEmbeddedTexCoords->m_nNumFrondMaps > 0)
{
m_pEmbeddedTexCoords->m_pFrondTexCoords = new float[m_pEmbeddedTexCoords->m_nNumFrondMaps * 8];
for (int i = 0; i < m_pEmbeddedTexCoords->m_nNumFrondMaps; ++i)
{
for (int j = 0; j < 8; ++j)
m_pEmbeddedTexCoords->m_pFrondTexCoords[i * 8 + j] = pFile->ParseFloat( );
}
}
}
break;
case File_CompositeFilename:
{
CIdvFilename cFile = pFile->ParseString( );
m_pEmbeddedTexCoords->m_strCompositeFilename = cFile.NoPath( );
}
break;
case File_HorizontalBillboard:
{
m_bHorizontalBillboard = pFile->ParseBool( );
}
break;
case File_360Billboard:
{
m_b360Billboard = pFile->ParseBool( );
}
break;
default:
throw(IdvFileError("malformed texture coord info"));
}
if (pFile->EndOfFile( ))
throw(IdvFileError("premature end of file reached parsing texture coordinate info"));
else
nToken = pFile->ParseToken( );
} while (nToken != File_EndTextureCoordInfo);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetCollisionObjectCount definition
unsigned int CSpeedTreeRT::GetCollisionObjectCount( )
{
unsigned int nCount = 0;
if (m_pCollisionObjects)
nCount = m_pCollisionObjects->m_vObjects.size( );
return nCount;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetCollisionObjectCount definition
void CSpeedTreeRT::GetCollisionObject(unsigned int nIndex, ECollisionObjectType& eType, float* pPosition, float* pDimensions)
{
if (m_pCollisionObjects)
{
if (nIndex >= m_pCollisionObjects->m_vObjects.size( ))
SetError(IdvFormatString("collision object index (%d) exceeds maximum index (%d)", nIndex, m_pCollisionObjects->m_vObjects.size( )).c_str( ));
else
{
eType = m_pCollisionObjects->m_vObjects[nIndex].m_eType;
memcpy(pPosition, m_pCollisionObjects->m_vObjects[nIndex].m_afPosition, 3 * sizeof(float));
memcpy(pDimensions, m_pCollisionObjects->m_vObjects[nIndex].m_afDimensions, 3 * sizeof(float));
}
}
else
SetError("no collision objects are stored with this tree");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseCollisionObjects definition
void CSpeedTreeRT::ParseCollisionObjects(CTreeFileAccess* pFile)
{
m_pCollisionObjects = new SCollisionObjects;
int nToken = pFile->ParseToken( );
do
{
SShape sShape;
switch (nToken)
{
case File_CollisionSphere:
sShape.m_eType = CSpeedTreeRT::CO_SPHERE;
break;
case File_CollisionCylinder:
sShape.m_eType = CSpeedTreeRT::CO_CYLINDER;
break;
case File_CollisionBox:
sShape.m_eType = CSpeedTreeRT::CO_BOX;
break;
default:
throw(IdvFileError("malformed collision object info"));
}
float afPosition[3];
afPosition[0] = pFile->ParseFloat( );
afPosition[1] = pFile->ParseFloat( );
afPosition[2] = pFile->ParseFloat( );
Assign3d(sShape.m_afPosition, afPosition);
switch (sShape.m_eType)
{
case CSpeedTreeRT::CO_SPHERE:
sShape.m_afDimensions[0] = pFile->ParseFloat( ); // radius
break;
case CSpeedTreeRT::CO_CYLINDER:
sShape.m_afDimensions[0] = pFile->ParseFloat( ); // radius
sShape.m_afDimensions[1] = pFile->ParseFloat( ); // height
break;
case CSpeedTreeRT::CO_BOX:
sShape.m_afDimensions[0] = pFile->ParseFloat( ); // x
sShape.m_afDimensions[1] = pFile->ParseFloat( ); // y
sShape.m_afDimensions[2] = pFile->ParseFloat( ); // z
break;
default:
throw(IdvFileError("unknown collision object type"));
}
m_pCollisionObjects->m_vObjects.push_back(sShape);
if (pFile->EndOfFile( ))
throw(IdvFileError("premature end of file reached parsing collision object info"));
else
nToken = pFile->ParseToken( );
} while (nToken != File_EndCollisionInfo);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SaveTextureCoords definition
void CSpeedTreeRT::SaveTextureCoords(CTreeFileAccess* pFile) const
{
if (m_pEmbeddedTexCoords)
{
pFile->SaveToken(File_BeginTextureCoordInfo);
// leaves
pFile->SaveToken(File_LeafTextureCoords);
pFile->SaveInt(m_pEmbeddedTexCoords->m_nNumLeafMaps);
int i, j, k, l;
for (i = 0; i < m_pEmbeddedTexCoords->m_nNumLeafMaps; ++i)
for (j = 0; j < 8; ++j)
pFile->SaveFloat(m_pEmbeddedTexCoords->m_pLeafTexCoords[i * 8 + j]);
// billboards
pFile->SaveToken(File_BillboardTextureCoords);
pFile->SaveInt(m_pEmbeddedTexCoords->m_nNumBillboards);
for (i = 0; i < m_pEmbeddedTexCoords->m_nNumBillboards; ++i)
for (k = 0; k < 8; ++k)
pFile->SaveFloat(m_pEmbeddedTexCoords->m_pBillboardTexCoords[i * 8 + k]);
// fronds
pFile->SaveToken(File_FrondTextureCoords);
pFile->SaveInt(m_pEmbeddedTexCoords->m_nNumFrondMaps);
for (i = 0; i < m_pEmbeddedTexCoords->m_nNumFrondMaps; ++i)
for (l = 0; l < 8; ++l)
pFile->SaveFloat(m_pEmbeddedTexCoords->m_pFrondTexCoords[i * 8 + l]);
// pFile->SaveToken(File_CompositeFilename);
// pFile->SaveString(m_pEmbeddedTexCoords->m_strCompositeFilename);
//
// pFile->SaveToken(File_HorizontalBillboard);
// pFile->SaveBool(m_bHorizontalBillboard);
//
// pFile->SaveToken(File_360Billboard);
// pFile->SaveBool(m_b360Billboard);
pFile->SaveToken(File_EndTextureCoordInfo);
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SaveCollisionObjects definition
void CSpeedTreeRT::SaveCollisionObjects(CTreeFileAccess* pFile) const
{
if (m_pCollisionObjects)
{
pFile->SaveToken(File_BeginCollisionInfo);
for (unsigned int i = 0; i < m_pCollisionObjects->m_vObjects.size( ); ++i)
{
SShape& sShape = m_pCollisionObjects->m_vObjects[i];
switch (sShape.m_eType)
{
case CSpeedTreeRT::CO_SPHERE:
pFile->SaveToken(File_CollisionSphere);
break;
case CSpeedTreeRT::CO_CYLINDER:
pFile->SaveToken(File_CollisionCylinder);
break;
case CSpeedTreeRT::CO_BOX:
pFile->SaveToken(File_CollisionBox);
break;
}
pFile->SaveFloat(sShape.m_afPosition[0]);
pFile->SaveFloat(sShape.m_afPosition[1]);
pFile->SaveFloat(sShape.m_afPosition[2]);
switch (sShape.m_eType)
{
case CSpeedTreeRT::CO_SPHERE:
pFile->SaveFloat(sShape.m_afDimensions[0]); // radius
break;
case CSpeedTreeRT::CO_CYLINDER:
pFile->SaveFloat(sShape.m_afDimensions[0]); // radius
pFile->SaveFloat(sShape.m_afDimensions[1]); // height
break;
case CSpeedTreeRT::CO_BOX:
pFile->SaveFloat(sShape.m_afDimensions[0]); // x
pFile->SaveFloat(sShape.m_afDimensions[1]); // y
pFile->SaveFloat(sShape.m_afDimensions[2]); // z
break;
}
}
pFile->SaveToken(File_EndCollisionInfo);
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::DeleteFrondGeometry definition
void CSpeedTreeRT::DeleteFrondGeometry(void)
{
try
{
if (m_bTreeComputed &&
m_pFrondGeometry &&
!m_pInstanceData &&
m_pInstanceRefCount &&
*m_pInstanceRefCount == 1)
{
delete m_pFrondGeometry;
m_pFrondGeometry = NULL;
}
}
SpeedTreeCatch("CSpeedTreeRT::DeleteFrondGeometry")
SpeedTreeCatchAll("CSpeedTreeRT::DeleteFrondGeometry");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondGeometryMapIndexes definition
unsigned char* CSpeedTreeRT::GetFrondGeometryMapIndexes(int nLodLevel) const
{
unsigned char* pMapIndexes = NULL;
try
{
if (m_pFrondGeometry)
{
unsigned short usNumStrips = m_pFrondGeometry->GetNumStrips(static_cast<unsigned short>(nLodLevel));
if (usNumStrips > 0)
{
pMapIndexes = new unsigned char[usNumStrips];
const unsigned short* pStripLengths = m_pFrondGeometry->GetStripLengthsPointer(static_cast<unsigned short>(nLodLevel));
const unsigned short** pStrips = const_cast<const unsigned short**>(m_pFrondGeometry->GetStripsPointer(static_cast<unsigned short>(nLodLevel)));
const unsigned char* pTexIndices = m_pFrondGeometry->GetVertexTexIndices( );
for (int i = 0; i < usNumStrips; ++i)
{
if (pStripLengths[i] == 0)
pMapIndexes[i] = 0;
else
pMapIndexes[i] = pTexIndices[pStrips[i][0]];
}
}
}
}
SpeedTreeCatch("CSpeedTreeRT::GetFrondGeometryMapIndexes( )")
SpeedTreeCatchAll("CSpeedTreeRT::GetFrondGeometryMapIndexes( )");
return pMapIndexes;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetGeometry definition
void CSpeedTreeRT::GetGeometry(SGeometry& sGeometry, unsigned long ulBitVector, short sOverrideBranchLodValue, short sOverrideFrondLodValue, short sOverrideLeafLodValue)
{
st_assert(m_pEngine);
try
{
if (ulBitVector & SpeedTree_BranchGeometry)
GetBranchGeometry(sGeometry, sOverrideBranchLodValue); // compute fizzle-alpha value here
if (ulBitVector & SpeedTree_FrondGeometry)
GetFrondGeometry(sGeometry, sOverrideFrondLodValue); // compute fizzle-alpha value here
if (ulBitVector & SpeedTree_LeafGeometry)
GetLeafGeometry(sGeometry, sOverrideLeafLodValue);
if (ulBitVector & SpeedTree_BillboardGeometry)
{
if (m_b360Billboard && !(ulBitVector & SpeedTree_SimpleBillboardOverride))
Get360BillboardGeometry(sGeometry);
else
GetSimpleBillboardGeometry(sGeometry);
}
}
SpeedTreeCatch("CSpeedTreeRT::GetGeometry")
SpeedTreeCatchAll("CSpeedTreeRT::GetGeometry");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetBranchGeometry definition
void CSpeedTreeRT::GetBranchGeometry(SGeometry& sGeometry, short sOverrideLodValue)
{
if (m_pBranchGeometry)
{
SGeometry::SIndexed& sBranches = sGeometry.m_sBranches;
// get vertex attributes
sBranches.m_usVertexCount = m_pBranchGeometry->GetVertexCount( );
sBranches.m_pColors = m_pBranchGeometry->GetVertexColors( );
sBranches.m_pCoords = m_pBranchGeometry->GetVertexCoords( );
sBranches.m_pNormals = m_pBranchGeometry->GetVertexNormals( );
sBranches.m_pBinormals = m_pBranchGeometry->GetVertexBinormals( );
sBranches.m_pTangents = m_pBranchGeometry->GetVertexTangents( );
sBranches.m_pTexCoords0 = m_pBranchGeometry->GetVertexTexCoords0( );
sBranches.m_pTexCoords1 = m_pBranchGeometry->GetVertexTexCoords1( );
sBranches.m_pWindWeights = m_pBranchGeometry->GetVertexWindWeights( );
sBranches.m_pWindMatrixIndices = m_pBranchGeometry->GetVertexWindMatrixIndices( );
// get indexing data
short sLodLevel = (sOverrideLodValue == -1) ? GetDiscreteBranchLodLevel( ) : sOverrideLodValue;
sBranches.m_nDiscreteLodLevel = sLodLevel;
sBranches.m_usNumStrips = m_pBranchGeometry->GetNumStrips(sLodLevel);
sBranches.m_pStripLengths = m_pBranchGeometry->GetStripLengthsPointer(sLodLevel);
sBranches.m_pStrips = const_cast<const unsigned short**>(m_pBranchGeometry->GetStripsPointer(sLodLevel));
// determine alpha test value
if (m_bDropToBillboard && m_pEmbeddedTexCoords && m_pEmbeddedTexCoords->m_nNumBillboards > 0)
{
unsigned short usLodStageCount = GetNumLeafLodLevels( ) + 1; // add one for billboard
float fHighLodAlphaValue = -1.0f, fLowLodAlphaValue = -1.0f;
short sHighLod = -1, sLowLod = -1;
GetTransitionValues(GetLodLevel( ), usLodStageCount, m_fLeafLodTransitionRadius, m_fLeafTransitionFactor, m_fLeafLodCurveExponent,
float(m_ucTargetAlphaValue), fHighLodAlphaValue, fLowLodAlphaValue, sHighLod, sLowLod);
if (sHighLod == usLodStageCount - 2)
{
sGeometry.m_fBranchAlphaTestValue = fHighLodAlphaValue;
}
else if (sHighLod == usLodStageCount - 1)
{
sGeometry.m_fBranchAlphaTestValue = 255.0f; // completely off
sBranches.m_nDiscreteLodLevel = -1;
}
else
{
sGeometry.m_fBranchAlphaTestValue = m_ucTargetAlphaValue;
}
}
else
sGeometry.m_fBranchAlphaTestValue = m_ucTargetAlphaValue;
}
else
SetError("no branch geometry exists, possible prior call to DeleteBranchGeometry");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetFrondGeometry definition
void CSpeedTreeRT::GetFrondGeometry(SGeometry& sGeometry, short sOverrideLodValue)
{
if (m_pFrondGeometry)
{
SGeometry::SIndexed& sFronds = sGeometry.m_sFronds;
// get vertex attributes
sFronds.m_usVertexCount = m_pFrondGeometry->GetVertexCount( );
sFronds.m_pColors = m_pFrondGeometry->GetVertexColors( );
sFronds.m_pCoords = m_pFrondGeometry->GetVertexCoords( );
sFronds.m_pNormals = m_pFrondGeometry->GetVertexNormals( );
sFronds.m_pBinormals = m_pFrondGeometry->GetVertexBinormals( );
sFronds.m_pTangents = m_pFrondGeometry->GetVertexTangents( );
sFronds.m_pTexCoords0 = m_pFrondGeometry->GetVertexTexCoords0( );
sFronds.m_pTexCoords1 = m_pFrondGeometry->GetVertexTexCoords1( );
sFronds.m_pWindWeights = m_pFrondGeometry->GetVertexWindWeights( );
sFronds.m_pWindMatrixIndices = m_pFrondGeometry->GetVertexWindMatrixIndices( );
// get indexing data
short sLodLevel = (sOverrideLodValue == -1) ? GetDiscreteFrondLodLevel( ) : sOverrideLodValue;
sFronds.m_nDiscreteLodLevel = sLodLevel;
sFronds.m_usNumStrips = m_pFrondGeometry->GetNumStrips(sLodLevel);
sFronds.m_pStripLengths = m_pFrondGeometry->GetStripLengthsPointer(sLodLevel);
sFronds.m_pStrips = (const unsigned short**) m_pFrondGeometry->GetStripsPointer(sLodLevel);
// determine alpha test value
if (m_bDropToBillboard && m_pEmbeddedTexCoords && m_pEmbeddedTexCoords->m_nNumBillboards > 0)
{
unsigned short usLodStageCount = GetNumLeafLodLevels( ) + 1; // add one for billboard
float fHighLodAlphaValue = -1.0f, fLowLodAlphaValue = -1.0f;
short sHighLod = -1, sLowLod = -1;
GetTransitionValues(GetLodLevel( ), usLodStageCount, m_fLeafLodTransitionRadius, m_fLeafTransitionFactor, m_fLeafLodCurveExponent,
float(m_ucTargetAlphaValue), fHighLodAlphaValue, fLowLodAlphaValue, sHighLod, sLowLod);
if (sHighLod == usLodStageCount - 2)
sGeometry.m_fFrondAlphaTestValue = fHighLodAlphaValue;
else if (sHighLod == usLodStageCount - 1)
{
sGeometry.m_fFrondAlphaTestValue = 255.0f; // completely off
sFronds.m_nDiscreteLodLevel = -1;
}
else
sGeometry.m_fFrondAlphaTestValue = m_ucTargetAlphaValue;
}
else
sGeometry.m_fFrondAlphaTestValue = m_ucTargetAlphaValue;
}
else
SetError("no frond geometry exists, possible prior call to DeleteFrondGeometry");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafGeometry definition
void CSpeedTreeRT::GetLeafGeometry(SGeometry& sGeometry, short sOverrideLeafLodValue)
{
st_assert(m_pLeafGeometry);
unsigned short usLeafLodCount = GetNumLeafLodLevels( );
unsigned short usLodStageCount = usLeafLodCount + ((m_bDropToBillboard && m_pEmbeddedTexCoords && m_pEmbeddedTexCoords->m_nNumBillboards > 0) ? 1 : 0);
// get camera attributes for billboarding
CVec3 cCameraPos, cCameraDirection, cAdjustedDirection;
CTreeEngine::GetCamera(cCameraPos, cCameraDirection);
// direction should be adjusted for application coordinate system
Assign3d(cAdjustedDirection, cCameraDirection);
float fAzimuth = VecRad2Deg(atan2f(cAdjustedDirection[1], cAdjustedDirection[0]));
float fPitch = -VecRad2Deg(asinf(cAdjustedDirection[2]));
if (sOverrideLeafLodValue > -1)
{
st_assert(sOverrideLeafLodValue < GetNumLeafLodLevels( ));
// higher LOD layer only (since the app is requesting a single discrete LOD)
m_pLeafGeometry->Update(sGeometry.m_sLeaves0, sOverrideLeafLodValue, fAzimuth, fPitch, m_fLeafSizeIncreaseFactor);
sGeometry.m_sLeaves0.m_bIsActive = true;
sGeometry.m_sLeaves0.m_fAlphaTestValue = m_ucTargetAlphaValue;
// no LOD layer
sGeometry.m_sLeaves1.m_bIsActive = false;
}
else
{
// LOD_SMOOTH is most common
if (m_eLeafLodMethod == LOD_SMOOTH)
{
// determine alpha values for both leaf layers
float fHighLodAlphaValue = -1.0f, fLowLodAlphaValue = -1.0f;
short sHighLod = -1, sLowLod = -1;
GetTransitionValues(GetLodLevel( ), usLodStageCount, m_fLeafLodTransitionRadius, m_fLeafTransitionFactor, m_fLeafLodCurveExponent,
float(m_ucTargetAlphaValue), fHighLodAlphaValue, fLowLodAlphaValue, sHighLod, sLowLod);
// assign higher LOD layer
sGeometry.m_sLeaves0.m_bIsActive = (sHighLod != -1 && sHighLod < usLeafLodCount);
if (sGeometry.m_sLeaves0.m_bIsActive)
{
m_pLeafGeometry->Update(sGeometry.m_sLeaves0, sHighLod, fAzimuth, fPitch, m_fLeafSizeIncreaseFactor);
sGeometry.m_sLeaves0.m_fAlphaTestValue = fHighLodAlphaValue;
}
// assign lower LOD layer
sGeometry.m_sLeaves1.m_bIsActive = (sLowLod != -1 && sLowLod < usLeafLodCount);
if (sGeometry.m_sLeaves1.m_bIsActive)
{
m_pLeafGeometry->Update(sGeometry.m_sLeaves1, sLowLod, fAzimuth, fPitch, m_fLeafSizeIncreaseFactor);
sGeometry.m_sLeaves1.m_fAlphaTestValue = fLowLodAlphaValue;
}
}
// no LOD is active, always use highest
else if (m_eLeafLodMethod == LOD_NONE)
{
// set leaf layer 0
m_pLeafGeometry->Update(sGeometry.m_sLeaves0, 0, fAzimuth, fPitch, m_fLeafSizeIncreaseFactor);
sGeometry.m_sLeaves0.m_bIsActive = true;
sGeometry.m_sLeaves0.m_fAlphaTestValue = m_ucTargetAlphaValue;
// set leaf layer 1
sGeometry.m_sLeaves1.m_bIsActive = false;
}
else // assume LOD_POP (do hard transitions between LODs)
{
// set leaf layer 0
unsigned short usDiscreteLodLevel = GetDiscreteLeafLodLevel( );
if (usDiscreteLodLevel < GetNumLeafLodLevels( ))
{
m_pLeafGeometry->Update(sGeometry.m_sLeaves0, usDiscreteLodLevel, fAzimuth, fPitch, m_fLeafSizeIncreaseFactor);
sGeometry.m_sLeaves0.m_bIsActive = true;
sGeometry.m_sLeaves0.m_fAlphaTestValue = m_ucTargetAlphaValue;
}
// set leaf layer 1
sGeometry.m_sLeaves1.m_bIsActive = false;
}
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::Get360BillboardGeometry definition
void CSpeedTreeRT::Get360BillboardGeometry(SGeometry& sGeometry)
{
GetSimpleBillboardGeometry(sGeometry);
if (sGeometry.m_sBillboard0.m_bIsActive &&
m_pEmbeddedTexCoords)
{
// the call to GetSimpleBillboardGoemetry() determines an alpha value that
// reflects how much of the billboard should be seen vs. the 3D tree. we
// need to convert that to a floating scalar to multiply against the cross
// fading alpha's determined below
float fFade = (sGeometry.m_sBillboard0.m_fAlphaTestValue - m_ucTargetAlphaValue) / (255.0f - m_ucTargetAlphaValue);
// get camera attributes
CVec3 cCameraPos, cCameraDir;
CTreeEngine::GetCamera(cCameraPos, cCameraDir);
cCameraDir = -cCameraDir;
float fAzimuth = ComputeAzimuth(cCameraDir);
if (fAzimuth < 0.0f)
fAzimuth += 360.0f;
// find blending/LOD values for 360 degree blending between two billboards
float fPrimaryLodAlphaValue = -1.0f, fSecondaryLodAlphaValue = -1.0f;
short sPrimaryLod = -1, sSecondaryLod = -1;
unsigned short usLodStageCount = static_cast<unsigned short>(m_pEmbeddedTexCoords->m_nNumBillboards);
float fLodWidth = 0.5f / usLodStageCount;
float fLodValue = VecInterpolate(fLodWidth, 1.0f - fLodWidth, fAzimuth / 360.0f);
GetTransitionValues(fLodValue, usLodStageCount, 0.5f / usLodStageCount, m_fLeafTransitionFactor, m_fLeafLodCurveExponent,
float(m_ucTargetAlphaValue), fPrimaryLodAlphaValue, fSecondaryLodAlphaValue, sPrimaryLod, sSecondaryLod);
// reverse them to account for leaf LOD reversed behavior
sPrimaryLod = static_cast<short>((m_pEmbeddedTexCoords->m_nNumBillboards - 1) - sPrimaryLod);
sSecondaryLod = static_cast<unsigned short>((m_pEmbeddedTexCoords->m_nNumBillboards - 1) - sSecondaryLod);
// wrap index value if necessary
if (sPrimaryLod == usLodStageCount - 1)
sPrimaryLod = 0;
if (sSecondaryLod == usLodStageCount - 1)
sSecondaryLod = 0;
// assign texture coordinates - the 0 (zero) can be a billboard index ranging from
// zero to m_pEmbeddedTexCoords->m_nNumBillboards - 1
sGeometry.m_sBillboard0.m_pTexCoords = m_pEmbeddedTexCoords->m_pBillboardTexCoords + sPrimaryLod * 8;
sGeometry.m_sBillboard0.m_fAlphaTestValue = VecInterpolate(fPrimaryLodAlphaValue, 255.0f, fFade);
sGeometry.m_sBillboard1.m_pCoords = sGeometry.m_sBillboard0.m_pCoords;
sGeometry.m_sBillboard1.m_pTexCoords = m_pEmbeddedTexCoords->m_pBillboardTexCoords + sSecondaryLod * 8;
sGeometry.m_sBillboard1.m_bIsActive = true;
sGeometry.m_sBillboard1.m_fAlphaTestValue = VecInterpolate(fSecondaryLodAlphaValue, 255.0f, fFade);
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetSimpleBillboardGeometry definition
void CSpeedTreeRT::GetSimpleBillboardGeometry(SGeometry& sGeometry)
{
// determine alpha test value and visibility
if (m_bDropToBillboard && m_pEmbeddedTexCoords && m_pEmbeddedTexCoords->m_nNumBillboards > 0)
{
unsigned short usLodStageCount = GetNumLeafLodLevels( ) + 1; // add one for billboard
float fHighLodAlphaValue = -1.0f, fLowLodAlphaValue = -1.0f;
short sHighLod = -1, sLowLod = -1;
GetTransitionValues(GetLodLevel( ), usLodStageCount, m_fLeafLodTransitionRadius, m_fLeafTransitionFactor, m_fLeafLodCurveExponent,
float(m_ucTargetAlphaValue), fHighLodAlphaValue, fLowLodAlphaValue, sHighLod, sLowLod);
// determine if either leaf LOD level should be a billboard
sGeometry.m_sBillboard0.m_fAlphaTestValue = -1.0f;
if (sHighLod == usLodStageCount - 1)
sGeometry.m_sBillboard0.m_fAlphaTestValue = fHighLodAlphaValue;
else if (sLowLod == usLodStageCount - 1)
sGeometry.m_sBillboard0.m_fAlphaTestValue = fLowLodAlphaValue;
// setup first billboard layer
sGeometry.m_sBillboard0.m_bIsActive = (sGeometry.m_sBillboard0.m_fAlphaTestValue != -1.0f);
if (sGeometry.m_sBillboard0.m_bIsActive)
{
// find appropriate coordinates
#ifdef UPVECTOR_POS_Z
sGeometry.m_sBillboard0.m_pCoords = m_pSimpleBillboard->GetBillboardCoords(m_pTreeSizes[STS_BB_SIZE], m_pTreeSizes[STS_MAX_BOX + 2]);
#else
sGeometry.m_sBillboard0.m_pCoords = m_pSimpleBillboard->GetBillboardCoords(m_pTreeSizes[STS_BB_SIZE], fabs(m_pTreeSizes[STS_MAX_BOX + 1]));
#endif
// assign texture coordinates - the 0 (zero) can be a billboard index ranging from
// zero to m_pEmbeddedTexCoords->m_nNumBillboards - 1
sGeometry.m_sBillboard0.m_pTexCoords = m_pEmbeddedTexCoords->m_pBillboardTexCoords + 0 * 8;;
}
// second billboard layer is always off for simple billboard
sGeometry.m_sBillboard1.m_bIsActive = false;
// horizontal billboard
if (m_bHorizontalBillboard)
{
// always the plane that bisects the bounding box (computed once at initialization)
sGeometry.m_sHorizontalBillboard.m_pCoords = m_afHorizontalCoords;
// the horizontal billboard is always the last billboard in the sequence
sGeometry.m_sHorizontalBillboard.m_pTexCoords = m_pEmbeddedTexCoords->m_pBillboardTexCoords + (m_pEmbeddedTexCoords->m_nNumBillboards - 1) * 8;
sGeometry.m_sHorizontalBillboard.m_fAlphaTestValue = sGeometry.m_sBillboard0.m_fAlphaTestValue;
sGeometry.m_sHorizontalBillboard.m_bIsActive = sGeometry.m_sHorizontalBillboard.m_fAlphaTestValue >= 0.0f;
}
else
sGeometry.m_sHorizontalBillboard.m_bIsActive = false;
}
else
{
sGeometry.m_sBillboard0.m_bIsActive = false;
sGeometry.m_sBillboard1.m_bIsActive = false;
sGeometry.m_sHorizontalBillboard.m_bIsActive = false;
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetTransitionValues definition
//
// Given the function parameters, this function will return by reference
// two values that are used for alpha test fading between two discrete LODs.
//
// fLodLevel current LOD level, 0.0 = low, 1.0 = high
// usLodCount number of discrete LOD levels
// fOverlapRadius in LOD units (0.0, 1.0) amount of overlap between two neighboring LODs
// fTransitionFactor (0.0, 0.5) point at which overlap occurs
// fHighValue alpha value for higher LOD (0, 255)
// fLowValue alpha value for lower LOD (0, 255)
void CSpeedTreeRT::GetTransitionValues(float fLodLevel, unsigned short usLodCount, float fOverlapRadius,
float fTransitionFactor, float fCurveExponent, float fTargetAlphaValue,
float& fHighValue, float& fLowValue, short& sHighLod, short& sLowLod)
{
float fLodWidth = 1.0f / usLodCount;
unsigned short usDiscreteTransitionPoint = static_cast<unsigned short>(RoundToNearestInt((1.0f - fLodLevel) / fLodWidth));
float fDistanceToTransitionPoint = (1.0f - fLodLevel) - usDiscreteTransitionPoint * fLodWidth;
// check to see if there is no overlap
if (usDiscreteTransitionPoint == 0 ||
usDiscreteTransitionPoint == usLodCount ||
fabs(fDistanceToTransitionPoint) > fOverlapRadius)
{
// high LOD
fHighValue = fTargetAlphaValue;
sHighLod = static_cast<short>((1.0f - fLodLevel) * usLodCount);
sHighLod = __min(sHighLod, static_cast<short>(usLodCount - 1));
// low LOD (inactive)
fLowValue = 255.0f;
sLowLod = -1;
}
else // yes there is an overlap, so two factors are needed
{
// transition stage ranges from 0.0 (high LOD side) to 1.0 (low LOD side)
float fTransitionStage = 1.0f - (fOverlapRadius - fDistanceToTransitionPoint) / (2.0f * fOverlapRadius);
// high LOD
float fAmountOfHigh = 1.0f - (fTransitionStage - fTransitionFactor) / (1.0f - fTransitionFactor);
fAmountOfHigh = __min(fAmountOfHigh, 1.0f); // cap at 1.0
sHighLod = usDiscreteTransitionPoint - 1;
fHighValue = VecInterpolate(fTargetAlphaValue, 255.0f, powf((1.0f - fAmountOfHigh), fCurveExponent));
// low LOD
float fAmountOfLow = fTransitionStage / (1.0f - fTransitionFactor);
fAmountOfLow = __min(fAmountOfLow, 1.0f); // cap at 1.0
sLowLod = usDiscreteTransitionPoint;
fLowValue = VecInterpolate(fTargetAlphaValue, 255.0f, powf((1.0f - fAmountOfLow), fCurveExponent));
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetLeafLodSizeAdjustments definition
const float* CSpeedTreeRT::GetLeafLodSizeAdjustments(void)
{
try
{
if (!m_pLeafLodSizeFactors)
{
int nNumLods = GetNumLeafLodLevels( );
m_pLeafLodSizeFactors = new float[nNumLods];
for (int i = 0; i < nNumLods; ++i)
m_pLeafLodSizeFactors[i] = (nNumLods > 0) ? 1.0f + m_fLeafSizeIncreaseFactor * float(i) : 1.0f;
}
}
SpeedTreeCatch("CSpeedTreeRT::GetLeafLodSizeAdjustments")
SpeedTreeCatchAll("CSpeedTreeRT::GetLeafLodSizeAdjustments");
return m_pLeafLodSizeFactors;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseShadowProjectionInfo definition
void CSpeedTreeRT::ParseShadowProjectionInfo(CTreeFileAccess* pFile)
{
m_pProjectedShadow = new CProjectedShadow;
m_pProjectedShadow->Parse(*pFile);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ComputeSelfShadowTexCoords definition
void CSpeedTreeRT::ComputeSelfShadowTexCoords(void)
{
if (m_pBranchGeometry &&
m_pFrondGeometry &&
m_pProjectedShadow)
{
// compute the radius of the circumscribing sphere
CVec3 cMin(m_pTreeSizes[0], m_pTreeSizes[1], m_pTreeSizes[2]), cMax(m_pTreeSizes[3], m_pTreeSizes[4], m_pTreeSizes[5]);
float fRadius = cMin.Distance(cMax) * 0.5f;
// compute the center
CVec3 cCenter = (cMin + cMax) * 0.5f;
if (m_pEmbeddedTexCoords)
{
// compute branch tex coords
m_pProjectedShadow->ComputeTexCoords(m_pBranchGeometry, cCenter, fRadius, m_pEmbeddedTexCoords->m_afShadowTexCoords);
// compute frond tex coords
m_pProjectedShadow->ComputeTexCoords(m_pFrondGeometry, cCenter, fRadius, m_pEmbeddedTexCoords->m_afShadowTexCoords);
}
else
{
// compute branch tex coords
m_pProjectedShadow->ComputeTexCoords(m_pBranchGeometry, cCenter, fRadius);
// compute frond tex coords
m_pProjectedShadow->ComputeTexCoords(m_pFrondGeometry, cCenter, fRadius);
}
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetTextures definition
void CSpeedTreeRT::GetTextures(CSpeedTreeRT::STextures& sTextures) const
{
try
{
// branch
sTextures.m_pBranchTextureFilename = m_pEngine->GetBranchTextureFilename( ).c_str( );
// leaves
const vector<SIdvLeafTexture>& vLeaves = m_pEngine->GetLeafTextures( );
sTextures.m_uiLeafTextureCount = vLeaves.size( );
if (sTextures.m_uiLeafTextureCount > 0)
{
sTextures.m_pLeafTextureFilenames = new const char*[sTextures.m_uiLeafTextureCount];
for (unsigned int i = 0; i < vLeaves.size( ); ++i)
sTextures.m_pLeafTextureFilenames[i] = vLeaves[i].m_strFilename.c_str( );
}
else
sTextures.m_pLeafTextureFilenames = NULL;
// fronds
sTextures.m_uiFrondTextureCount = m_pFrondEngine->GetTextureCount( );
if (sTextures.m_uiFrondTextureCount > 0)
{
sTextures.m_pFrondTextureFilenames = new const char*[sTextures.m_uiFrondTextureCount];
for (unsigned int i = 0; i < sTextures.m_uiFrondTextureCount; ++i)
sTextures.m_pFrondTextureFilenames[i] = m_pFrondEngine->GetTextureFilename(i);
}
else
sTextures.m_pFrondTextureFilenames = NULL;
// self shadow
if (m_pProjectedShadow)
sTextures.m_pSelfShadowFilename = m_pProjectedShadow->GetSelfShadowFilename( );
// composite
if (m_pEmbeddedTexCoords)
sTextures.m_pCompositeFilename = m_pEmbeddedTexCoords->m_strCompositeFilename.c_str( );
}
SpeedTreeCatch("CSpeedTreeRT::GetTextures")
SpeedTreeCatchAll("CSpeedTreeRT::GetTextures");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafTextureCoords definition
void CSpeedTreeRT::SetLeafTextureCoords(unsigned int nLeafMapIndex, const float* pTexCoords)
{
st_assert(m_pEngine);
st_assert(nLeafMapIndex >= 0);
st_assert(pTexCoords);
st_assert(m_pLeafGeometry);
try
{
st_assert(nLeafMapIndex < m_pEngine->GetLeafTextures( ).size( ));
m_pLeafGeometry->SetTextureCoords(nLeafMapIndex, pTexCoords);
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafTextureCoords")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafTextureCoords");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetFrondTextureCoords definition
void CSpeedTreeRT::SetFrondTextureCoords(unsigned int nFrondMapIndex, const float* pTexCoords)
{
try
{
if (m_pFrondGeometry &&
pTexCoords)
m_pFrondEngine->SetTextureCoords(m_pFrondGeometry, nFrondMapIndex, pTexCoords, m_bTextureFlip);
}
SpeedTreeCatch("CSpeedTreeRT::SetFrondTextureCoords")
SpeedTreeCatchAll("CSpeedTreeRT::SetFrondTextureCoords");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SaveUserData definition
void CSpeedTreeRT::SaveUserData(CTreeFileAccess* pFile) const
{
if (m_pUserData)
{
pFile->SaveToken(File_BeginUserData);
pFile->SaveToken(File_UserData);
pFile->SaveString(string(m_pUserData));
pFile->SaveToken(File_EndUserData);
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseUserData definition
void CSpeedTreeRT::ParseUserData(CTreeFileAccess* pFile)
{
int nToken = pFile->ParseToken( );
do
{
switch (nToken)
{
case File_UserData:
m_pUserData = CopyUserData(pFile->ParseString( ).c_str( ));
break;
}
nToken = pFile->ParseToken( );
} while (nToken != File_EndUserData);
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetUserData definition
const char* CSpeedTreeRT::GetUserData(void) const
{
return m_pUserData;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetupHorizontalBillboard definition
void CSpeedTreeRT::SetupHorizontalBillboard(void)
{
if (m_pEmbeddedTexCoords && m_bHorizontalBillboard)
{
float fHalfHeight = (m_pTreeSizes[2] + m_pTreeSizes[5]) * 0.5f;
// ccw from max x, max y to match the stored tex coords
m_afHorizontalCoords[0] = m_pTreeSizes[3];
m_afHorizontalCoords[1] = m_pTreeSizes[4];
m_afHorizontalCoords[2] = fHalfHeight;
m_afHorizontalCoords[3] = m_pTreeSizes[0];
m_afHorizontalCoords[4] = m_pTreeSizes[4];
m_afHorizontalCoords[5] = fHalfHeight;
m_afHorizontalCoords[6] = m_pTreeSizes[0];
m_afHorizontalCoords[7] = m_pTreeSizes[1];
m_afHorizontalCoords[8] = fHalfHeight;
m_afHorizontalCoords[9] = m_pTreeSizes[3];
m_afHorizontalCoords[10] = m_pTreeSizes[1];
m_afHorizontalCoords[11] = fHalfHeight;
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::GetTextureFlip definition
bool CSpeedTreeRT::GetTextureFlip(void)
{
return m_bTextureFlip;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetTextureFlip definition
void CSpeedTreeRT::SetTextureFlip(bool bFlag)
{
m_bTextureFlip = bFlag;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::CopyUserData definition
//
// unsigned int uiSize does NOT include the null terminator
char* CSpeedTreeRT::CopyUserData(const char* pOrig)
{
int nLength = strlen(pOrig);
char* pCopy = new char[nLength + 1];
memcpy(pCopy, pOrig, nLength + 1);
return pCopy;
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetBranchTextureFilename definition
void CSpeedTreeRT::SetBranchTextureFilename(const char* pFilename)
{
st_assert(m_pEngine);
try
{
if (m_pEngine->TransientDataIntact( ))
m_pEngine->SetBranchTextureFilename(pFilename);
else
SetError("SetBranchTextureFilename() has no effect after DeleteTransientData() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetBranchTextureFilename")
SpeedTreeCatchAll("CSpeedTreeRT::SetBranchTextureFilename");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetLeafTextureFilename definition
void CSpeedTreeRT::SetLeafTextureFilename(unsigned int nLeafMapIndex, const char* pFilename)
{
st_assert(m_pEngine);
try
{
if (m_pEngine->TransientDataIntact( ))
m_pEngine->SetLeafTextureFilename(nLeafMapIndex, pFilename);
else
SetError("SetLeafTextureFilename() has no effect after DeleteTransientData() has been called");
}
SpeedTreeCatch("CSpeedTreeRT::SetLeafTextureFilename")
SpeedTreeCatchAll("CSpeedTreeRT::SetLeafTextureFilename");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SetFrondTextureFilename definition
void CSpeedTreeRT::SetFrondTextureFilename(unsigned int nFrondMapIndex, const char* pFilename)
{
st_assert(m_pFrondEngine);
try
{
m_pFrondEngine->SetTextureFilename(nFrondMapIndex, pFilename);
}
SpeedTreeCatch("CSpeedTreeRT::SetFrondTextureFilename")
SpeedTreeCatchAll("CSpeedTreeRT::SetFrondTextureFilename");
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::SaveSupplementalTexCoordInfo definition
void CSpeedTreeRT::SaveSupplementalTexCoordInfo(CTreeFileAccess* pFile) const
{
if (m_pEmbeddedTexCoords)
{
pFile->SaveToken(File_BeginSupplementalTexCoordInfo);
pFile->SaveToken(File_SupplementalCompositeFilename);
pFile->SaveString(m_pEmbeddedTexCoords->m_strCompositeFilename);
pFile->SaveToken(File_SupplementalHorizontalBillboard);
pFile->SaveBool(m_bHorizontalBillboard);
pFile->SaveToken(File_Supplemental360Billboard);
pFile->SaveBool(m_b360Billboard);
pFile->SaveToken(File_SupplementalShadowTexCoords);
for (int i = 0; i < 8; ++i)
pFile->SaveFloat(m_pEmbeddedTexCoords->m_afShadowTexCoords[i]);
pFile->SaveToken(File_EndSupplementalTexCoordInfo);
}
}
///////////////////////////////////////////////////////////////////////
// CSpeedTreeRT::ParseSupplementalTexCoordInfo definition
void CSpeedTreeRT::ParseSupplementalTexCoordInfo(CTreeFileAccess* pFile)
{
if (m_pEmbeddedTexCoords)
{
int nToken = pFile->ParseToken( );
do
{
switch (nToken)
{
case File_SupplementalCompositeFilename:
{
CIdvFilename cFile = pFile->ParseString( );
m_pEmbeddedTexCoords->m_strCompositeFilename = cFile.NoPath( );
}
break;
case File_SupplementalHorizontalBillboard:
{
m_bHorizontalBillboard = pFile->ParseBool( );
}
break;
case File_Supplemental360Billboard:
{
m_b360Billboard = pFile->ParseBool( );
}
break;
case File_SupplementalShadowTexCoords:
{
for (int i = 0; i < 8; ++i)
m_pEmbeddedTexCoords->m_afShadowTexCoords[i] = pFile->ParseFloat( );
}
break;
default:
throw(IdvFileError("malformed texture coord info"));
}
if (pFile->EndOfFile( ))
throw(IdvFileError("premature end of file reached parsing texture coordinate info"));
else
nToken = pFile->ParseToken( );
} while (nToken != File_EndSupplementalTexCoordInfo);
}
}