Files
Client/GameTools/Zallad3D SceneClass/BGMController.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

891 lines
20 KiB
C++

#include "BGMController.h"
#include "RegionTrigger.h"
#include <vector>
#include <string>
#include <math.h>
#include <algorithm>
#include <exception>
#include <d3dx8.h>
#include <assert.h>
#include "TriggerEvent.h"
#include "SceneStateMgr.h"
///////////////////////////////////////////////////////////////////////////
//
static char BGMDataMark[] = "BGMData";
///////////////////////////////////////////////////////////////////////////
//
CBGMController & CBGMController::GetInstance( IDirectSound8 * pDSound )
{
static CBGMController instance( pDSound );
return instance;
}
///////////////////////////////////////////////////////////////////////////
//
CBGMController::CBGMController( IDirectSound8 * pDSound )
: m_pRegionTrigger( new CRegionTrigger )
, m_pEvents( new EVENTS )
, m_pEventArgs( new EVENTARGS )
, m_iTriggerSelected( UINT_MAX )
, m_iPointSelected( UINT_MAX )
, m_iLastDeleted( UINT_MAX )
, m_pTriggerRegions( new TRIGGERREGIONS )
, m_pContainer_ObjID( new CONTAINER_OBJID )
, m_pPrevTrigger( NULL )
, m_pVertexes( new TRIGGERVERTEXES )
{
}
///////////////////////////////////////////////////////////////////////////
//
CBGMController::~CBGMController()
{
delete m_pRegionTrigger;
delete m_pEvents;
delete m_pEventArgs;
delete m_pTriggerRegions;
delete m_pContainer_ObjID;
delete m_pVertexes;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Create( const char * szFilename, bool forEdit )
{
FILE * fp = fopen( szFilename, "rb" );
if( fp == NULL )
return;
//화일 크기를 구함
fseek( fp, 0, SEEK_END );
long fileSize = ftell( fp );
//화일을 전부 읽음.
char * Buffer = new char[fileSize+1];
fseek( fp, 0, SEEK_SET );
fread( Buffer, fileSize, 1, fp );
//strstr함수 호출에 문제가 되므로 NULL문자를 없앰.
for( int i = 0; i < fileSize; i++ )
{
if( Buffer[i] == '\0' )
Buffer[i] = '\n';
}
//맨 끝에만 NULL 문자를 붙임.
Buffer[fileSize] = '\0';
char * p = strstr( Buffer, BGMDataMark );
delete [] Buffer;
if( p )
{
long offset = p - Buffer;
fseek( fp, offset, SEEK_SET );
Create( fp, forEdit );
}
fclose( fp );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Create( FILE * fp, bool forEdit )
{
Destroy();
char szMark[32];
fread( szMark, sizeof( char ), strlen( BGMDataMark ) + 1, fp );
if( strcmp( szMark, BGMDataMark ) != 0 )
throw bad_exception( "잘못된 트리거 화일입니다. 읽을 수 없습니다." );
//화일 포인터의 위치가 올바른지 표식을 통해 확인함.
char c[2];
fread( c, sizeof( char ), 2, fp );
assert( c[0] == 'R' && c[1] == 'E' );
m_pRegionTrigger->Create( fp );
//표식검사
fread( c, sizeof( char ), 2, fp );
assert( c[0] == 'S' && c[1] == 'F' );
int size;
fread( &size, sizeof( int ), 1, fp );
for( int i = 0; i < size; i++ )
{
m_pEventArgs->push_back( EVENTARG() );
EVENTARG & eventArg = m_pEventArgs->back();
int size2;
fread( &size2, sizeof( int ), 1, fp );
for( int j = 0; j < size2; j++ )
{
int eventID;
fread( &eventID, sizeof( int ), 1, fp );
unsigned char ArgLength;
fread( &ArgLength, sizeof( unsigned char ), 1, fp );
char szArgStr[256];
fread( szArgStr, sizeof( char ), ArgLength, fp );
szArgStr[ArgLength] = '\0';
eventArg.push_back( EVENTARGUNIT( eventID, szArgStr ) );
}
}
//TriggerRegions을 로드
fread( &size, sizeof( int ), 1, fp );
for( i = 0; i < size; i++ )
{
m_pTriggerRegions->push_back( TRIGGERREGION() );
TRIGGERREGION & tRegion = m_pTriggerRegions->back();
TRIGGERLIST & trgList = tRegion.first;
POLYGON & polygon = tRegion.second;
unsigned nTrgList = 0;
fread( &nTrgList, sizeof( unsigned ), 1, fp );
trgList.resize( nTrgList );
fread( &trgList[0], sizeof( int ) * nTrgList, 1, fp );
unsigned nPoly = 0;
fread( &nPoly, sizeof( unsigned ), 1, fp );
polygon.resize( nPoly );
fread( &polygon[0], sizeof( SVector2 ) * nPoly, 1, fp );
}
//표식 검사
fread( c, sizeof( char ), 2, fp );
assert( c[0] == 'V' && c[1] == 'T' );
if( forEdit )
{
//에디터용 세팅들을 해줌
//Vertex 정보들을 로드...
int size = 0;
fread( &size, sizeof( 1 ), 1, fp );
for( i = 0; i < size; i++ )
{
int size2 = 0;
fread( &size2, sizeof( int ), 1, fp );
m_pVertexes->push_back( VERTEXVECTOR() );
VERTEXVECTOR & vec = m_pVertexes->back();
for( int j = 0; j < size2; j++ )
{
float x, y, z;
fread( &x, sizeof( float ), 1, fp );
fread( &y, sizeof( float ), 1, fp );
fread( &z, sizeof( float ), 1, fp );
vec.push_back( D3DXVECTOR3( x, y, z ) );
}
}
}
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Save( const char * szFilename )
{
FILE * fp = fopen( szFilename, "wb" );
Save( fp );
fclose( fp );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Save( FILE * fp )
{
//BGMController Data의 시작임을 나타내는 표식을 저장
fwrite( BGMDataMark, sizeof( char ), strlen( BGMDataMark ) + 1, fp );
//화일 포인터의 위치가 올바른지 확인하기 위한 표식을 남겨둠
char c[2] = { 'R', 'E' };
fwrite( c, sizeof( char ), 2, fp );
m_pRegionTrigger->Save( fp );
//표식을 또 남김
char c1[2] = { 'S', 'F' };
fwrite( c1, sizeof( char ), 2, fp );
int size = m_pEventArgs->size();
fwrite( &size, sizeof( int ), 1, fp );
for( int i = 0; i < size; i++ )
{
EVENTARG & arg = m_pEventArgs->at( i );
int size2 = arg.size();
fwrite( &size2, sizeof( int ), 1, fp );
for( int j = 0; j < size2; j++ )
{
int eventID = arg[j].first;
string & str = arg[j].second;
unsigned char stringlen = str.size();
fwrite( &eventID, sizeof( int ), 1, fp );
fwrite( &stringlen, sizeof( unsigned char ), 1, fp );
fwrite( str.c_str(), sizeof( char ), stringlen, fp );
}
}
//TriggerRegions 저장
size = m_pTriggerRegions->size();
fwrite( &size, sizeof( int ), 1, fp );
for( i = 0; i < size; i++ )
{
TRIGGERREGION & tRegion = m_pTriggerRegions->at( i );
TRIGGERLIST & trgList = tRegion.first;
POLYGON & polygon = tRegion.second;
unsigned nTrgList = trgList.size();
fwrite( &nTrgList, sizeof( unsigned ), 1, fp );
fwrite( &trgList[0], sizeof( int ) * nTrgList, 1, fp );
unsigned nPoly = polygon.size();
fwrite( &nPoly, sizeof( unsigned ), 1, fp );
fwrite( &polygon[0], sizeof( SVector2 ) * nPoly, 1, fp );
}
//또다른 표식
char c2[2] = { 'V', 'T' };
fwrite( c2, sizeof( char ), 2, fp );
//Vertex 정보들을 저장
size = m_pVertexes->size();
fwrite( &size, sizeof( int ), 1, fp );
for( i = 0; i < size; i++ )
{
VERTEXVECTOR & vec = m_pVertexes->at( i );
int size2 = vec.size();
fwrite( &size2, sizeof( int ), 1, fp );
for( int j = 0; j < size2; j++ )
{
fwrite( &vec[j].x, sizeof( float ), 1, fp );
fwrite( &vec[j].y, sizeof( float ), 1, fp );
fwrite( &vec[j].z, sizeof( float ), 1, fp );
}
}
//잘 읽혔는지 검사하기 위한 마지막 표식
char c3[3] = { 'E', 'N', 'D' };
fwrite( c3, sizeof( char ), 3, fp );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::DestroyAllEvents()
{
for( unsigned i = 0; i < m_pEvents->size(); i++ )
{
EVENT & event = m_pEvents->at( i );
for( unsigned j = 0; j < event.size(); j++ )
{
delete event[j];
event[j] = NULL;
}
}
m_pEvents->clear();
m_pEventArgs->clear();
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Destroy()
{
DestroyAllEvents();
m_pRegionTrigger->Destroy();
m_pTriggerRegions->clear();
m_pVertexes->clear();
m_pContainer_ObjID->clear();
m_iTriggerSelected = m_iPointSelected = m_iLastDeleted = UINT_MAX;
m_pPrevTrigger = NULL;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Update( float x, float y, float z )
{
SVector2 pos( x, z );
TRIGGERLIST const * pCurrentTrigger = &m_pRegionTrigger->CheckTrigger( pos );
if( m_pPrevTrigger == pCurrentTrigger )
return;
if( m_pPrevTrigger != NULL && pCurrentTrigger != NULL )
{
for( int i = 0; i < pCurrentTrigger->size(); i++ )
{
int triggerID = pCurrentTrigger->at( i );
TRIGGERLIST::const_iterator it = find( m_pPrevTrigger->begin(), m_pPrevTrigger->end(), triggerID );
if( it == m_pPrevTrigger->end() )
{
BeginEvent( triggerID );
}
}
unsigned prevSize = m_pPrevTrigger->size();
for( i = 0; i < prevSize; i++ )
{
int triggerID = m_pPrevTrigger->at( i );
TRIGGERLIST::const_iterator it = find( pCurrentTrigger->begin(), pCurrentTrigger->end(), triggerID );
if( it == pCurrentTrigger->end() )
{
EndEvent( triggerID );
}
}
}
m_pPrevTrigger = pCurrentTrigger;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::BeginEvent( int triggerID )
{
if( triggerID >= 0 && triggerID < m_pEventArgs->size() )
{
EVENTARG & eventArg = m_pEventArgs->at( triggerID );
if( triggerID >= m_pEvents->size() )
m_pEvents->resize( triggerID + 1, EVENT() );
EVENT & event = m_pEvents->at( triggerID );
//event를 clear한다.
for( unsigned i = 0; i < event.size(); i++ )
delete event[i];
event.clear();
for( i = 0; i < eventArg.size(); i++ )
{
if( eventArg[i].first == 0 )
event.push_back( new CEvent_MusicPlay );
else if( eventArg[i].first == 1 )
event.push_back( new CEvent_ShowMessage );
else if( eventArg[i].first == 2 )
event.push_back( new CEvent_MusicPlayOnce );
else if( eventArg[i].first == 3 )
event.push_back( new CEvent_ShowEffect );
else
event.push_back( NULL );
if( event[i] )
{
event[i]->Create( eventArg[i].second.c_str() );
event[i]->Begin();
}
}
}
}
void CBGMController::EndEvent( int triggerID )
{
if( triggerID >= 0 && triggerID < m_pEvents->size() )
{
EVENT & event = m_pEvents->at( triggerID );
for( unsigned i = 0; i < event.size(); i++ )
{
if( event[i] )
{
event[i]->End();
delete event[i];
event[i] = NULL;
}
}
event.clear();
}
}
///////////////////////////////////////////////////////////////////////////
//
//트리거 에디팅을 위한 함수들
bool CBGMController::IsValidTriggerSelected()
{
if( m_iTriggerSelected >= m_pTriggerRegions->size() )
return false;
return true;
}
bool CBGMController::IsValidPointSelected()
{
if( m_iPointSelected >= (*m_pTriggerRegions)[ m_iTriggerSelected ].second.size() )
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////
//
unsigned CBGMController::AddTrigger()
{
m_pEventArgs->push_back( EVENTARG() );
m_pTriggerRegions->push_back( TRIGGERREGION() );
m_iTriggerSelected = m_pTriggerRegions->size() - 1;
TRIGGERREGION & added = m_pTriggerRegions->back();
added.first.push_back( m_iTriggerSelected );
m_pVertexes->push_back( VERTEXVECTOR() );
return m_iTriggerSelected;
}
///////////////////////////////////////////////////////////////////////////
//
template < class T >
typename vector<T>::iterator GetIteratorOfIndex( vector<T> & Vector, unsigned index )
{
unsigned curIndex = 0;
for( vector<T>::iterator i = Vector.begin(); i != Vector.end(); i++ )
{
if( curIndex == index )
return i;
curIndex++;
}
return i;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::DeleteTrigger()
{
if( !IsValidTriggerSelected() )
return;
EVENTARGS::iterator i = GetIteratorOfIndex( *m_pEventArgs, m_iTriggerSelected );
if( i != m_pEventArgs->end() )
m_pEventArgs->erase( i );
TRIGGERREGIONS::iterator i2 = GetIteratorOfIndex( *m_pTriggerRegions, m_iTriggerSelected );
if( i2 != m_pTriggerRegions->end() )
m_pTriggerRegions->erase( i2 );
TRIGGERVERTEXES::iterator i3 = GetIteratorOfIndex( *m_pVertexes, m_iTriggerSelected );
if( i3 != m_pVertexes->end() )
m_pVertexes->erase( i3 );
}
///////////////////////////////////////////////////////////////////////////
//
bool CBGMController::SelectTrigger( unsigned index )
{
if( !IsValidTriggerSelected() )
return false;
m_iTriggerSelected = index;
m_iPointSelected = 0;
return true;
}
///////////////////////////////////////////////////////////////////////////
//
int CBGMController::AddEvent( int eventID, const char * szArg )
{
if( IsValidTriggerSelected() )
{
EVENTARG & eventArg = m_pEventArgs->at( m_iTriggerSelected );
eventArg.push_back( EVENTARGUNIT( eventID, szArg ) );
for( int i = 0; i < eventArg.size(); i++ )
{
EVENTARGUNIT & arg = eventArg[i];
}
return eventArg.size() - 1;
}
return 0;
}
bool CBGMController::SetEvent( int Event, int eventType, const char * szArg )
{
if( IsValidTriggerSelected() )
{
EVENTARG & eventArg = m_pEventArgs->at( m_iTriggerSelected );
eventArg[ Event ].first = eventType;
eventArg[ Event ].second = szArg;
return true;
}
return false;
}
bool CBGMController::GetEvent( int Event, int & eventType, char * szArg )
{
if( IsValidTriggerSelected() )
{
EVENTARG & eventArg = m_pEventArgs->at( m_iTriggerSelected );
eventType = eventArg[ Event ].first;
strcpy( szArg, eventArg[ Event ].second.c_str() );
return true;
}
return false;
}
bool CBGMController::DeleteEvent( int Event )
{
if( IsValidTriggerSelected() )
{
EVENTARG & eventArg = m_pEventArgs->at( m_iTriggerSelected );
EVENTARG::iterator iter = GetIteratorOfIndex( eventArg, Event );
if( iter != eventArg.end() )
eventArg.erase( iter );
for( int i = 0; i < eventArg.size(); i++ )
{
EVENTARGUNIT & arg = eventArg[i];
}
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::AddPointToVertexes( unsigned triggerIndex, float x, float y, float z )
{
if( triggerIndex >= m_pVertexes->size() )
return;
VERTEXVECTOR & vec = m_pVertexes->at( triggerIndex );
vec.push_back( D3DXVECTOR3( x, y, z ) );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::MovePointAtVertexes( unsigned triggerIndex, unsigned pointIndex, float x, float y, float z )
{
if( triggerIndex >= m_pVertexes->size() )
return;
VERTEXVECTOR & vec = m_pVertexes->at( triggerIndex );
vec[pointIndex] = D3DXVECTOR3( x, y, z );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::DeletePointAtVertexes( unsigned triggerIndex, unsigned pointIndex )
{
if( triggerIndex >= m_pVertexes->size() )
return;
VERTEXVECTOR & vec = m_pVertexes->at( triggerIndex );
VERTEXVECTOR::iterator it = GetIteratorOfIndex( vec, triggerIndex );
if( it == vec.end() )
return;
vec.erase( it );
}
///////////////////////////////////////////////////////////////////////////
//
unsigned CBGMController::AddPoint( float x, float y, float z, long objID )
{
if( !IsValidTriggerSelected() )
return -1;
TRIGGERREGION & region = m_pTriggerRegions->at( m_iTriggerSelected );
region.second.push_back( SVector2( x, z ) );
unsigned size = region.second.size();
if( m_iTriggerSelected >= m_pContainer_ObjID->size() )
m_pContainer_ObjID->resize( m_iTriggerSelected + 1 );
m_iPointSelected = region.second.size() - 1;
if( m_iPointSelected >= (*m_pContainer_ObjID)[ m_iTriggerSelected ].size() )
(*m_pContainer_ObjID)[ m_iTriggerSelected ].resize( m_iPointSelected + 1 );
(*m_pContainer_ObjID)[ m_iTriggerSelected ][ m_iPointSelected ] = objID;
AddPointToVertexes( m_iTriggerSelected, x, y, z );
return m_iPointSelected;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::MovePoint( float x, float y, float z )
{
if( !IsValidTriggerSelected() )
return;
if( !IsValidPointSelected() )
return;
TRIGGERREGION & region = m_pTriggerRegions->at( m_iTriggerSelected );
region.second[ m_iPointSelected ].x = x;
region.second[ m_iPointSelected ].y = z;
MovePointAtVertexes( m_iTriggerSelected, m_iPointSelected, x, y, z );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::SetObjID( long objID )
{
if( m_iTriggerSelected >= m_pContainer_ObjID->size() )
m_pContainer_ObjID->resize( m_iTriggerSelected + 1 );
if( m_iPointSelected >= (*m_pContainer_ObjID)[ m_iTriggerSelected ].size() )
(*m_pContainer_ObjID)[ m_iTriggerSelected ].resize( m_iPointSelected + 1 );
(*m_pContainer_ObjID)[ m_iTriggerSelected ][ m_iPointSelected ] = objID;
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::DeletePoint()
{
m_iLastDeleted = UINT_MAX;
if( !IsValidTriggerSelected() )
return;
if( !IsValidPointSelected() )
return;
TRIGGERREGION & region = m_pTriggerRegions->at( m_iTriggerSelected );
if( region.second.size() <= 3 )
{
throw bad_exception( "트리거를 이루는 점들이 3개 이상이 되어야 하므로 주어진 노드를 지울 수 없습니다." );
}
POLYGON::iterator it = GetIteratorOfIndex( region.second, m_iPointSelected );
region.second.erase( it );
DeletePointAtVertexes( m_iTriggerSelected, m_iPointSelected );
m_iLastDeleted = ( m_iTriggerSelected << 14 ) | m_iPointSelected;
m_iPointSelected--;
}
///////////////////////////////////////////////////////////////////////////
//
/*
bool CBGMController::SelectPoint( unsigned index )
{
m_iPointSelected = index & 0x00003fff;
m_iTriggerSelected = ( index & 0xfffc000 ) >> 14;
if( !IsValidTriggerSelected() )
return false;
if( !IsValidPointSelected() )
return false;
return true;
}
*/
bool CBGMController::SelectPoint( unsigned triggerIndex, unsigned pointIndex )
{
m_iPointSelected = pointIndex;
m_iTriggerSelected = triggerIndex;
if( !IsValidTriggerSelected() )
return false;
if( !IsValidPointSelected() )
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////
//
#define DISTANCE( x1, y1, x2, y2 ) sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
void CBGMController::SelectClosestPoint( float x, float y, float z, float limitDistance )
{
double fDistance = 100000.0f;
unsigned triggerIndex = UINT_MAX;
unsigned polygonIndex = UINT_MAX;
for( unsigned i = 0; i < m_pTriggerRegions->size(); i++ )
{
POLYGON & polygon = m_pTriggerRegions->at( i ).second;
for( unsigned i2 = 0; i2 < polygon.size(); i2++ )
{
double dist = DISTANCE( polygon[i2].x, polygon[i2].y, x, z ); //y는 높이 값이므로 무시
if( dist < fDistance )
{
fDistance = dist;
triggerIndex = i;
polygonIndex = i2;
}
}
}
if( fDistance <= limitDistance )
SelectPoint( triggerIndex, polygonIndex );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::GetPoint( float & x, float & y, float & z, long & objID )
{
TRIGGERREGION & region = m_pTriggerRegions->at( m_iTriggerSelected );
VERTEXVECTOR & vec = m_pVertexes->at( m_iTriggerSelected );
x = vec[m_iPointSelected].x;
y = vec[m_iPointSelected].y;
z = vec[m_iPointSelected].z;
// x = region.second[ m_iPointSelected ].x;
// y = 0.0f;
// z = region.second[ m_iPointSelected ].y;
objID = (*m_pContainer_ObjID)[ m_iTriggerSelected ][ m_iPointSelected ];
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Rebuild( float Epsilon )
{
m_pPrevTrigger = NULL;
m_pRegionTrigger->Create( *m_pTriggerRegions, Epsilon );
}
///////////////////////////////////////////////////////////////////////////
//
void CBGMController::Render( IDirect3DDevice8 * p3DDevice )
{
DWORD cullmode = 0;
DWORD fillmode = 0;
p3DDevice->GetRenderState( D3DRS_CULLMODE, &cullmode );
p3DDevice->GetRenderState( D3DRS_CULLMODE, &fillmode );
CSceneStateMgr::_SetD3DRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
CSceneStateMgr::_SetD3DRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME);
p3DDevice->SetVertexShader( D3DFVF_XYZ );
for( unsigned i = 0; i < m_pVertexes->size(); i++ )
{
VERTEXVECTOR & vertex = m_pVertexes->at( i );
if( vertex.size() >= 3 )
{
p3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, vertex.size() - 2, vertex[0], sizeof( D3DXVECTOR3 ) );
}
}
CSceneStateMgr::_SetD3DRenderState( D3DRS_CULLMODE, cullmode );
CSceneStateMgr::_SetD3DRenderState( D3DRS_FILLMODE, fillmode );
}
///////////////////////////////////////////////////////////////////////////
//
int CBGMController::GetPolygonCount()
{
return m_pVertexes->size();
}
int CBGMController::GetVertexCount( int i )
{
VERTEXVECTOR & vec = m_pVertexes->at( i );
return vec.size();
}
void CBGMController::GetVertex( int i, int j, float & x, float & y, float & z )
{
VERTEXVECTOR & vec = m_pVertexes->at( i );
x = vec[j].x;
y = vec[j].y;
z = vec[j].z;
}
///////////////////////////////////////////////////////////////////////////
//