/////////////////////////////////////////////////////////////////////// // 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 #include #include "FileAccess.h" #define FULL_VERSION #include "EvalCode.h" #include #include "SFileNameEnDe.h" #include #include "../../../../Internal/include/kfile/KStream.h" using namespace std; // static/global variables vector 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::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 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::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::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(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(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::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::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(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(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((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((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((1.0f - fLodLevel) * nNumLodLevels); if (nLevel == nNumLodLevels) nLevel--; st_assert(nLevel >= 0 && nLevel < nNumLodLevels); } else { int nNumLodLevels = GetNumLeafLodLevels( ); nLevel = static_cast((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(m_pEngine->GetNumBranchLodLevels( )); } /////////////////////////////////////////////////////////////////////// // CSpeedTreeRT::GetNumLeafLodLevels definition unsigned short CSpeedTreeRT::GetNumLeafLodLevels(void) const { return static_cast(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(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(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* 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::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(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(nLodLevel)); if (usNumStrips > 0) { pMapIndexes = new unsigned char[usNumStrips]; const unsigned short* pStripLengths = m_pFrondGeometry->GetStripLengthsPointer(static_cast(nLodLevel)); const unsigned short** pStrips = const_cast(m_pFrondGeometry->GetStripsPointer(static_cast(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(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(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((m_pEmbeddedTexCoords->m_nNumBillboards - 1) - sPrimaryLod); sSecondaryLod = static_cast((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(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((1.0f - fLodLevel) * usLodCount); sHighLod = __min(sHighLod, static_cast(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& 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); } }