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.
1143 lines
35 KiB
C++
1143 lines
35 KiB
C++
#include "gosFXHeaders.hpp"
|
|
#include <MLR\MLRIndexedTriangleCloud.hpp>
|
|
|
|
//==========================================================================//
|
|
// File: gosFX_Beam.cpp //
|
|
// Project: gosFX //
|
|
// Contents: Base gosFX::Beam 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::Beam__Specification #############################
|
|
//############################################################################
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam__Specification::Beam__Specification(
|
|
Stuff::MemoryStream *stream,
|
|
int gfx_version
|
|
):
|
|
Effect__Specification(BeamClassID, stream, gfx_version)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(stream);
|
|
|
|
//
|
|
//-------------------
|
|
// Load in the curves
|
|
//-------------------
|
|
//
|
|
m_profilesPerMeter.Load(stream, gfx_version);
|
|
m_minimumDeviation.Load(stream, gfx_version);
|
|
m_maximumDeviation.Load(stream, gfx_version);
|
|
m_pDisplacement.Load(stream, gfx_version);
|
|
m_pScale.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);
|
|
m_pUOffset.Load(stream, gfx_version);
|
|
m_pVOffset.Load(stream, gfx_version);
|
|
m_pUSize.Load(stream, gfx_version);
|
|
m_pVSize.Load(stream, gfx_version);
|
|
|
|
int type;
|
|
*stream >> m_maxProfileCount >> type >> m_insideOut >> m_randomDirections;
|
|
m_profileType = static_cast<ProfileType>(type);
|
|
BuildTemplate();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam__Specification::Beam__Specification():
|
|
Effect__Specification(BeamClassID)
|
|
{
|
|
m_maxProfileCount = 0;
|
|
m_profileType = e_Ribbon;
|
|
m_insideOut = false;
|
|
m_randomDirections = false;
|
|
Check_Pointer(this);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam__Specification::~Beam__Specification()
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam__Specification*
|
|
gosFX::Beam__Specification::Make(
|
|
Stuff::MemoryStream *stream,
|
|
int gfx_version
|
|
)
|
|
{
|
|
Check_Object(stream);
|
|
|
|
gos_PushCurrentHeap(Heap);
|
|
Beam__Specification *spec =
|
|
new gosFX::Beam__Specification(stream, gfx_version);
|
|
gos_PopCurrentHeap();
|
|
|
|
return spec;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam__Specification::Save(Stuff::MemoryStream *stream)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
BaseClass::Save(stream);
|
|
|
|
//
|
|
//----------------
|
|
// Save our curves
|
|
//----------------
|
|
//
|
|
m_profilesPerMeter.Save(stream);
|
|
m_minimumDeviation.Save(stream);
|
|
m_maximumDeviation.Save(stream);
|
|
m_pDisplacement.Save(stream);
|
|
m_pScale.Save(stream);
|
|
m_pRed.Save(stream);
|
|
m_pGreen.Save(stream);
|
|
m_pBlue.Save(stream);
|
|
m_pAlpha.Save(stream);
|
|
m_pUOffset.Save(stream);
|
|
m_pVOffset.Save(stream);
|
|
m_pUSize.Save(stream);
|
|
m_pVSize.Save(stream);
|
|
|
|
|
|
*stream << m_maxProfileCount << static_cast<int>(m_profileType);
|
|
*stream << m_insideOut;
|
|
*stream << m_randomDirections;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam__Specification::BuildDefaults()
|
|
{
|
|
|
|
Check_Object(this);
|
|
BaseClass::BuildDefaults();
|
|
|
|
m_profilesPerMeter.SetCurve(.004f);
|
|
|
|
m_minimumDeviation.SetCurve(0.0f);
|
|
m_maximumDeviation.SetCurve(0.0f);
|
|
|
|
m_pDisplacement.m_ageCurve.SetCurve(0.0f);
|
|
m_pDisplacement.m_seeded = false;
|
|
m_pDisplacement.m_seedCurve.SetCurve(1.0f);
|
|
|
|
m_pScale.m_ageCurve.SetCurve(1.0f);
|
|
m_pScale.m_seeded = false;
|
|
m_pScale.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);
|
|
|
|
m_pUOffset.SetCurve(0.0f);
|
|
m_pVOffset.SetCurve(0.0f);
|
|
m_pUSize.SetCurve(1.0f);
|
|
m_pVSize.SetCurve(1.0f);
|
|
|
|
m_maxProfileCount = 2;
|
|
m_profileType = e_Triangle;
|
|
m_insideOut = false;
|
|
m_randomDirections = false;
|
|
BuildTemplate();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
bool
|
|
gosFX::Beam__Specification::IsDataValid(bool fix_data)
|
|
{
|
|
|
|
Check_Object(this);
|
|
if(m_maxProfileCount<2)
|
|
{
|
|
if(fix_data)
|
|
{
|
|
m_maxProfileCount=2;
|
|
PAUSE(("Warning: Value \"maxProfileCount\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Make sure we don't overwrite MLR buffers
|
|
//-----------------------------------------
|
|
//
|
|
int profile_limit = MidLevelRenderer::Limits::Max_Number_Vertices_Per_Effect/m_vertices.GetLength();
|
|
if (m_maxProfileCount > profile_limit)
|
|
{
|
|
if (fix_data)
|
|
{
|
|
m_maxProfileCount = profile_limit;
|
|
PAUSE(("Warning: Max Profile Count in Effect \"%s\" is too big and has been reset",(char *)m_name));
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
Stuff::Scalar min,max;
|
|
m_pScale.ExpensiveComputeRange(&min,&max);
|
|
if (min<5.0f*Stuff::SMALL)
|
|
{
|
|
if (fix_data)
|
|
m_pScale.SetMinimum(10.0f*Stuff::SMALL);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
Stuff::Scalar max_offset, min_offset;
|
|
Stuff::Scalar max_scale, min_scale;
|
|
m_pUSize.ExpensiveComputeRange(&min_scale, &max_scale);
|
|
Stuff::Scalar lower = min_scale;
|
|
if (lower > 0.0f)
|
|
lower = 0.0f;
|
|
Stuff::Scalar upper = max_scale;
|
|
|
|
//
|
|
//------------------------------------
|
|
// Calculate the worst case UV offsets
|
|
//------------------------------------
|
|
//
|
|
m_pVOffset.ExpensiveComputeRange(&min_offset, &max_offset);
|
|
lower += min_offset;
|
|
upper += max_offset;
|
|
|
|
if (upper > 99.0f || lower < -99.0f)
|
|
{
|
|
if(fix_data)
|
|
{
|
|
m_pVOffset.SetCurve(0.0f);
|
|
PAUSE(("Warning: Curve \"VOffset\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
m_pVSize.ExpensiveComputeRange(&min_scale, &max_scale);
|
|
lower = min_scale;
|
|
if (lower > 0.0f)
|
|
lower = 0.0f;
|
|
upper = max_scale;
|
|
|
|
//
|
|
//------------------------------------
|
|
// Calculate the worst case UV offsets
|
|
//------------------------------------
|
|
//
|
|
max_offset, min_offset;
|
|
m_pUOffset.ExpensiveComputeRange(&min_offset, &max_offset);
|
|
lower += min_offset;
|
|
upper += max_offset;
|
|
|
|
if (upper > 99.0f || lower < -99.0f)
|
|
{
|
|
if(fix_data)
|
|
{
|
|
m_pUOffset.SetCurve(0.0f);
|
|
PAUSE(("Warning: Curve \"UOffset\" in Effect \"%s\" Is Out of Range and has been Reset",(char *)m_name));
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
return BaseClass::IsDataValid(fix_data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam__Specification::Copy(Beam__Specification *spec)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(spec);
|
|
|
|
BaseClass::Copy(spec);
|
|
|
|
//
|
|
//----------------
|
|
// Copy the curves
|
|
//----------------
|
|
//
|
|
gos_PushCurrentHeap(Heap);
|
|
m_profilesPerMeter = spec->m_profilesPerMeter;
|
|
m_minimumDeviation = spec->m_minimumDeviation;
|
|
m_maximumDeviation = spec->m_maximumDeviation;
|
|
m_pDisplacement = spec->m_pDisplacement;
|
|
m_pScale = spec->m_pScale;
|
|
m_pRed = spec->m_pRed;
|
|
m_pGreen = spec->m_pGreen;
|
|
m_pBlue = spec->m_pBlue;
|
|
m_pAlpha = spec->m_pAlpha;
|
|
m_pUOffset = spec->m_pUOffset;
|
|
m_pVOffset = spec->m_pVOffset;
|
|
m_pUSize = spec->m_pUSize;
|
|
m_pVSize = spec->m_pVSize;
|
|
m_maxProfileCount = spec->m_maxProfileCount;
|
|
m_profileType = spec->m_profileType;
|
|
m_vertices = spec->m_vertices;
|
|
m_uvs = spec->m_uvs;
|
|
m_insideOut = spec->m_insideOut;
|
|
m_randomDirections = spec->m_randomDirections;
|
|
gos_PopCurrentHeap();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam__Specification::BuildTemplate()
|
|
{
|
|
Check_Object(this);
|
|
|
|
switch (m_profileType)
|
|
{
|
|
case e_Ribbon:
|
|
case e_AlignedRibbon:
|
|
m_vertices.SetLength(2);
|
|
m_vertices[0] = Stuff::Vector3D(1.0f, 0.0f, 0.0f);
|
|
m_vertices[1] = Stuff::Vector3D(-1.0f, 0.0f, 0.0f);
|
|
m_uvs.SetLength(2);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_VerticalRibbon:
|
|
m_vertices.SetLength(2);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, -1.0f);
|
|
m_vertices[1] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_uvs.SetLength(2);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_Triangle:
|
|
m_vertices.SetLength(4);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_vertices[1] = Stuff::Vector3D(-0.8660254f, 0.0f, -0.5f);
|
|
m_vertices[2] = Stuff::Vector3D(0.8660254f, 0.0f, -0.5f);
|
|
m_vertices[3] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_uvs.SetLength(4);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f/3.0f);
|
|
m_uvs[2] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 2.0f/3.0f);
|
|
m_uvs[3] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_Square:
|
|
m_vertices.SetLength(5);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_vertices[1] = Stuff::Vector3D(-1.0f, 0.0f, 0.0f);
|
|
m_vertices[2] = Stuff::Vector3D(0.0f, 0.0f, -1.0f);
|
|
m_vertices[3] = Stuff::Vector3D(1.0f, 0.0f, 0.0f);
|
|
m_vertices[4] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_uvs.SetLength(5);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.25f);
|
|
m_uvs[2] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.5f);
|
|
m_uvs[3] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.75f);
|
|
m_uvs[4] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_Cross:
|
|
m_vertices.SetLength(5);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, 0.0f);
|
|
m_vertices[1] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_vertices[2] = Stuff::Vector3D(-1.0f, 0.0f, 0.0f);
|
|
m_vertices[3] = Stuff::Vector3D(0.0f, 0.0f, -1.0f);
|
|
m_vertices[4] = Stuff::Vector3D(1.0f, 0.0f, 0.0f);
|
|
m_uvs.SetLength(5);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
m_uvs[2] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
m_uvs[3] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
m_uvs[4] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_Pentagon:
|
|
m_vertices.SetLength(6);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_vertices[1] = Stuff::Vector3D(-0.9510565f, 0.0f, 0.309017f);
|
|
m_vertices[2] = Stuff::Vector3D(-0.58778525f, 0.0f, -0.8090167f);
|
|
m_vertices[3] = Stuff::Vector3D(0.58778525f, 0.0f, -0.8090167f);
|
|
m_vertices[4] = Stuff::Vector3D(0.9510565f, 0.0f, 0.309017f);
|
|
m_vertices[5] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_uvs.SetLength(6);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.2f);
|
|
m_uvs[2] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.4f);
|
|
m_uvs[3] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.6f);
|
|
m_uvs[4] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.8f);
|
|
m_uvs[5] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
|
|
case e_Hexagon:
|
|
m_vertices.SetLength(7);
|
|
m_vertices[0] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_vertices[1] = Stuff::Vector3D(-0.8660254f, 0.0f, 0.5f);
|
|
m_vertices[2] = Stuff::Vector3D(-0.8660254f, 0.0f, -0.5f);
|
|
m_vertices[3] = Stuff::Vector3D(0.0f, 0.0f, -1.0f);
|
|
m_vertices[4] = Stuff::Vector3D(0.8660254f, 0.0f, -0.5f);
|
|
m_vertices[5] = Stuff::Vector3D(0.8660254f, 0.0f, 0.5f);
|
|
m_vertices[6] = Stuff::Vector3D(0.0f, 0.0f, 1.0f);
|
|
m_uvs.SetLength(7);
|
|
m_uvs[0] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.0f);
|
|
m_uvs[1] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f/6.0f);
|
|
m_uvs[2] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 2.0f/6.0f);
|
|
m_uvs[3] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 0.5f);
|
|
m_uvs[4] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 4.0f/6.0f);
|
|
m_uvs[5] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 5.0f/6.0f);
|
|
m_uvs[6] = Stuff::Vector2DOf<Stuff::Scalar>(0.0f, 1.0f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//############################################################################
|
|
//############################ gosFX::Beam ###############################
|
|
//############################################################################
|
|
|
|
gosFX::Beam::ClassData*
|
|
gosFX::Beam::DefaultData = NULL;
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::InitializeClass()
|
|
{
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
BeamClassID,
|
|
"gosFX::Beam",
|
|
BaseClass::DefaultData,
|
|
(Effect::Factory)&Make,
|
|
(Specification::Factory)&Specification::Make
|
|
);
|
|
Check_Object(DefaultData);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::TerminateClass()
|
|
{
|
|
Check_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam::Beam(
|
|
Specification *spec,
|
|
unsigned flags
|
|
):
|
|
Effect(DefaultData, spec, flags)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(spec);
|
|
|
|
//
|
|
//----------------------------------
|
|
// Figure out how much space we need
|
|
//----------------------------------
|
|
//
|
|
m_profiles.SetLength(spec->m_maxProfileCount);
|
|
unsigned vertex_count = spec->m_vertices.GetLength();
|
|
Verify(vertex_count < 8);
|
|
unsigned index_count = (vertex_count - 1)*6;
|
|
unsigned size =
|
|
sizeof(Stuff::Point3D)
|
|
+ sizeof(Stuff::RGBAColor)
|
|
+ sizeof(Stuff::Vector2DOf<Stuff::Scalar>);
|
|
size *= vertex_count*spec->m_maxProfileCount;
|
|
size += sizeof(unsigned short)*(spec->m_maxProfileCount-1)*index_count;
|
|
|
|
//
|
|
//-----------------------
|
|
// Allocate the tube mesh
|
|
//-----------------------
|
|
//
|
|
gos_PushCurrentHeap(MidLevelRenderer::EffectHeap);
|
|
m_mesh =
|
|
new MidLevelRenderer::MLRIndexedTriangleCloud(
|
|
spec->m_maxProfileCount*(vertex_count-1)*2
|
|
);
|
|
Check_Object(m_mesh);
|
|
gos_PopCurrentHeap();
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Set up the data pointers into the channel block
|
|
//------------------------------------------------
|
|
//
|
|
m_triangleCount = 0;
|
|
m_vertexCount = 0;
|
|
m_data.SetLength(size);
|
|
m_P_vertices = Cast_Pointer(Stuff::Point3D*, &m_data[0]);
|
|
size = spec->m_maxProfileCount*vertex_count*sizeof(Stuff::Point3D);
|
|
m_P_colors = Cast_Pointer(Stuff::RGBAColor*, &m_data[size]);
|
|
size += spec->m_maxProfileCount*vertex_count*sizeof(Stuff::RGBAColor);
|
|
m_P_uvs = Cast_Pointer(Stuff::Vector2DOf<Stuff::Scalar>*, &m_data[size]);
|
|
size += spec->m_maxProfileCount*vertex_count*sizeof(Stuff::Vector2DOf<Stuff::Scalar>);
|
|
unsigned short *mesh_indices = Cast_Pointer(unsigned short*, &m_data[size]);
|
|
m_mesh->SetData(
|
|
&m_triangleCount,
|
|
&m_vertexCount,
|
|
mesh_indices,
|
|
m_P_vertices,
|
|
m_P_colors,
|
|
m_P_uvs
|
|
);
|
|
BuildMesh(mesh_indices);
|
|
|
|
//
|
|
//-------------------------------
|
|
// Set up an empty profile cloud
|
|
//-------------------------------
|
|
//
|
|
m_activeProfileCount = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::BuildMesh(unsigned short *indices)
|
|
{
|
|
Check_Object(this);
|
|
Check_Pointer(indices);
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Figure out the parameters of the mesh
|
|
//--------------------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
unsigned vertex_count = spec->m_vertices.GetLength();
|
|
Verify(vertex_count < 8);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Crosses are built different from everything else
|
|
//-------------------------------------------------
|
|
//
|
|
if (spec->m_profileType != Specification::e_Cross)
|
|
{
|
|
for (unsigned short profile=0; profile<spec->m_maxProfileCount-1; ++profile)
|
|
{
|
|
unsigned short base = static_cast<short>(profile * vertex_count);
|
|
for (unsigned short panel=0; panel<vertex_count-1; ++panel)
|
|
{
|
|
if (spec->m_insideOut)
|
|
{
|
|
*indices++ = static_cast<short>(base+panel+1);
|
|
*indices++ = static_cast<short>(base+panel);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count+1);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count+1);
|
|
*indices++ = static_cast<short>(base+panel);
|
|
}
|
|
else
|
|
{
|
|
*indices++ = static_cast<short>(base+panel);
|
|
*indices++ = static_cast<short>(base+panel+1);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count+1);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count+1);
|
|
*indices++ = static_cast<short>(base+panel+vertex_count);
|
|
*indices++ = static_cast<short>(base+panel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Verify(vertex_count==5);
|
|
for (unsigned short profile=0; profile<spec->m_maxProfileCount-1; ++profile)
|
|
{
|
|
unsigned short base = static_cast<short>(profile * vertex_count);
|
|
for (unsigned short panel=0; panel<4; ++panel)
|
|
{
|
|
if (spec->m_insideOut)
|
|
{
|
|
*indices++ = static_cast<short>(base+panel+1);
|
|
*indices++ = base;
|
|
*indices++ = static_cast<short>(base+panel+6);
|
|
*indices++ = static_cast<short>(base+5);
|
|
*indices++ = static_cast<short>(base+panel+6);
|
|
*indices++ = base;
|
|
}
|
|
else
|
|
{
|
|
*indices++ = base;
|
|
*indices++ = static_cast<short>(base+panel+1);
|
|
*indices++ = static_cast<short>(base+panel+6);
|
|
*indices++ = static_cast<short>(base+panel+6);
|
|
*indices++ = static_cast<short>(base+5);
|
|
*indices++ = base;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam::~Beam()
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(m_mesh);
|
|
delete m_mesh;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
gosFX::Beam*
|
|
gosFX::Beam::Make(
|
|
Specification *spec,
|
|
unsigned flags
|
|
)
|
|
{
|
|
Check_Object(spec);
|
|
|
|
gos_PushCurrentHeap(Heap);
|
|
Beam *tube = new gosFX::Beam(spec, flags);
|
|
gos_PopCurrentHeap();
|
|
|
|
return tube;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::Start(Effect::ExecuteInfo *info)
|
|
{
|
|
Check_Object(this);
|
|
Check_Pointer(info);
|
|
|
|
//
|
|
//----------------------
|
|
// Let effect initialize
|
|
//----------------------
|
|
//
|
|
BaseClass::Start(info);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the effect is off, we will create two profiles. If they effect is on,
|
|
// we just keep doing what we do
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
for (int i=0; i<spec->m_maxProfileCount; ++i)
|
|
CreateNewProfile(i);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
bool gosFX::Beam::Execute(ExecuteInfo *base_info)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(base_info);
|
|
GFX_LOGIC("Execute::Beam");
|
|
|
|
//
|
|
//----------------------------------------
|
|
// If we aren't supposed to execute, don't
|
|
//----------------------------------------
|
|
//
|
|
if (!IsExecuted())
|
|
return false;
|
|
ExecuteInfo *info = Cast_Pointer(ExecuteInfo*, base_info);
|
|
Check_Object(info);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Update the head of the tube to the current location, then compute the
|
|
// inverse transformation to keep all particles in their place of origin
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Stuff::LinearMatrix4D local_to_world;
|
|
local_to_world.Multiply(m_localToParent, *info->m_parentToWorld);
|
|
Stuff::LinearMatrix4D world_to_new_local;
|
|
world_to_new_local.Invert(local_to_world);
|
|
|
|
//
|
|
//--------------------------
|
|
// Figure out the birth rate
|
|
//--------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Verify(spec->GetClassID() == BeamClassID);
|
|
Stuff::Scalar dT =
|
|
static_cast<Stuff::Scalar>(info->m_time - m_lastRan);
|
|
Verify(dT >= 0.0f);
|
|
Stuff::Scalar prev_age = m_age;
|
|
m_age += dT * m_ageRate;
|
|
Max_Clamp(m_age, 1.0f);
|
|
|
|
//
|
|
//-----------------------------------------------------
|
|
// Figure out how many profiles we will need this frame
|
|
//-----------------------------------------------------
|
|
//
|
|
Check_Object(info->m_endPoint);
|
|
Stuff::Vector3D core(
|
|
info->m_endPoint->x - local_to_world(3,0),
|
|
info->m_endPoint->y - local_to_world(3,1),
|
|
info->m_endPoint->z - local_to_world(3,2)
|
|
);
|
|
local_to_world.AlignLocalAxisToWorldVector(core, Stuff::Y_Axis, Stuff::Z_Axis, Stuff::X_Axis);
|
|
m_activeProfileCount =
|
|
static_cast<int>(ceil(core.GetLength() * spec->m_profilesPerMeter.ComputeValue(m_age, m_seed)));
|
|
Clamp(m_activeProfileCount, 2, spec->m_maxProfileCount);
|
|
|
|
//
|
|
//----------------------------------
|
|
// Deal with all the active profiles
|
|
//----------------------------------
|
|
//
|
|
Stuff::ExtentBox box(Stuff::Point3D::Identity, Stuff::Point3D::Identity);
|
|
for (int i=0; i<m_activeProfileCount; ++i)
|
|
{
|
|
//
|
|
//---------------------------------------
|
|
// Figure out where the profile should be
|
|
//---------------------------------------
|
|
//
|
|
Profile *profile = GetProfile(i);
|
|
Check_Object(profile);
|
|
profile->m_profileToWorld = local_to_world;
|
|
Stuff::Scalar progress = static_cast<Stuff::Scalar>(i)/(m_activeProfileCount-1);
|
|
profile->m_profileToWorld(3,0) = Stuff::Lerp(local_to_world(3,0), info->m_endPoint->x, progress);
|
|
profile->m_profileToWorld(3,1) = Stuff::Lerp(local_to_world(3,1), info->m_endPoint->y, progress);
|
|
profile->m_profileToWorld(3,2) = Stuff::Lerp(local_to_world(3,2), info->m_endPoint->z, progress);
|
|
Stuff::Sphere bounds;
|
|
AnimateProfile(i, world_to_new_local, info->m_time, &bounds);
|
|
|
|
//
|
|
//---------------------
|
|
// Deal with the bounds
|
|
//---------------------
|
|
//
|
|
if (!i)
|
|
{
|
|
box.maxX = bounds.center.x + bounds.radius;
|
|
box.minX = bounds.center.x - bounds.radius;
|
|
box.maxY = bounds.center.y + bounds.radius;
|
|
box.minY = bounds.center.y - bounds.radius;
|
|
box.maxZ = bounds.center.z + bounds.radius;
|
|
box.minZ = bounds.center.z - bounds.radius;
|
|
}
|
|
else
|
|
{
|
|
Stuff::ExtentBox local_box;
|
|
local_box.maxX = bounds.center.x + bounds.radius;
|
|
local_box.minX = bounds.center.x - bounds.radius;
|
|
local_box.maxY = bounds.center.y + bounds.radius;
|
|
local_box.minY = bounds.center.y - bounds.radius;
|
|
local_box.maxZ = bounds.center.z + bounds.radius;
|
|
local_box.minZ = bounds.center.z - bounds.radius;
|
|
box.Union(box, local_box);
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Put the age back and run the base effect stuff
|
|
//-----------------------------------------------
|
|
//
|
|
m_age = prev_age;
|
|
if (!BaseClass::Execute(info))
|
|
return false;
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Now, build a info->m_bounds around this box
|
|
//--------------------------------------------
|
|
//
|
|
Verify(box.maxX >= box.minX);
|
|
Verify(box.maxY >= box.minY);
|
|
Verify(box.maxZ >= box.minZ);
|
|
Stuff::OBB local_bounds = Stuff::OBB::Identity;
|
|
local_bounds.axisExtents.x = 0.5f * (box.maxX - box.minX);
|
|
local_bounds.axisExtents.y = 0.5f * (box.maxY - box.minY);
|
|
local_bounds.axisExtents.z = 0.5f * (box.maxZ - box.minZ);
|
|
local_bounds.localToParent(3,0) = box.minX + local_bounds.axisExtents.x;
|
|
local_bounds.localToParent(3,1) = box.minY + local_bounds.axisExtents.y;
|
|
local_bounds.localToParent(3,2) = box.minZ + local_bounds.axisExtents.z;
|
|
local_bounds.sphereRadius = local_bounds.axisExtents.GetLength() + Stuff::SMALL;
|
|
Stuff::OBB parent_bounds;
|
|
parent_bounds.Multiply(local_bounds, m_localToParent);
|
|
info->m_bounds->Union(*info->m_bounds, parent_bounds);
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void gosFX::Beam::Kill()
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Destroy all the profiles and set up an empty profile cloud
|
|
//-------------------------------------------------------------
|
|
//
|
|
m_activeProfileCount = 0;
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Now let the base effect handle stopping
|
|
//----------------------------------------
|
|
//
|
|
BaseClass::Kill();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::CreateNewProfile(unsigned index)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Figure out the age and age rate of the new profile
|
|
//----------------------------------------------------
|
|
//
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Profile *profile = GetProfile(index);
|
|
Check_Object(profile);
|
|
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);
|
|
profile->m_seed = seed;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Establish the base position and figure out the direction of drift
|
|
//------------------------------------------------------------------
|
|
//
|
|
ComputeDirection(index);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::ComputeDirection(unsigned index)
|
|
{
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Profile *profile = GetProfile(index);
|
|
Check_Object(profile);
|
|
|
|
Stuff::Scalar seed = profile->m_seed;
|
|
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;
|
|
Stuff::Radian angle = pitch_min + Stuff::Random::GetFraction() * pitch_range;
|
|
Stuff::SinCosPair xy(angle);
|
|
profile->m_direction.x = (Stuff::Random::GetFraction() >= 0.5f) ? xy.sine : -xy.sine;
|
|
profile->m_direction.y = 0.0f;
|
|
profile->m_direction.z = xy.cosine;
|
|
Check_Object(&profile->m_direction);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::AnimateProfile(
|
|
unsigned profile_index,
|
|
const Stuff::LinearMatrix4D &world_to_new_local,
|
|
Stuff::Time till,
|
|
Stuff::Sphere *bounds
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// If the profile gets too old, don't do anything else
|
|
//----------------------------------------------------
|
|
//
|
|
Profile *profile = GetProfile(profile_index);
|
|
Check_Object(profile);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Figure out the scale and displacement of the profile in world space
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Set_Statistic(Beam_Count, Beam_Count+1);
|
|
Stuff::Scalar seed = profile->m_seed;
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
Stuff::Scalar age = m_age;
|
|
Stuff::Scalar scale = spec->m_pScale.ComputeValue(age, seed);
|
|
Verify(scale > 0.0f);
|
|
Stuff::Scalar disp = spec->m_pDisplacement.ComputeValue(age, seed);
|
|
Stuff::Point3D offset;
|
|
if (spec->m_randomDirections)
|
|
ComputeDirection(profile_index);
|
|
offset.Multiply(profile->m_direction, disp);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Now build the template to new local space matrix
|
|
//-------------------------------------------------
|
|
//
|
|
Stuff::AffineMatrix4D template_to_profile(true);
|
|
template_to_profile(0,0) = scale;
|
|
template_to_profile(1,1) = scale;
|
|
template_to_profile(2,2) = scale;
|
|
template_to_profile.BuildTranslation(offset);
|
|
Stuff::AffineMatrix4D template_to_world;
|
|
template_to_world.Multiply(template_to_profile, profile->m_profileToWorld);
|
|
Stuff::AffineMatrix4D template_to_new_local;
|
|
template_to_new_local.Multiply(template_to_world, world_to_new_local);
|
|
bounds->center = template_to_new_local;
|
|
bounds->radius = scale;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Now we just multiply the template through to the vertices unless we are
|
|
// doing the aligned ribbon
|
|
//------------------------------------------------------------------------
|
|
//
|
|
unsigned i;
|
|
unsigned vertex_count = spec->m_vertices.GetLength();
|
|
Verify(vertex_count < 8);
|
|
unsigned vertex_index = profile_index * vertex_count;
|
|
if (spec->m_profileType != Specification::e_AlignedRibbon)
|
|
{
|
|
Check_Pointer(m_P_vertices);
|
|
for (i=0; i<vertex_count; ++i)
|
|
{
|
|
m_P_vertices[vertex_index+i].Multiply(
|
|
spec->m_vertices[i],
|
|
template_to_new_local
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------
|
|
// Figure out the UV adjustments
|
|
//------------------------------
|
|
//
|
|
Check_Pointer(m_P_uvs);
|
|
Stuff::Scalar u = spec->m_pUOffset.ComputeValue(age, seed);
|
|
Stuff::Scalar v = spec->m_pVOffset.ComputeValue(age, seed);
|
|
Stuff::Scalar u2 = spec->m_pUSize.ComputeValue(age, seed);
|
|
Stuff::Scalar v2 = spec->m_pVSize.ComputeValue(age, seed);
|
|
u += u2*profile_index;
|
|
for (i=0; i<vertex_count; ++i)
|
|
{
|
|
m_P_uvs[vertex_index+i].x = u;
|
|
m_P_uvs[vertex_index+i].y = spec->m_uvs[i].y*v2 + v;
|
|
}
|
|
|
|
//
|
|
//---------------------------
|
|
// Lastly, animate the colors
|
|
//---------------------------
|
|
//
|
|
Check_Pointer(m_P_colors);
|
|
Stuff::RGBAColor color;
|
|
color.red = spec->m_pRed.ComputeValue(age, seed);
|
|
color.green = spec->m_pGreen.ComputeValue(age, seed);
|
|
color.blue = spec->m_pBlue.ComputeValue(age, seed);
|
|
color.alpha = spec->m_pAlpha.ComputeValue(age, seed);
|
|
for (i=0; i<vertex_count; ++i)
|
|
m_P_colors[vertex_index+i] = color;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void gosFX::Beam::Draw(DrawInfo *info)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(info);
|
|
GFX_RENDER("Beam");
|
|
|
|
//
|
|
//---------------------------------------------------------
|
|
// If we have active particles, set up the draw information
|
|
//---------------------------------------------------------
|
|
//
|
|
if (m_activeProfileCount>1)
|
|
{
|
|
MidLevelRenderer::DrawEffectInformation dInfo;
|
|
dInfo.effect = m_mesh;
|
|
Specification *spec = GetSpecification();
|
|
Check_Object(spec);
|
|
dInfo.state.Combine(info->m_state, spec->m_state);
|
|
dInfo.clippingFlags = info->m_clippingFlags;
|
|
Stuff::LinearMatrix4D local_to_world;
|
|
local_to_world.Multiply(m_localToParent, *info->m_parentToWorld);
|
|
dInfo.effectToWorld = &local_to_world;
|
|
unsigned vertex_count = spec->m_vertices.GetLength();
|
|
m_vertexCount = m_activeProfileCount * vertex_count;
|
|
m_triangleCount = 2 * (m_activeProfileCount-1) * (vertex_count-1);
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If we are doing the aligned ribbon, we will have to orient each of
|
|
// the profiles and then compute its vertex positions accordingly
|
|
//-------------------------------------------------------------------
|
|
//
|
|
if (spec->m_profileType == Specification::e_AlignedRibbon)
|
|
{
|
|
int vertex = 0;
|
|
unsigned vertex_count = spec->m_vertices.GetLength();
|
|
Verify(vertex_count < 8);
|
|
Stuff::Point3D
|
|
camera_in_world(info->m_clipper->GetCameraToWorldMatrix());
|
|
Stuff::LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(local_to_world);
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// go thru all the profiles and figure out where they are in local
|
|
// space
|
|
//----------------------------------------------------------------
|
|
//
|
|
for (int i=0; i<m_activeProfileCount; ++i)
|
|
{
|
|
Profile *profile = GetProfile(i);
|
|
Check_Object(profile);
|
|
Stuff::Point3D camera_in_profile;
|
|
camera_in_profile.MultiplyByInverse(camera_in_world, profile->m_profileToWorld);
|
|
|
|
//
|
|
//---------------------------------------------------------
|
|
// Figure out the scale and displacement of the template in
|
|
// profile space
|
|
//---------------------------------------------------------
|
|
//
|
|
Stuff::Scalar age = m_age;
|
|
Stuff::Scalar seed = profile->m_seed;
|
|
Stuff::Scalar scale = spec->m_pScale.ComputeValue(age, seed);
|
|
Stuff::Scalar disp = spec->m_pDisplacement.ComputeValue(age, seed);
|
|
Stuff::Point3D offset_in_profile;
|
|
offset_in_profile.Multiply(profile->m_direction, disp);
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// Figure out the direction that we want the profile to face
|
|
// and build a rotation vector that does it
|
|
//----------------------------------------------------------
|
|
//
|
|
Stuff::Vector3D direction;
|
|
direction.Subtract(camera_in_profile, offset_in_profile);
|
|
Stuff::LinearMatrix4D template_rotation(true);
|
|
template_rotation.AlignLocalAxisToWorldVector(
|
|
direction,
|
|
Stuff::Z_Axis,
|
|
Stuff::Y_Axis,
|
|
-1
|
|
);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Now build the template to new local space matrix
|
|
//-------------------------------------------------
|
|
//
|
|
Stuff::AffineMatrix4D template_to_profile;
|
|
template_to_profile(0,0) = scale*template_rotation(0,0);
|
|
template_to_profile(0,1) = scale*template_rotation(0,1);
|
|
template_to_profile(0,2) = scale*template_rotation(0,2);
|
|
template_to_profile(1,0) = scale*template_rotation(1,0);
|
|
template_to_profile(1,1) = scale*template_rotation(1,1);
|
|
template_to_profile(1,2) = scale*template_rotation(1,2);
|
|
template_to_profile(2,0) = scale*template_rotation(2,0);
|
|
template_to_profile(2,1) = scale*template_rotation(2,1);
|
|
template_to_profile(2,2) = scale*template_rotation(2,2);
|
|
template_to_profile.BuildTranslation(offset_in_profile);
|
|
Stuff::AffineMatrix4D template_to_world;
|
|
template_to_world.Multiply(template_to_profile, profile->m_profileToWorld);
|
|
Stuff::AffineMatrix4D template_to_local;
|
|
template_to_local.Multiply(template_to_world, world_to_local);
|
|
|
|
//
|
|
//------------------------------------
|
|
// Multiply the points thru the matrix
|
|
//------------------------------------
|
|
//
|
|
for (int v=0; v<vertex_count; ++v)
|
|
{
|
|
m_P_vertices[vertex++].Multiply(
|
|
spec->m_vertices[v],
|
|
template_to_local
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------
|
|
// Now draw the effect
|
|
//--------------------
|
|
//
|
|
info->m_clipper->DrawEffect(&dInfo);
|
|
}
|
|
|
|
BaseClass::Draw(info);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
void
|
|
gosFX::Beam::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|