Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
617 lines
18 KiB
C++
617 lines
18 KiB
C++
#include "gosFXHeaders.hpp"
|
|
|
|
//==========================================================================//
|
|
// File: gosFX_ParticleCloud.cpp //
|
|
// Project: gosFX //
|
|
// Contents: Base gosFX::ParticleCloud Component //
|
|
//--------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// 09/28/98 JTR Created //
|
|
// //
|
|
//--------------------------------------------------------------------------//
|
|
// Copyright (C) 1997-1998, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//==========================================================================//
|
|
//
|
|
//############################################################################
|
|
//######################## gosFX::ParticleCloud__Specification #############################
|
|
//############################################################################
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::ParticleCloud__Specification::ParticleCloud__Specification(
|
|
Stuff::RegisteredClass::ClassID class_id,
|
|
Stuff::MemoryStream *stream,
|
|
int gfx_version
|
|
):
|
|
Effect__Specification(class_id, stream, gfx_version)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(stream);
|
|
|
|
//
|
|
//-------------------
|
|
// Load in the curves
|
|
//-------------------
|
|
//
|
|
m_startingPopulation.Load(stream, gfx_version);
|
|
m_particlesPerSecond.Load(stream, gfx_version);
|
|
m_emitterSizeX.Load(stream, gfx_version);
|
|
m_emitterSizeY.Load(stream, gfx_version);
|
|
m_emitterSizeZ.Load(stream, gfx_version);
|
|
m_minimumDeviation.Load(stream, gfx_version);
|
|
m_maximumDeviation.Load(stream, gfx_version);
|
|
m_startingSpeed.Load(stream, gfx_version);
|
|
m_pLifeSpan.Load(stream, gfx_version);
|
|
m_pEtherVelocityX.Load(stream, gfx_version);
|
|
m_pEtherVelocityY.Load(stream, gfx_version);
|
|
m_pEtherVelocityZ.Load(stream, gfx_version);
|
|
m_pAccelerationX.Load(stream, gfx_version);
|
|
m_pAccelerationY.Load(stream, gfx_version);
|
|
m_pAccelerationZ.Load(stream, gfx_version);
|
|
m_pDrag.Load(stream, gfx_version);
|
|
m_pRed.Load(stream, gfx_version);
|
|
m_pGreen.Load(stream, gfx_version);
|
|
m_pBlue.Load(stream, gfx_version);
|
|
m_pAlpha.Load(stream, gfx_version);
|
|
|
|
*stream >> m_maxParticleCount;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::ParticleCloud__Specification::ParticleCloud__Specification(
|
|
Stuff::RegisteredClass::ClassID class_id
|
|
):
|
|
Effect__Specification(class_id)
|
|
{
|
|
m_maxParticleCount = 0;
|
|
Check_Pointer(this);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::ParticleCloud__Specification::~ParticleCloud__Specification()
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud__Specification::Save(Stuff::MemoryStream *stream)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
BaseClass::Save(stream);
|
|
|
|
//
|
|
//----------------
|
|
// Save our curves
|
|
//----------------
|
|
//
|
|
m_startingPopulation.Save(stream);
|
|
m_particlesPerSecond.Save(stream);
|
|
m_emitterSizeX.Save(stream);
|
|
m_emitterSizeY.Save(stream);
|
|
m_emitterSizeZ.Save(stream);
|
|
m_minimumDeviation.Save(stream);
|
|
m_maximumDeviation.Save(stream);
|
|
m_startingSpeed.Save(stream);
|
|
m_pLifeSpan.Save(stream);
|
|
m_pEtherVelocityX.Save(stream);
|
|
m_pEtherVelocityY.Save(stream);
|
|
m_pEtherVelocityZ.Save(stream);
|
|
m_pAccelerationX.Save(stream);
|
|
m_pAccelerationY.Save(stream);
|
|
m_pAccelerationZ.Save(stream);
|
|
m_pDrag.Save(stream);
|
|
m_pRed.Save(stream);
|
|
m_pGreen.Save(stream);
|
|
m_pBlue.Save(stream);
|
|
m_pAlpha.Save(stream);
|
|
|
|
*stream << m_maxParticleCount;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud__Specification::BuildDefaults()
|
|
{
|
|
|
|
Check_Object(this);
|
|
BaseClass::BuildDefaults();
|
|
|
|
m_startingPopulation.SetCurve(0.0f);
|
|
m_particlesPerSecond.SetCurve(5.0f);
|
|
|
|
m_emitterSizeX.SetCurve(0.0f);
|
|
m_emitterSizeY.SetCurve(0.0f);
|
|
m_emitterSizeZ.SetCurve(0.0f);
|
|
|
|
m_minimumDeviation.SetCurve(0.0f);
|
|
m_maximumDeviation.SetCurve(0.0f);
|
|
|
|
m_startingSpeed.m_ageCurve.SetCurve(1.0f);
|
|
m_startingSpeed.m_seeded = false;
|
|
m_startingSpeed.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pLifeSpan.m_ageCurve.SetCurve(1.0f);
|
|
m_pLifeSpan.m_seeded = false;
|
|
m_pLifeSpan.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pAccelerationX.m_ageCurve.SetCurve(0.0f);
|
|
m_pAccelerationX.m_seeded = false;
|
|
m_pAccelerationX.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pAccelerationY.m_ageCurve.SetCurve(0.0f);
|
|
m_pAccelerationY.m_seeded = false;
|
|
m_pAccelerationY.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pAccelerationZ.m_ageCurve.SetCurve(0.0f);
|
|
m_pAccelerationZ.m_seeded = false;
|
|
m_pAccelerationZ.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pEtherVelocityX.m_ageCurve.SetCurve(0.0f);
|
|
m_pEtherVelocityX.m_seeded = false;
|
|
m_pEtherVelocityX.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pEtherVelocityY.m_ageCurve.SetCurve(0.0f);
|
|
m_pEtherVelocityY.m_seeded = false;
|
|
m_pEtherVelocityY.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pEtherVelocityZ.m_ageCurve.SetCurve(0.0f);
|
|
m_pEtherVelocityZ.m_seeded = false;
|
|
m_pEtherVelocityZ.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pDrag.m_ageCurve.SetCurve(0.0f);
|
|
m_pDrag.m_seeded = false;
|
|
m_pDrag.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pRed.m_ageCurve.SetCurve(1.0f);
|
|
m_pRed.m_seeded = false;
|
|
m_pRed.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pGreen.m_ageCurve.SetCurve(1.0f);
|
|
m_pGreen.m_seeded = false;
|
|
m_pGreen.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pBlue.m_ageCurve.SetCurve(1.0f);
|
|
m_pBlue.m_seeded = false;
|
|
m_pBlue.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pAlpha.m_ageCurve.SetCurve(1.0f);
|
|
m_pAlpha.m_seeded = false;
|
|
m_pAlpha.m_seedCurve.SetCurve(1.0f);
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
bool
|
|
gosFX::ParticleCloud__Specification::IsDataValid(bool fix_data)
|
|
{
|
|
|
|
Check_Object(this);
|
|
Stuff::Scalar min,max;
|
|
m_pLifeSpan.ExpensiveComputeRange(&min,&max);
|
|
if(min<0.0f)
|
|
{
|
|
if(fix_data)
|
|
{
|
|
m_pLifeSpan.m_ageCurve.SetCurve(1.0f);
|
|
m_pLifeSpan.m_seeded = false;
|
|
m_pLifeSpan.m_seedCurve.SetCurve(1.0f);
|
|
PAUSE(("Warning: Curve \"pLifeSpan\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
m_startingPopulation.ExpensiveComputeRange(&min,&max);
|
|
if(min<0.0f)
|
|
if(fix_data)
|
|
{
|
|
m_startingPopulation.SetCurve(0.0f);
|
|
PAUSE(("Warning: Curve \"startingPopulation\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
|
|
}
|
|
else
|
|
return false;
|
|
|
|
if(m_maxParticleCount<1)
|
|
if(fix_data)
|
|
{
|
|
m_maxParticleCount=1;
|
|
PAUSE(("Warning: Value \"maxParticleCount\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return BaseClass::IsDataValid(fix_data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud__Specification::Copy(ParticleCloud__Specification *spec)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(spec);
|
|
|
|
BaseClass::Copy(spec);
|
|
|
|
//
|
|
//----------------
|
|
// Copy the curves
|
|
//----------------
|
|
//
|
|
gos_PushCurrentHeap(Heap);
|
|
m_startingPopulation = spec->m_startingPopulation;
|
|
m_particlesPerSecond = spec->m_particlesPerSecond;
|
|
m_emitterSizeX = spec->m_emitterSizeX;
|
|
m_emitterSizeY = spec->m_emitterSizeY;
|
|
m_emitterSizeZ = spec->m_emitterSizeZ;
|
|
m_minimumDeviation = spec->m_minimumDeviation;
|
|
m_maximumDeviation = spec->m_maximumDeviation;
|
|
m_startingSpeed = spec->m_startingSpeed;
|
|
m_pLifeSpan = spec->m_pLifeSpan;
|
|
m_pEtherVelocityX = spec->m_pEtherVelocityX;
|
|
m_pEtherVelocityY = spec->m_pEtherVelocityY;
|
|
m_pEtherVelocityZ = spec->m_pEtherVelocityZ;
|
|
m_pAccelerationX = spec->m_pAccelerationX;
|
|
m_pAccelerationY = spec->m_pAccelerationY;
|
|
m_pAccelerationZ = spec->m_pAccelerationZ;
|
|
m_pDrag = spec->m_pDrag;
|
|
m_pRed = spec->m_pRed;
|
|
m_pGreen = spec->m_pGreen;
|
|
m_pBlue = spec->m_pBlue;
|
|
m_pAlpha = spec->m_pAlpha;
|
|
m_maxParticleCount = spec->m_maxParticleCount;
|
|
gos_PopCurrentHeap();
|
|
}
|
|
|
|
//############################################################################
|
|
//############################ gosFX::ParticleCloud ###############################
|
|
//############################################################################
|
|
|
|
gosFX::ParticleCloud::ClassData*
|
|
gosFX::ParticleCloud::DefaultData = NULL;
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud::InitializeClass()
|
|
{
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
ParticleCloudClassID,
|
|
"gosFX::ParticleCloud",
|
|
BaseClass::DefaultData,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Register_Object(DefaultData);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud::TerminateClass()
|
|
{
|
|
Check_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::ParticleCloud::ParticleCloud(
|
|
ClassData *class_data,
|
|
Specification *spec,
|
|
unsigned flags
|
|
):
|
|
Effect(class_data, spec, flags)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(spec);
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Set up the data pointers into the channel block
|
|
//------------------------------------------------
|
|
//
|
|
m_data.SetLength(spec->m_maxParticleCount*spec->m_totalParticleSize);
|
|
|
|
//
|
|
//-------------------------------
|
|
// Set up an empty particle cloud
|
|
//-------------------------------
|
|
//
|
|
m_activeParticleCount = 0;
|
|
m_birthAccumulator = 0.0f;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud::Start(ExecuteInfo *info)
|
|
{
|
|
Check_Object(this);
|
|
Check_Pointer(info);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Let effect initialize, then figure out how many particles we want to make
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
BaseClass::Start(info);
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Stuff::Scalar newbies =
|
|
spec->m_startingPopulation.ComputeValue(m_age, m_seed);
|
|
Min_Clamp(newbies, 0.0f);
|
|
m_birthAccumulator += newbies;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
bool gosFX::ParticleCloud::Execute(ExecuteInfo *info)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(info);
|
|
Verify(IsExecuted());
|
|
GFX_LOGIC("Execute::ParticleCloud");
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we were given a new matrix, see if we have a parent. If so,
|
|
// concatenate the two and figure out its inverse. If no parent, then
|
|
// just invert the new matrix, otherwise just use the existing one
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Stuff::LinearMatrix4D new_world_to_local;
|
|
Stuff::LinearMatrix4D *matrix = NULL;
|
|
int sim_mode = GetSimulationMode();
|
|
if (sim_mode == DynamicWorldSpaceSimulationMode)
|
|
{
|
|
Stuff::LinearMatrix4D local_to_world;
|
|
local_to_world.Multiply(m_localToParent, *info->m_parentToWorld);
|
|
new_world_to_local.Invert(local_to_world);
|
|
matrix = &new_world_to_local;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// Figure out the birth rate and request the new particles
|
|
//--------------------------------------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Stuff::Scalar dT =
|
|
static_cast<Stuff::Scalar>(info->m_time - m_lastRan);
|
|
Verify(dT >= 0.0f);
|
|
if (m_age + dT * m_ageRate >= 1.0f)
|
|
m_birthAccumulator = 0.0f;
|
|
else
|
|
{
|
|
Stuff::Scalar new_life =
|
|
spec->m_particlesPerSecond.ComputeValue(m_age, m_seed);
|
|
Min_Clamp(new_life, 0.0f);
|
|
m_birthAccumulator += dT * new_life;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Deal with all the active particles
|
|
//-----------------------------------
|
|
//
|
|
int i;
|
|
int last_real = -1;
|
|
for (i = 0; i < m_activeParticleCount; i++)
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If the particle is active, age it and if it is not yet time to die,
|
|
// go to the next particle, otherwise kill it
|
|
//--------------------------------------------------------------------
|
|
//
|
|
GFX_LOGIC("Execute::ParticleCloud::AnimateParticle");
|
|
Particle *particle = GetParticle(i);
|
|
Check_Object(particle);
|
|
if (particle->m_age < 1.0f)
|
|
{
|
|
particle->m_age += dT*particle->m_ageRate;
|
|
if (AnimateParticle(i, matrix, info->m_time))
|
|
{
|
|
last_real = i;
|
|
continue;
|
|
}
|
|
DestroyParticle(i);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If there are new particles to be born, go ahead and create them now
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (m_birthAccumulator >= 1.0f)
|
|
{
|
|
Stuff::Point3D translation;
|
|
CreateNewParticle(i, &translation);
|
|
if (AnimateParticle(i, matrix, info->m_time))
|
|
last_real = i;
|
|
else
|
|
DestroyParticle(i);
|
|
m_birthAccumulator -= 1.0f;
|
|
}
|
|
}
|
|
m_activeParticleCount = last_real + 1;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If there are still new particles to be born, then we must try to grow
|
|
// the active particle count
|
|
//----------------------------------------------------------------------
|
|
//
|
|
while (
|
|
m_birthAccumulator >= 1.0f
|
|
&& m_activeParticleCount < spec->m_maxParticleCount
|
|
)
|
|
{
|
|
GFX_LOGIC("Execute::ParticleCloud::CreateParticle");
|
|
i = m_activeParticleCount++;
|
|
Stuff::Point3D translation;
|
|
CreateNewParticle(i, &translation);
|
|
if (!AnimateParticle(i, matrix, info->m_time))
|
|
{
|
|
DestroyParticle(i);
|
|
--m_activeParticleCount;
|
|
}
|
|
m_birthAccumulator -= 1.0f;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------
|
|
// Only allow fractional births to carry over to next frame
|
|
//---------------------------------------------------------
|
|
//
|
|
m_birthAccumulator -= static_cast<Stuff::Scalar>(floor(m_birthAccumulator));
|
|
|
|
//
|
|
//----------------------------
|
|
// Now let effect do its thing
|
|
//----------------------------
|
|
//
|
|
return BaseClass::Execute(info);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
bool gosFX::ParticleCloud::HasFinished()
|
|
{
|
|
Check_Object(this);
|
|
return BaseClass::HasFinished() && (m_activeParticleCount == 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void gosFX::ParticleCloud::Kill()
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Destroy all the particles and set up an empty particle cloud
|
|
//-------------------------------------------------------------
|
|
//
|
|
for(int i=0; i < m_activeParticleCount; i++)
|
|
DestroyParticle(i);
|
|
m_activeParticleCount = 0;
|
|
m_birthAccumulator = 0.0f;
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Now let the base effect handle stopping
|
|
//----------------------------------------
|
|
//
|
|
BaseClass::Kill();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud::CreateNewParticle(
|
|
unsigned index,
|
|
Stuff::Point3D *translation
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Figure out the age and age rate of the new particle
|
|
//----------------------------------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Particle *particle = GetParticle(index);
|
|
Check_Object(particle);
|
|
particle->m_age = 0.0f;
|
|
Stuff::Scalar min_seed =
|
|
spec->m_minimumChildSeed.ComputeValue(m_age, m_seed);
|
|
Stuff::Scalar seed_range =
|
|
spec->m_maximumChildSeed.ComputeValue(m_age, m_seed) - min_seed;
|
|
Stuff::Scalar seed =
|
|
Stuff::Random::GetFraction()*seed_range + min_seed;
|
|
Clamp(seed, 0.0f, 1.0f);
|
|
particle->m_seed = seed;
|
|
Stuff::Scalar lifetime =
|
|
spec->m_pLifeSpan.ComputeValue(m_age, seed);
|
|
Min_Clamp(lifetime, 0.0333333f);
|
|
particle->m_ageRate = 1.0f / lifetime;
|
|
|
|
//
|
|
//--------------------------------
|
|
// Figure out the initial position
|
|
//--------------------------------
|
|
//
|
|
Stuff::YawPitchRange
|
|
initial_p(
|
|
Stuff::Random::GetFraction() * Stuff::Two_Pi,
|
|
Stuff::Random::GetFraction() * Stuff::Pi - Stuff::Pi_Over_2,
|
|
Stuff::Random::GetFraction()
|
|
);
|
|
Stuff::Vector3D position(initial_p);
|
|
translation->x =
|
|
position.x * spec->m_emitterSizeX.ComputeValue(m_age, seed);
|
|
translation->y =
|
|
position.y * spec->m_emitterSizeY.ComputeValue(m_age, seed);
|
|
translation->z =
|
|
position.z * spec->m_emitterSizeZ.ComputeValue(m_age, seed);
|
|
|
|
//
|
|
//--------------------------------
|
|
// Figure out the initial velocity
|
|
//--------------------------------
|
|
//
|
|
Stuff::Scalar pitch_min =
|
|
spec->m_minimumDeviation.ComputeValue(m_age, seed);
|
|
Stuff::Scalar pitch_range =
|
|
spec->m_maximumDeviation.ComputeValue(m_age, seed) - pitch_min;
|
|
if (pitch_range < 0.0f)
|
|
pitch_range = 0.0f;
|
|
pitch_min +=
|
|
pitch_range * Stuff::Random::GetFraction() - Stuff::Pi_Over_2;
|
|
Stuff::YawPitchRange
|
|
initial_v(
|
|
Stuff::Random::GetFraction() * Stuff::Two_Pi,
|
|
pitch_min,
|
|
spec->m_startingSpeed.ComputeValue(m_age, seed)
|
|
);
|
|
particle->m_localLinearVelocity = initial_v;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void gosFX::ParticleCloud::DestroyParticle(unsigned index)
|
|
{
|
|
Particle *particle = GetParticle(index);
|
|
Check_Object(particle);
|
|
particle->m_age = 1.0f;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::ParticleCloud::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|