📄 fxfoliagereplicator.cc
字号:
// Initialise parents' persistent fields.
Parent::initPersistFields();
// Add out own persistent fields.
addGroup( "Debugging" );
addField( "UseDebugInfo", TypeBool, Offset( mFieldData.mUseDebugInfo, fxFoliageReplicator ) );
addField( "DebugBoxHeight", TypeF32, Offset( mFieldData.mDebugBoxHeight, fxFoliageReplicator ) );
addField( "HideFoliage", TypeBool, Offset( mFieldData.mHideFoliage, fxFoliageReplicator ) );
addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxFoliageReplicator ) );
addField( "PlacementAreaHeight",TypeS32, Offset( mFieldData.mPlacementBandHeight, fxFoliageReplicator ) );
addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxFoliageReplicator ) );
endGroup( "Debugging" );
addGroup( "Media" );
addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxFoliageReplicator ) );
addField( "FoliageFile", TypeFilename, Offset( mFieldData.mFoliageFile, fxFoliageReplicator ) );
addField( "FoliageCount", TypeS32, Offset( mFieldData.mFoliageCount, fxFoliageReplicator ) );
addField( "FoliageRetries", TypeS32, Offset( mFieldData.mFoliageRetries, fxFoliageReplicator ) );
endGroup( "Media" );
addGroup( "Area" );
addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxFoliageReplicator ) );
addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxFoliageReplicator ) );
addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxFoliageReplicator ) );
addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxFoliageReplicator ) );
endGroup( "Area" );
addGroup( "Dimensions" );
addField( "MinWidth", TypeF32, Offset( mFieldData.mMinWidth, fxFoliageReplicator ) );
addField( "MaxWidth", TypeF32, Offset( mFieldData.mMaxWidth, fxFoliageReplicator ) );
addField( "MinHeight", TypeF32, Offset( mFieldData.mMinHeight, fxFoliageReplicator ) );
addField( "MaxHeight", TypeF32, Offset( mFieldData.mMaxHeight, fxFoliageReplicator ) );
addField( "FixAspectRatio", TypeBool, Offset( mFieldData.mFixAspectRatio, fxFoliageReplicator ) );
addField( "FixSizeToMax", TypeBool, Offset( mFieldData.mFixSizeToMax, fxFoliageReplicator ) );
addField( "OffsetZ", TypeF32, Offset( mFieldData.mOffsetZ, fxFoliageReplicator ) );
addField( "RandomFlip", TypeBool, Offset( mFieldData.mRandomFlip, fxFoliageReplicator ) );
endGroup( "Dimensions" );
addGroup( "Culling" );
addField( "UseCulling", TypeBool, Offset( mFieldData.mUseCulling, fxFoliageReplicator ) );
addField( "CullResolution", TypeS32, Offset( mFieldData.mCullResolution, fxFoliageReplicator ) );
addField( "ViewDistance", TypeF32, Offset( mFieldData.mViewDistance, fxFoliageReplicator ) );
addField( "ViewClosest", TypeF32, Offset( mFieldData.mViewClosest, fxFoliageReplicator ) );
addField( "FadeInRegion", TypeF32, Offset( mFieldData.mFadeInRegion, fxFoliageReplicator ) );
addField( "FadeOutRegion", TypeF32, Offset( mFieldData.mFadeOutRegion, fxFoliageReplicator ) );
addField( "AlphaCutoff", TypeF32, Offset( mFieldData.mAlphaCutoff, fxFoliageReplicator ) );
addField( "GroundAlpha", TypeF32, Offset( mFieldData.mGroundAlpha, fxFoliageReplicator ) );
endGroup( "Culling" );
addGroup( "Animation" );
addField( "SwayOn", TypeBool, Offset( mFieldData.mSwayOn, fxFoliageReplicator ) );
addField( "SwaySync", TypeBool, Offset( mFieldData.mSwaySync, fxFoliageReplicator ) );
addField( "SwayMagSide", TypeF32, Offset( mFieldData.mSwayMagnitudeSide, fxFoliageReplicator ) );
addField( "SwayMagFront", TypeF32, Offset( mFieldData.mSwayMagnitudeFront, fxFoliageReplicator ) );
addField( "MinSwayTime", TypeF32, Offset( mFieldData.mMinSwayTime, fxFoliageReplicator ) );
addField( "MaxSwayTime", TypeF32, Offset( mFieldData.mMaxSwayTime, fxFoliageReplicator ) );
endGroup( "Animation" );
addGroup( "Lighting" );
addField( "LightOn", TypeBool, Offset( mFieldData.mLightOn, fxFoliageReplicator ) );
addField( "LightSync", TypeBool, Offset( mFieldData.mLightSync, fxFoliageReplicator ) );
addField( "MinLuminance", TypeF32, Offset( mFieldData.mMinLuminance, fxFoliageReplicator ) );
addField( "MaxLuminance", TypeF32, Offset( mFieldData.mMaxLuminance, fxFoliageReplicator ) );
addField( "LightTime", TypeF32, Offset( mFieldData.mLightTime, fxFoliageReplicator ) );
endGroup( "Lighting" );
addGroup( "Restrictions" );
addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxFoliageReplicator ) );
addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxFoliageReplicator ) );
addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxFoliageReplicator ) );
addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxFoliageReplicator ) );
addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxFoliageReplicator ) );
addField( "AllowedTerrainSlope",TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxFoliageReplicator ) );
endGroup( "Restrictions" );
}
//------------------------------------------------------------------------------
void fxFoliageReplicator::CreateFoliage(void)
{
F32 HypX, HypY;
F32 Angle;
U32 RelocationRetry;
Point3F FoliagePosition;
Point3F FoliageStart;
Point3F FoliageEnd;
Point3F FoliageScale;
bool CollisionResult;
RayInfo RayEvent;
// Let's get a minimum bounding volume.
Point3F MinPoint( -0.5, -0.5, -0.5 );
Point3F MaxPoint( 0.5, 0.5, 0.5 );
// Check Host.
AssertFatal(isClientObject(), "Trying to create Foliage on Server, this is bad!")
// Cannot continue without Foliage Texture!
if (mFieldData.mFoliageFile == "") return;
// Check that we can position somewhere!
if (!( mFieldData.mAllowOnTerrain ||
mFieldData.mAllowOnInteriors ||
mFieldData.mAllowStatics ||
mFieldData.mAllowOnWater))
{
// Problem ...
Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not place Foliage, All alloweds are off!");
// Return here.
return;
}
// Destroy Foliage if we've already got some.
if (mCurrentFoliageCount != 0) DestroyFoliage();
// Inform the user if culling has been disabled!
if (!mFieldData.mUseCulling)
{
// Console Output.
Con::printf("fxFoliageReplicator - Culling has been disabled!");
}
// ----------------------------------------------------------------------------------------------------------------------
// > Calculate the Potential Foliage Nodes Required to achieve the selected culling resolution.
// > Populate Quad-tree structure to depth determined by culling resolution.
//
// A little explanation is called for here ...
//
// The approach to this problem has been choosen to make it *much* easier for
// the user to control the quad-tree culling resolution. The user enters a single
// world-space value 'mCullResolution' which controls the highest resolution at
// which the replicator will check visibility culling.
//
// example: If 'mCullResolution' is 32 and the size of the replicated area is 128 radius
// (256 diameter) then this results in the replicator creating a quad-tree where
// there are 256/32 = 8x8 blocks. Each of these can be checked to see if they
// reside within the viewing frustum and if not then they get culled therefore
// removing the need to parse all the billboards that occcupy that region.
// Most of the time you will get better than this as the culling algorithm will
// check the culling pyramid from the top to bottom e.g. the follow 'blocks'
// will be checked:-
//
// 1 x 256 x 256 (All of replicated area)
// 4 x 128 x 128 (4 corners of above)
// 16 x 64 x 64 (16 x 4 corners of above)
// etc.
//
//
// 1. First-up, the replicator needs to create a fixed-list of quad-tree nodes to work with.
//
// To calculate this we take the largest outer-radius value set in the replicator and
// calculate how many quad-tree levels are required to achieve the selected 'mCullResolution'.
// One of the initial problems is that the replicator has seperate radii values for X & Y.
// This can lead to a culling resolution smaller in one axis than the other if there is a
// difference between the Outer-Radii. Unfortunately, we just live with this as there is
// not much we can do here if we still want to allow the user to have this kind of
// elliptical placement control.
//
// To calculate the number of nodes needed we using the following equation:-
//
// Note:- We are changing the Logarithmic bases from 10 -> 2 ... grrrr!
//
// Cr = mCullResolution
// Rs = Maximum Radii Diameter
//
//
// ( Log10( Rs / Cr ) )
// int ( ---------------- + 0.5 )
// ( Log10( 2 ) )
//
// ---------|
// \
// \ n
// / 4
// /
// ---------|
// n = 0
//
//
// So basically we calculate the number of blocks in 1D at the highest resolution, then
// calculate the inverse exponential (base 2 - 1D) to achieve that quantity of blocks.
// We round that upto the next highest integer = e. We then sum 4 to the power 0->e
// which gives us the correct number of nodes required. e is also stored as the starting
// level value for populating the quad-tree (see 3. below).
//
// 2. We then proceed to calculate the billboard positions as normal and calculate and assign
// each billboard a basic volume (rather than treat each as a point). We need to take into
// account possible front/back swaying as well as the basic plane dimensions here.
// When all the billboards have been choosen we then proceed to populate the quad-tree.
//
// 3. To populate the quad-tree we start with a box which completely encapsulates the volume
// occupied by all the billboards and enter into a recursive procedure to process that node.
// Processing this node involves splitting it into quadrants in X/Y untouched (for now).
// We then find candidate billboards with each of these quadrants searching using the
// current subset of shapes from the parent (this reduces the searching to a minimum and
// is very efficient).
//
// If a quadrant does not enclose any billboards then the node is dropped otherwise it
// is processed again using the same procedure.
//
// This happens until we have recursed through the maximum number of levels as calculated
// using the summation max (see equation above). When level 0 is reached, the current list
// of enclosed objects is stored within the node (for the rendering algorithm).
//
// 4. When this is complete we have finished here. The next stage is when rendering takes place.
// An algorithm steps through the quad-tree from the top and does visibility culling on
// each box (with respect to the viewing frustum) and culls as appropriate. If the box is
// visible then the next level is checked until we reach level 0 where the node contains
// a complete subset of billboards enclosed by the visible box.
//
//
// Using the above algorithm we can now generate *massive* quantities of billboards and (using the
// appropriate 'mCullResolution') only visible blocks of billboards will be processed.
//
// - Melv.
//
// ----------------------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------------
// Step 1.
// ----------------------------------------------------------------------------------------------------------------------
// Calculate the maximum dimension.
F32 MaxDimension = 2 * ( (mFieldData.mOuterRadiusX > mFieldData.mOuterRadiusY) ? mFieldData.mOuterRadiusX : mFieldData.mOuterRadiusY );
// Let's check that our cull resolution is not greater than half our maximum dimension (and less than 1).
if (mFieldData.mCullResolution > (MaxDimension/2) || mFieldData.mCullResolution < 8)
{
// Problem ...
Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could create Foliage, invalid Culling Resolution!");
Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Culling Resolution *must* be >=8 or <= %0.2f!", (MaxDimension/2));
// Return here.
return;
}
// Take first Timestamp.
F32 mStartCreationTime = Platform::getRealMilliseconds();
// Calculate the quad-tree levels needed for selected 'mCullResolution'.
mQuadTreeLevels = (U32)(mCeil(mLog( MaxDimension / mFieldData.mCullResolution ) / mLog( 2.0f )));
// Calculate the number of potential nodes required.
mPotentialFoliageNodes = 0;
for (U32 n = 0; n <= mQuadTreeLevels; n++)
mPotentialFoliageNodes += (U32)(mCeil(mPow(4.0f, n))); // Ceil to be safe!
// ----------------------------------------------------------------------------------------------------------------------
// Step 2.
// ----------------------------------------------------------------------------------------------------------------------
// Set Seed.
RandomGen.setSeed(mFieldData.mSeed);
// Have we setup the Trig Tables?
if (!mTrigTableInitialised)
{
F32 tIdx = 0.0f;
// No, so setup Tables.
for (U32 idx = 0; idx < 720; idx++, tIdx+=M_2PI/720.0f)
{
mCosTable[idx] = mCos(tIdx);
mSinTable[idx] = mSin(tIdx);
}
// Signal Trig Tables Initialised.
mTrigTableInitialised = true;
}
// Add Foliage.
for (U32 idx = 0; idx < mFieldData.mFoliageCount; idx++)
{
fxFoliageItem* pFoliageItem;
// Reset Relocation Retry.
RelocationRetry = mFieldData.mFoliageRetries;
// Find it a home ...
do
{
// Calculate a random offset
HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX);
HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY);
Angle = RandomGen.randF(0, M_2PI);
// Calculate the new position.
Point3F randomShapePosLocal;
randomShapePosLocal.x = HypX * mCos(Angle);
randomShapePosLocal.y = HypY * mSin(Angle);
// Transform into world space coordinates
Point3F shapePosWorld;
MatrixF objToWorld = getRenderTransform();
objToWorld.mulP(randomShapePosLocal, &shapePosWorld);
FoliagePosition = shapePosWorld;
// Initialise RayCast Search Start/End Positions.
FoliageStart = FoliageEnd = FoliagePosition;
FoliageStart.z = 2000.f;
FoliageEnd.z= -2000.f;
// Perform Ray Cast Collision on Client.
CollisionResult = gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_COLLISION_MASK, &RayEvent);
// Did we hit anything?
if (CollisionResult)
{
// For now, let's pretend we didn't get a collision.
CollisionResult = false;
// Yes, so get it's type.
U32 CollisionType = RayEvent.object->getTypeMask();
// Check Illegal Placements, fail if we hit a disallowed type.
if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) ||
((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) ||
((CollisionType & StaticTSObjectType) && !mFieldData.mAllowStatics) ||
((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue;
// If we collided with water and are not allowing on the water surface then let's find the
// terrain underneath and pass this on as the original collision else fail.
if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface &&
!gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
// We passed with flying colour so carry on.
CollisionResult = true;
}
// Invalidate if we are below Allowed Terrain Angle.
if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false;
// Wait until we get a collision.
} while(!CollisionResult && --RelocationRetry);
// Check for Relocation Problem.
if (RelocationRetry > 0)
{
// Adjust Impact point.
RayEvent.point.z += mFieldData.mOffsetZ;
// Set New Position.
FoliagePosition = RayEvent.point;
}
else
{
// Warning.
Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not find satisfactory position for Foliage!");
// Skip to next.
continue;
}
// Create our Foliage Item.
pFoliageItem = new fxFoliageItem;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -