Click here to Skip to main content
15,891,136 members
Articles / Desktop Programming / MFC

Professional User Interface Suite

Rate me:
Please Sign up or sign in to vote.
4.99/5 (174 votes)
13 Jan 200423 min read 1.5M   23.6K   641  
MFC extension library enabling software to be provided with a professional UI
// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "GLViews.h"
#include "ChildView.h"
#include "MainFrm.h"

#include <io.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// C3DObject

IMPLEMENT_DYNCREATE( C3DObject, CObject );

C3DObject::C3DObject(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	bool bEnabledParentDependency, //  = true
	bool bEnabledScale, //  = false
	bool bEnabledTranslation, //  = true
	bool bEnabledOrientation //  = true
	)
	: m_sName( sName )
	, m_pParent( NULL )
	, m_bEnabledParentDependency( bEnabledParentDependency )
	, m_bEnabledScale( bEnabledScale )
	, m_bEnabledTranslation( bEnabledTranslation )
	, m_bEnabledOrientation( bEnabledOrientation )
	, m_hti( NULL )
{
	ASSERT( sName != NULL );
	ASSERT( !m_sName.IsEmpty() );
	LocalLoadIdentity();
}

C3DObject::~C3DObject()
{
	ASSERT( m_pParent == NULL );
	for(	POSITION pos = m_listChilds.GetHeadPosition();
			pos != NULL;
			)
	{ // walk all childs
		C3DObject * pChild = m_listChilds.GetNext( pos );
		ASSERT_VALID( pChild );
		ASSERT( pChild->m_pParent == this );
		pChild->m_pParent = NULL;
		delete pChild;
	} // walk all childs
	m_listChilds.RemoveAll();
}

void C3DObject::SerializeState(
	CArchive & ar
	)
{
	ASSERT_VALID( this );
	if( ar.IsStoring() )
	{
		ar << m_v3LocalScale.x;
		ar << m_v3LocalScale.y;
		ar << m_v3LocalScale.z;
		
		ar << m_v3LocalTranslation.x;
		ar << m_v3LocalTranslation.y;
		ar << m_v3LocalTranslation.z;

		ar << m_quatLocalOrientation.x;
		ar << m_quatLocalOrientation.y;
		ar << m_quatLocalOrientation.z;
		ar << m_quatLocalOrientation.w;

//		ar << m_mtxLastTransformation.m11;
//		ar << m_mtxLastTransformation.m21;
//		ar << m_mtxLastTransformation.m31;
//		ar << m_mtxLastTransformation.m41;
//		ar << m_mtxLastTransformation.m12;
//		ar << m_mtxLastTransformation.m22;
//		ar << m_mtxLastTransformation.m32;
//		ar << m_mtxLastTransformation.m42;
//		ar << m_mtxLastTransformation.m13;
//		ar << m_mtxLastTransformation.m23;
//		ar << m_mtxLastTransformation.m33;
//		ar << m_mtxLastTransformation.m43;
//		ar << m_mtxLastTransformation.m14;
//		ar << m_mtxLastTransformation.m24;
//		ar << m_mtxLastTransformation.m34;
//		ar << m_mtxLastTransformation.m44;

		ar << m_sName;

		DWORD dwEnabledFlags = 0L;
		if( m_bEnabledParentDependency )
			dwEnabledFlags |= 0x00000001L;
		if( m_bEnabledScale )
			dwEnabledFlags |= 0x00000002L;
		if( m_bEnabledTranslation )
			dwEnabledFlags |= 0x00000004L;
		if( m_bEnabledOrientation )
			dwEnabledFlags |= 0x00000008L;

		ar << dwEnabledFlags;
	} // if( ar.IsStoring() )
	else
	{
		LocalLoadIdentity();

		ar >> m_v3LocalScale.x;
		ar >> m_v3LocalScale.y;
		ar >> m_v3LocalScale.z;
		
		ar >> m_v3LocalTranslation.x;
		ar >> m_v3LocalTranslation.y;
		ar >> m_v3LocalTranslation.z;

		ar >> m_quatLocalOrientation.x;
		ar >> m_quatLocalOrientation.y;
		ar >> m_quatLocalOrientation.z;
		ar >> m_quatLocalOrientation.w;

//		ar >> m_mtxLastTransformation.m11;
//		ar >> m_mtxLastTransformation.m21;
//		ar >> m_mtxLastTransformation.m31;
//		ar >> m_mtxLastTransformation.m41;
//		ar >> m_mtxLastTransformation.m12;
//		ar >> m_mtxLastTransformation.m22;
//		ar >> m_mtxLastTransformation.m32;
//		ar >> m_mtxLastTransformation.m42;
//		ar >> m_mtxLastTransformation.m13;
//		ar >> m_mtxLastTransformation.m23;
//		ar >> m_mtxLastTransformation.m33;
//		ar >> m_mtxLastTransformation.m43;
//		ar >> m_mtxLastTransformation.m14;
//		ar >> m_mtxLastTransformation.m24;
//		ar >> m_mtxLastTransformation.m34;
//		ar >> m_mtxLastTransformation.m44;

		ar >> m_sName;
		ASSERT( !m_sName.IsEmpty() );

		m_bEnabledParentDependency = false;
		m_bEnabledScale = false;
		m_bEnabledTranslation = false;
		m_bEnabledOrientation = false;
		DWORD dwEnabledFlags = 0L;
		ar >> dwEnabledFlags;
		if( (dwEnabledFlags&0x00000001L) != 0 )
			m_bEnabledParentDependency = true;
		if( (dwEnabledFlags&0x00000002L) != 0 )
			m_bEnabledScale = true;
		if( (dwEnabledFlags&0x00000004L) != 0 )
			m_bEnabledTranslation = true;
		if( (dwEnabledFlags&0x00000008L) != 0 )
			m_bEnabledOrientation = true;

	} // else from if( ar.IsStoring() )
}

void C3DObject::WalkTree(
	C3DObject::eWalkTreeQuery walkTreeQuery,
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror // = NULL
	)
{
	ASSERT_VALID( this );
	if(	!OnWalkTree(
			walkTreeQuery,
			pCam,
			pView3D,
			lpvCookie,
			pObjMirror
			)
		)
		return;
	for(	POSITION pos = m_listChilds.GetHeadPosition();
			pos != NULL;
			)
	{ // walk all childs
		C3DObject * pChild = m_listChilds.GetNext( pos );
		ASSERT_VALID( pChild );
		ASSERT( pChild->m_pParent == this );
		if(		walkTreeQuery == EWTQ_RENDER
			&&	( !IsRenderSubtreeItem(pChild) )
			)
			continue;
		pChild->WalkTree(
			walkTreeQuery,
			pCam,
			pView3D,
			lpvCookie,
			pObjMirror
			);
	} // walk all childs
}

bool C3DObject::OnWalkTree(
	C3DObject::eWalkTreeQuery walkTreeQuery,
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
bool bRetVal = true;
	ASSERT_VALID( this );
	switch( walkTreeQuery )
	{
	case EWTQ_THREAD_INIT:
		OnThreadInit( lpvCookie );
	break;
	case EWTQ_THREAD_DONE:
		OnThreadDone( lpvCookie );
	break;
	case EWTQ_PLAY:
		OnPlay( lpvCookie );
	break;
	case EWTQ_TRANSFORM:
		ASSERT_VALID( pCam );
		ASSERT_VALID( pView3D );
		OnTransform( pCam, pView3D, lpvCookie );
	break;
	case EWTQ_RENDER:
		ASSERT_VALID( pCam );
		ASSERT_VALID( pView3D );
		OnRender( pCam, pView3D, lpvCookie, pObjMirror );
		if( !IsRenderSubtree() )
			bRetVal = false;
	break;
	case EWTQ_ADD_TO_TREE:
		OnAddToTree( (CObjectHierarchyTreeCtrl*)lpvCookie );
	break;
	} // switch( walkTreeQuery )
	return bRetVal;
}

void C3DObject::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::OnThreadDone( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
}

void C3DObject::CalcTransformation(
	_mrct mtxParentTransformation
	)
{
	ASSERT_VALID( this );
	if( m_bEnabledParentDependency )
		m_mtxLastTransformation = mtxParentTransformation;
	else
		m_mtxLastTransformation.load_identity();
	if( m_bEnabledScale )
		m_mtxLastTransformation *= m_v3LocalScale.get_as_scale();
	if( m_bEnabledTranslation )
		m_mtxLastTransformation *= m_v3LocalTranslation.get_as_translation();
	if( m_bEnabledOrientation )
		m_mtxLastTransformation *= m_quatLocalOrientation;
}

void C3DObject::OnTransform(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
C3DObject * pParent = GetParent();
	if( pParent != NULL )
		CalcTransformation( pParent->m_mtxLastTransformation );
	else
	{
		_mt mtxIdentity;
		CalcTransformation( mtxIdentity );
	}
}

void C3DObject::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
}

bool C3DObject::IsRenderSubtree()
{
	ASSERT_VALID( this );
	return true;
}

bool C3DObject::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	pObjChild;
	return true;
}

void C3DObject::OnAddToTree(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti == NULL );
HTREEITEM htiParent = TVI_ROOT;
C3DObject * pObjParent = GetParent();
	if( pObjParent != NULL )
	{
		ASSERT_VALID( pObjParent );
		htiParent = pObjParent->m_hti;
		ASSERT( htiParent != NULL );
	}
CString strTreeItemText;
int nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_GENERIC_OBJ;
	OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	ASSERT( !strTreeItemText.IsEmpty() );
	m_hti =
		pTreeCtrl->InsertItem(
			(LPCTSTR)strTreeItemText,
			nTreeImageIndex,
			nTreeImageIndex,
			htiParent,
			TVI_LAST
			);
	ASSERT( m_hti != NULL );
	VERIFY( pTreeCtrl->SetItemData( m_hti, (DWORD)this ) );
}

void C3DObject::OnTreeItemDblClick(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti != NULL );
	pTreeCtrl;
}

void C3DObject::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText = GetName();
	ASSERT( !strTreeItemText.IsEmpty() );
	if( m_pParent == NULL )
		nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_ROOT;
}

void C3DObject::GenerateSphere(
	int nTextureIndex,
	_v3rct ptCenter,
	GLfloat fRadius,
	int nStacksCount,
	GLfloat fAngleTheta0,
	GLfloat fAngleTheta1,
	GLfloat fAngleTheta1Phi0,
	GLfloat fAngleTheta1Phi1,
	bool bNoDepth // = false
	)
{
	ASSERT( fRadius >= 0 && nStacksCount > 3 );
	for( int nStackIdx0 = 0; nStackIdx0 < (nStacksCount / 2); nStackIdx0++ )
	{
		GLfloat fAngleStack0 =
			fAngleTheta1Phi0 +
				( fAngleTheta1Phi1 - fAngleTheta1Phi0 ) /
					( nStacksCount / 2 ) * nStackIdx0 ;
		GLfloat fAngleStack1 =
			fAngleTheta1Phi0 +
				( fAngleTheta1Phi1 - fAngleTheta1Phi0 ) /
					( nStacksCount / 2 ) * ( nStackIdx0 + 1 );
		_v3t
			vNormalPrev0, vNormalCurr0, vNormalPrev1, vNormalCurr1,
			vVertexPrev0, vVertexCurr0, vVertexPrev1, vVertexCurr1;
		GLfloat
			fTextureCoordPrevX0 = 0.0f, fTextureCoordPrevY0 = 0.0f,
			fTextureCoordCurrX0 = 0.0f, fTextureCoordCurrY0 = 0.0f,
			fTextureCoordPrevX1 = 0.0f, fTextureCoordPrevY1 = 0.0f,
			fTextureCoordCurrX1 = 0.0f, fTextureCoordCurrY1 = 0.0f;
		for( int nStackIdx1 = 0; nStackIdx1 <= nStacksCount; nStackIdx1++ )
		{
			if( nStackIdx1 > 0 )
			{
				vNormalPrev0 = vNormalCurr0;
				vNormalPrev1 = vNormalCurr1;
				vVertexPrev0 = vVertexCurr0;
				vVertexPrev1 = vVertexCurr1;
				fTextureCoordPrevX0 = fTextureCoordCurrX0;
				fTextureCoordPrevX1 = fTextureCoordCurrX1;
				fTextureCoordPrevY0 = fTextureCoordCurrY0;
				fTextureCoordPrevY1 = fTextureCoordCurrY1;
			} // if( nStackIdx1 > 0 )
			GLfloat fAngleStack2 =
				fAngleTheta0 +
					nStackIdx1 * (fAngleTheta1 - fAngleTheta0)
						/ nStacksCount;
			vNormalCurr0.load_vector(
				_ntr::cos(fAngleStack0) * _ntr::cos(fAngleStack2),
				_ntr::sin(fAngleStack0),
				_ntr::cos(fAngleStack0) * _ntr::sin(fAngleStack2)
				);
			vVertexCurr0.load_vector(
				ptCenter.x + fRadius * vNormalCurr0.x,
				ptCenter.y + fRadius * vNormalCurr0.y,
				ptCenter.z + fRadius * vNormalCurr0.z
				);
			fTextureCoordCurrX0 = ((GLfloat)nStackIdx1) / ((GLfloat)nStacksCount);
			fTextureCoordCurrY0 = (2.0f * nStackIdx0) / ((GLfloat)nStacksCount);
			vNormalCurr1.load_vector(
				_ntr::cos(fAngleStack1) * _ntr::cos(fAngleStack2),
				_ntr::sin(fAngleStack1),
				_ntr::cos(fAngleStack1) * _ntr::sin(fAngleStack2)
				);
			vVertexCurr1.load_vector(
				ptCenter.x + fRadius * vNormalCurr1.x,
				ptCenter.y + fRadius * vNormalCurr1.y,
				ptCenter.z + fRadius * vNormalCurr1.z
				);
			fTextureCoordCurrX1 = ((GLfloat)nStackIdx1) / ((GLfloat)nStacksCount);
			fTextureCoordCurrY1 = (2 * (nStackIdx0 + 1)) / (GLfloat)nStacksCount;
			if( nStackIdx1 > 0 )
			{
				C3DSquare * pObjSquare = new C3DSquare;
				pObjSquare->m_nTextureIndex = nTextureIndex;
				pObjSquare->m_bNoDepth = bNoDepth;
				pObjSquare->m_bUseNormal = true;
				pObjSquare->m_vecNormal = vNormalCurr1;
				pObjSquare->m_arrTextureCoords[0].m_fX = fTextureCoordPrevX0;
				pObjSquare->m_arrTextureCoords[0].m_fY = fTextureCoordPrevY0;
				pObjSquare->m_arrPoints[0] = vVertexPrev0;
				pObjSquare->m_arrTextureCoords[1].m_fX = fTextureCoordPrevX1;
				pObjSquare->m_arrTextureCoords[1].m_fY = fTextureCoordPrevY1;
				pObjSquare->m_arrPoints[1] = vVertexPrev1;
				pObjSquare->m_arrTextureCoords[2].m_fX = fTextureCoordCurrX1;
				pObjSquare->m_arrTextureCoords[2].m_fY = fTextureCoordCurrY1;
				pObjSquare->m_arrPoints[2] = vVertexCurr1;
				pObjSquare->m_arrTextureCoords[3].m_fX = fTextureCoordCurrX0;
				pObjSquare->m_arrTextureCoords[3].m_fY = fTextureCoordCurrY0;
				pObjSquare->m_arrPoints[3] = vVertexCurr0;
				AddChild( pObjSquare );
			} // if( nStackIdx1 > 0 )
		} // for( int nStackIdx1 = 0; nStackIdx1 <= nStacksCount; nStackIdx1++ )
	} // for( int nStackIdx0 = 0; nStackIdx0 < (nStacksCount / 2); nStackIdx0++ )
}

bool C3DObject::stat_LoadResourceToMemory(
	LPCTSTR pszResId,
	LPCTSTR pszRsType,
	LPVOID * pLpvOutBuffer,
	DWORD * pDwOutSize
	)
{
	*pLpvOutBuffer = NULL;
	*pDwOutSize = 0L;
HINSTANCE hInst =
		::AfxFindResourceHandle( pszResId, pszRsType );
	if( hInst == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
HRSRC hRsrc = FindResource( hInst, pszResId, pszRsType );
	if( hRsrc == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
HGLOBAL hGlobal = LoadResource( hInst, hRsrc );
	if( hGlobal == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
LPVOID lpvData = LockResource( hGlobal );
DWORD dwSize = SizeofResource( hInst, hRsrc );
	if( lpvData == NULL )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	if( dwSize == 0L )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	*pLpvOutBuffer = malloc( dwSize );
	if( (*pLpvOutBuffer) == NULL )
	{
		ASSERT( FALSE );
		::UnlockResource( hGlobal );
		::FreeResource( hGlobal );
		return false;
	}
	memcpy( *pLpvOutBuffer, lpvData, dwSize );
	*pDwOutSize = dwSize;
	::UnlockResource( hGlobal );
	::FreeResource( hGlobal );
	return true;
}

bool C3DObject::stat_IsFileExists(
	LPCTSTR sFilePath
	)
{
	ASSERT( sFilePath != NULL );
	if( sFilePath == NULL )
		return false;
#if (defined _UNICODE)
		struct _wfinddata_t fd;
#else
		struct _finddata_t fd;
#endif
	long hNextFile =
#if (defined _UNICODE)
			_wfindfirst(
#else
			_findfirst(
#endif
				(LPTSTR)sFilePath,
				&fd
				);
bool bExists = true;
	if( hNextFile < 0 )
		bExists = false;
	else
	{
		if( (fd.attrib&_A_SUBDIR) != 0 )
			bExists = false;
	} // else from if( hNextFile < 0 )
	_findclose( hNextFile );
	return bExists;
}

/////////////////////////////////////////////////////////////////////////////
// C3DModifier

IMPLEMENT_DYNCREATE( C3DModifier, C3DObject );

C3DModifier::C3DModifier(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	GLfloat fAnglePlayStepPitch, //  = 0.0f
	GLfloat fAnglePlayStepYaw, //  = 0.0f
	GLfloat fAnglePlayStepRoll //  = 0.0f
	)
	: C3DObject( sName )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DModifier::~C3DModifier()
{
}

void C3DModifier::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	ASSERT( !m_sName.IsEmpty() );
int nCount = m_listChilds.GetCount();
	ASSERT( nCount > 0 );
	if( nCount > 1 )
	{
		strTreeItemText = _T("Group modifier \"");
		strTreeItemText += m_sName;
		strTreeItemText += _T("\"");
		nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_MODIFIER_GRP;
		return;
	} // if( nCount > 1 )
C3DObject * pObjHead = m_listChilds.GetHead();
	ASSERT_VALID( pObjHead );
CString strChildDisplayText;
	pObjHead->OnQueryTreeDisplayParms(
		strChildDisplayText,
		nTreeImageIndex
		);
	ASSERT( !strChildDisplayText.IsEmpty() );
	strTreeItemText = _T("Modifier for: ");
	strTreeItemText += strChildDisplayText;
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_MODIFIER_ONE;
}

void C3DModifier::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if(		m_fAnglePlayStepPitch != 0.0f
		&&	m_fAnglePlayStepYaw != 0.0f
		&&	m_fAnglePlayStepRoll != 0.0f
		)
		LocalAdjustOrientation(
			m_fAnglePlayStepPitch,
			m_fAnglePlayStepYaw,
			m_fAnglePlayStepRoll
			);
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DSquare

IMPLEMENT_DYNCREATE( C3DSquare, C3DObject );

C3DSquare::C3DSquare()
	: C3DObject( GL_VIEWS_SQUARE_NAME )
	, m_nTextureIndex( -1 )
	, m_bUseNormal( false )
	, m_bNoDepth( false )
	, m_bNoCullFace( false )
	, m_bAdjustAlphaFunc( false )
	, m_bAdjustBlendFunc( false )
	, m_gleAlphaFunc( GL_NOTEQUAL )
	, m_glcAlphaRef( 0.0f )
	, m_gleBlendFactorS( GL_SRC_ALPHA )
	, m_gleBlendFactorD( GL_ONE )
{
}

C3DSquare::~C3DSquare()
{
}

void C3DSquare::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		if( m_bNoDepth )
		{
			glDisable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoDepth )
		if( m_bNoCullFace )
		{
			glDisable( GL_CULL_FACE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoCullFace )
		if( m_bAdjustAlphaFunc )
		{
			glEnable( GL_ALPHA_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glAlphaFunc( m_gleAlphaFunc, m_glcAlphaRef );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustAlphaFunc )
		if( m_bAdjustBlendFunc )
		{
			glEnable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( m_gleBlendFactorS, m_gleBlendFactorD );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustBlendFunc )
		if( m_nTextureIndex >= 0 )
		{
			ASSERT( m_nTextureIndex < GL_VIEWS_TEXTURE_COUNT );
			glEnable( GL_TEXTURE_2D );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBindTexture(
				GL_TEXTURE_2D,
				pView3D->m_TextureIds[m_nTextureIndex]
				);
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_nTextureIndex >= 0 )
		glBegin(GL_QUADS);
			for( int nVertexIdx = 0; nVertexIdx < 4; nVertexIdx++ )
			{
				glTexCoord2f(
					m_arrTextureCoords[nVertexIdx].m_fX,
					m_arrTextureCoords[nVertexIdx].m_fY
					);
				glVertex3fv(
					m_arrPoints[nVertexIdx].arr
					);
			}
		glEnd();
		if( m_nTextureIndex >= 0 )
		{
			ASSERT( m_nTextureIndex < GL_VIEWS_TEXTURE_COUNT );
			glDisable( GL_TEXTURE_2D );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_nTextureIndex >= 0 )
		if( m_bAdjustBlendFunc )
		{
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustBlendFunc )
		if( m_bAdjustAlphaFunc )
		{
			glDisable( GL_ALPHA_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bAdjustAlphaFunc )
		if( m_bNoCullFace )
		{
			glEnable( GL_CULL_FACE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoCullFace )
		if( m_bNoDepth )
		{
			glEnable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
		} // if( m_bNoDepth )
	glPopMatrix();
}

/////////////////////////////////////////////////////////////////////////////
// C3DOuterScene

IMPLEMENT_DYNCREATE( C3DOuterScene, C3DObject );

volatile bool C3DOuterScene::g_bRenderOuterScene = true;
GLfloat C3DOuterScene::g_fBottomPlaneValX = 0.7f;
GLfloat C3DOuterScene::g_fBottomPlaneValY = 0.4f;
GLfloat C3DOuterScene::g_fBottomPlaneValZ = 0.7f;
GLfloat C3DOuterScene::g_fRingRadius = 0.80f;
GLint C3DOuterScene::g_nSphereValStacks = 12;

C3DOuterScene::C3DOuterScene()
	: C3DObject( GL_VIEWS_OUTER_SCENE_NAME )
{
}

C3DOuterScene::~C3DOuterScene()
{
}

void C3DOuterScene::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;

C3DSquare * pObjSquare = new C3DSquare;
	pObjSquare->m_nTextureIndex = GL_VIEWS_TEXTURE_IDX_BOTTOM_PLANE;
	pObjSquare->m_bNoDepth = true;
	pObjSquare->m_arrTextureCoords[3].m_fX = 2.0f;
	pObjSquare->m_arrTextureCoords[3].m_fY = 2.0f;
	pObjSquare->m_arrPoints[3] = _v3t( -g_fBottomPlaneValX, -g_fBottomPlaneValY, -g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[2].m_fX = 0.0f;
	pObjSquare->m_arrTextureCoords[2].m_fY = 2.0f;
	pObjSquare->m_arrPoints[2] = _v3t(  g_fBottomPlaneValX, -g_fBottomPlaneValY, -g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[1].m_fX = 0.0f;
	pObjSquare->m_arrTextureCoords[1].m_fY = 0.0f;
	pObjSquare->m_arrPoints[1] = _v3t(  g_fBottomPlaneValX, -g_fBottomPlaneValY,  g_fBottomPlaneValZ );
	pObjSquare->m_arrTextureCoords[0].m_fX = 2.0f;
	pObjSquare->m_arrTextureCoords[0].m_fY = 0.0f;
	pObjSquare->m_arrPoints[0] = _v3t( -g_fBottomPlaneValX, -g_fBottomPlaneValY,  g_fBottomPlaneValZ );
	pObjSquare->m_bUseNormal = true;
_v3t pt0(
	pObjSquare->m_arrPoints[1].x - pObjSquare->m_arrPoints[0].x,
	pObjSquare->m_arrPoints[1].y - pObjSquare->m_arrPoints[0].y,
	pObjSquare->m_arrPoints[1].z - pObjSquare->m_arrPoints[0].z
	);
_v3t pt1(
	pObjSquare->m_arrPoints[2].x - pObjSquare->m_arrPoints[0].x,
	pObjSquare->m_arrPoints[2].y - pObjSquare->m_arrPoints[0].y,
	pObjSquare->m_arrPoints[2].z - pObjSquare->m_arrPoints[0].z
	);
_v3t vNormal = pt0 ^ pt1;
	vNormal.normalize();
	pObjSquare->m_vecNormal = vNormal;
	AddChild( pObjSquare );

	GenerateSphere(
		GL_VIEWS_TEXTURE_IDX_RING,
		_v3t( 0.0f, 0.0f, 0.0f ),
		g_fRingRadius,
		g_nSphereValStacks,
		_ntr::get_pi()*2.0f,
		0.0f,
		- 0.3f,
		+ 0.7f,
		true
		);
}

bool C3DOuterScene::IsRenderSubtree()
{
	ASSERT_VALID( this );
	return g_bRenderOuterScene;
}

void C3DOuterScene::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_OUTER_SCENE;
}

/////////////////////////////////////////////////////////////////////////////
// C3DCamera

IMPLEMENT_DYNCREATE( C3DCamera, C3DObject );

const GLfloat C3DCamera::g_arrFovValues[GL_VIEWS_FOV_COUNT] =
{
	(( 10.0f*3.1415926535897932384626433832795f)/180.0f ), //  0  10 degrees
	(( 15.0f*3.1415926535897932384626433832795f)/180.0f ), //  1  15 degrees
	(( 20.0f*3.1415926535897932384626433832795f)/180.0f ), //  2  20 degrees
	(( 25.0f*3.1415926535897932384626433832795f)/180.0f ), //  3  25 degrees
	(( 30.0f*3.1415926535897932384626433832795f)/180.0f ), //  4  30 degrees
	(( 35.0f*3.1415926535897932384626433832795f)/180.0f ), //  5  35 degrees
	(( 40.0f*3.1415926535897932384626433832795f)/180.0f ), //  6  40 degrees
	(( 45.0f*3.1415926535897932384626433832795f)/180.0f ), //  7  45 degrees
	(( 50.0f*3.1415926535897932384626433832795f)/180.0f ), //  8  50 degrees
	(( 55.0f*3.1415926535897932384626433832795f)/180.0f ), //  9  55 degrees
	(( 60.0f*3.1415926535897932384626433832795f)/180.0f ), // 10  60 degrees
	(( 65.0f*3.1415926535897932384626433832795f)/180.0f ), // 11  65 degrees
	(( 70.0f*3.1415926535897932384626433832795f)/180.0f ), // 12  70 degrees
	(( 75.0f*3.1415926535897932384626433832795f)/180.0f ), // 13  75 degrees
	(( 80.0f*3.1415926535897932384626433832795f)/180.0f ), // 14  80 degrees
	(( 85.0f*3.1415926535897932384626433832795f)/180.0f ), // 15  85 degrees
	(( 90.0f*3.1415926535897932384626433832795f)/180.0f ), // 16  90 degrees
	(( 95.0f*3.1415926535897932384626433832795f)/180.0f ), // 17  95 degrees
	((100.0f*3.1415926535897932384626433832795f)/180.0f ), // 18 100 degrees
	((110.0f*3.1415926535897932384626433832795f)/180.0f ), // 19 110 degrees
	((120.0f*3.1415926535897932384626433832795f)/180.0f ), // 20 120 degrees
	((130.0f*3.1415926535897932384626433832795f)/180.0f ), // 21 130 degrees
	((140.0f*3.1415926535897932384626433832795f)/180.0f ), // 22 140 degrees
	((150.0f*3.1415926535897932384626433832795f)/180.0f ), // 23 150 degrees
	((160.0f*3.1415926535897932384626433832795f)/180.0f ), // 24 160 degrees
	((170.0f*3.1415926535897932384626433832795f)/180.0f ), // 25 170 degrees
};

volatile bool C3DCamera::g_bRenderCameraAxes = true;
GLfloat C3DCamera::g_fAxisLineLength = 0.025f;
GLfloat C3DCamera::g_fAxisLineWidth = 2.0f;

C3DCamera::C3DCamera(
	UINT nTreeDblClickCmdID, // = 0L
	LPCTSTR sName, // = GL_VIEWS_NONAME
	int nFovIndex, // = GL_VIEWS_FOV_DEF_INDEX
	GLfloat fNearPlane, // = 1.0f
	GLfloat fFarPlane // = 5.0f
	)
	: C3DObject( sName )
	, m_nTreeDblClickCmdID( nTreeDblClickCmdID )
	, m_fAspect( 1.0f )
	, m_nFovIndex( nFovIndex )
	, m_fNearPlane( fNearPlane )
	, m_fFarPlane( fFarPlane )
{
	ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
}

C3DCamera::~C3DCamera()
{
}

void C3DCamera::SerializeState(
	CArchive & ar
	)
{
	ASSERT_VALID( this );
	ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
	C3DObject::SerializeState( ar );
	if( ar.IsStoring() )
	{
		ar << m_fAspect;
		ar << m_nFovIndex;
		ar << m_fNearPlane;
		ar << m_fFarPlane;
	} // if( ar.IsStoring() )
	else
	{
		ar >> m_fAspect;
		ar >> m_nFovIndex;
		ASSERT( 0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT );
		if( !(0 <= m_nFovIndex && m_nFovIndex < GL_VIEWS_FOV_COUNT) )
			m_nFovIndex = GL_VIEWS_FOV_DEF_INDEX;
		ar >> m_fNearPlane;
		ar >> m_fFarPlane;
	} // else from if( ar.IsStoring() )
}

void C3DCamera::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	if( (!g_bRenderCameraAxes) )
		return;
	if( pCam == this && pObjMirror == NULL )
		return;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glLineWidth( g_fAxisLineWidth );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glBegin( GL_LINES );
			glColor3f( 1.0f, 0.0f, 0.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f( g_fAxisLineLength,              0.0f,              0.0f );
			glColor3f( 0.0f, 1.0f, 0.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f(              0.0f, g_fAxisLineLength,              0.0f );
			glColor3f( 0.0f, 0.0f, 1.0f );	glVertex3f( 0.0f, 0.0f, 0.0f );	glVertex3f(              0.0f,              0.0f, g_fAxisLineLength );
			glColor3f( 1.0f, 1.0f, 1.0f ); 
		glEnd();
	glPopMatrix();
}

void C3DCamera::OnTreeItemDblClick(
	CObjectHierarchyTreeCtrl * pTreeCtrl
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pTreeCtrl );
	ASSERT( pTreeCtrl->GetSafeHwnd() != NULL );
	ASSERT( ::IsWindow( pTreeCtrl->GetSafeHwnd() ) );
	ASSERT( m_hti != NULL );
	if( m_nTreeDblClickCmdID == 0L )
		return;
CFrameWnd * pFrame = pTreeCtrl->GetParentFrame();
	ASSERT_VALID( pFrame );
	if( pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)) )
	{
		pFrame = pFrame->GetParentFrame();
		ASSERT_VALID( pFrame );
		ASSERT( !pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)) );
	}
	pFrame->SendMessage( WM_COMMAND, m_nTreeDblClickCmdID );
}

void C3DCamera::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_CAMERA_OBJ;
}

/////////////////////////////////////////////////////////////////////////////
// C3DCube

IMPLEMENT_DYNCREATE( C3DCube, C3DObject );

volatile bool C3DCube::g_bRenderCubeObjects = true;
GLfloat C3DCube::g_fCubeVal = 0.03f;
GLfloat C3DCube::g_fTranslateCubeVal = C3DCube::g_fCubeVal * 2.2f;

C3DCube::C3DCube(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	bool bCenterCube // = false
	)
	: C3DObject( sName )
	, m_bCenterCube( bCenterCube )
{
}

C3DCube::~C3DCube()
{
}

GLfloat C3DCube::g_fCenterCubePlayPitch = _ntr::d2r(0.4f);
GLfloat C3DCube::g_fCenterCubePlayYaw = _ntr::d2r(0.3f);
GLfloat C3DCube::g_fCenterCubePlayRoll = _ntr::d2r(0.2f);

void C3DCube::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;

static const struct
{
	int m_nTextureIndex;
	struct {
		GLfloat m_fTextureX, m_fTextureY,
			m_fPosX, m_fPosY, m_fPosZ;
	} m_arrVertexData[4];
} g_arrSquaresInitData[] = 
{
	{	GL_VIEWS_TEXTURE_IDX_CUBE_WHITE,
		{
			{ 0.0f, 0.0f, -g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 0.0f,  g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 1.0f,  g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 1.0f, -g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_WHITE,
		{
			{ 1.0f, 0.0f, -g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 1.0f, -g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 1.0f,  g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 0.0f,  g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_ORANGE,
		{
			{ 1.0f, 0.0f,  g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 1.0f,  g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
			{ 0.0f, 1.0f,  g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 0.0f,  g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
		},
	},	
	{	GL_VIEWS_TEXTURE_IDX_CUBE_ORANGE,
		{
			{ 0.0f, 0.0f, -g_fCubeVal, -g_fCubeVal, -g_fCubeVal },
			{ 1.0f, 0.0f, -g_fCubeVal, -g_fCubeVal,  g_fCubeVal },
			{ 1.0f, 1.0f, -g_fCubeVal,  g_fCubeVal,  g_fCubeVal },
			{ 0.0f, 1.0f, -g_fCubeVal,  g_fCubeVal, -g_fCubeVal },
		},
	},	
};

	for(	int nSquareIdx = 0;
			nSquareIdx < (sizeof(g_arrSquaresInitData)/sizeof(g_arrSquaresInitData[0]));
			nSquareIdx++
		)
	{
		C3DSquare * pObjSquare = new C3DSquare;
		pObjSquare->m_nTextureIndex =
			g_arrSquaresInitData[nSquareIdx].m_nTextureIndex;
		for( int nVertexIdx = 0; nVertexIdx < 4; nVertexIdx++ )
		{
			pObjSquare->m_arrTextureCoords[nVertexIdx].m_fX =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fTextureX;
			pObjSquare->m_arrTextureCoords[nVertexIdx].m_fY =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fTextureY;
			pObjSquare->m_arrPoints[nVertexIdx].x =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosX;
			pObjSquare->m_arrPoints[nVertexIdx].y =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosY;
			pObjSquare->m_arrPoints[nVertexIdx].z =
				g_arrSquaresInitData[nSquareIdx].m_arrVertexData[nVertexIdx].m_fPosZ;
		}
		pObjSquare->m_bNoCullFace = true;
		pObjSquare->m_bAdjustAlphaFunc = true;
		pObjSquare->m_bAdjustBlendFunc = true;
		AddChild( pObjSquare );
	}
}

void C3DCube::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if( m_bCenterCube )
	{
		ASSERT_VALID( m_pParent );
		ASSERT_KINDOF( C3DModifier, m_pParent );
		m_pParent->LocalAdjustOrientation(
			g_fCenterCubePlayPitch,
			g_fCenterCubePlayYaw,
			g_fCenterCubePlayRoll
			);
	}
#endif // GL_VIEWS_DRAW_ANIMS
}

bool C3DCube::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	if( g_bRenderCubeObjects )
		return true;
	if( pObjChild->IsKindOf(RUNTIME_CLASS(C3DSquare)) )
		return false;
	return true;
}

void C3DCube::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText =
		m_bCenterCube
			? _T("center cube")
			: _T("leaf cube")
			;
	nTreeImageIndex =
		m_bCenterCube
			? GL_VIEWS_TREE_IMG_IDX_CUBE_CENTER
			: GL_VIEWS_TREE_IMG_IDX_CUBE_LEAF
			;
}

/////////////////////////////////////////////////////////////////////////////
// C3DPlanet

IMPLEMENT_DYNCREATE( C3DPlanet, C3DObject );

volatile bool C3DPlanet::g_bRenderPlanetObjects = true;
GLfloat C3DPlanet::g_fPlanetRadiusEarth = 0.04f;
GLfloat C3DPlanet::g_fPlanetRadiusMoon = 0.01f;
GLint C3DPlanet::g_nPlanetSphereStacks = 12;
GLfloat C3DPlanet::g_fAnglePlayStepPitchEarth = _ntr::d2r( 2.5f );
GLfloat C3DPlanet::g_fAnglePlayStepPitchMoon = _ntr::d2r( 4.5f );
GLfloat C3DPlanet::g_fAnglePlayStepYawEarth = _ntr::d2r( 1.5f );
GLfloat C3DPlanet::g_fAnglePlayStepYawMoon = _ntr::d2r( 2.5f );
GLfloat C3DPlanet::g_fAnglePlayStepRollEarth = _ntr::d2r( 4.0f );
GLfloat C3DPlanet::g_fAnglePlayStepRollMoon = _ntr::d2r( 6.0f );

C3DPlanet::C3DPlanet(
	LPCTSTR sName, // = GL_VIEWS_NONAME
	int nPlanetTextureIndex, // = GL_VIEWS_TEXTURE_IDX_EARTH
	GLint nPlanetStacks, // = C3DPlanet::g_nPlanetSphereStacks
	GLfloat fPlanetRadius, // = C3DPlanet::g_fPlanetRadiusEarth
	GLfloat fAnglePlayStepPitch, // = C3DPlanet::g_fAnglePlayStepPitchEarth
	GLfloat fAnglePlayStepYaw, // = C3DPlanet::g_fAnglePlayStepYawEarth
	GLfloat fAnglePlayStepRoll // = C3DPlanet::g_fAnglePlayStepRollMoon
	)
	: C3DObject( sName )
	, m_nPlanetTextureIndex( nPlanetTextureIndex )
	, m_nPlanetStacks( nPlanetStacks )
	, m_fPlanetRadius( fPlanetRadius )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DPlanet::~C3DPlanet()
{
}

void C3DPlanet::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	lpvCookie;
	GenerateSphere(
		m_nPlanetTextureIndex,
		_v3t( 0.0f, 0.0f, 0.0f ),
		m_fPlanetRadius,
		m_nPlanetStacks,
		_ntr::get_pi() * 2.0f,
		0.0f,
		- _ntr::get_pi() / 2.0f,
		+ _ntr::get_pi() / 2.0f
		);
}

bool C3DPlanet::IsRenderSubtreeItem( C3DObject * pObjChild )
{
	ASSERT_VALID( this );
	ASSERT_VALID( pObjChild );
	ASSERT( pObjChild->GetParent() == this );
	if( g_bRenderPlanetObjects )
		return true;
	if( pObjChild->IsKindOf(RUNTIME_CLASS(C3DSquare)) )
		return false;
	return true;
}

void C3DPlanet::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	//nTreeImageIndex = ....;
}

void C3DPlanet::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
	if( !g_bRenderPlanetObjects )
		return;
#ifdef GL_VIEWS_DRAW_ANIMS
	ASSERT_VALID( m_pParent );
	ASSERT_KINDOF( C3DModifier, m_pParent );
	m_pParent->LocalAdjustOrientation(
		m_fAnglePlayStepPitch,
		m_fAnglePlayStepYaw,
		m_fAnglePlayStepRoll
		);
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DText

IMPLEMENT_DYNCREATE( C3DText, C3DObject );

volatile bool C3DText::g_bRenderTextObjects = true;
GLfloat C3DText::g_fScaleModifier = 0.03f;
GLfloat C3DText::g_fAnglePlayStepPitchText = 0.0f;
GLfloat C3DText::g_fAnglePlayStepYawText = 0.0f;
GLfloat C3DText::g_fAnglePlayStepRollText = _ntr::d2r( 2.0f );

C3DText::C3DText(
	LPCTSTR sName, // = GL_VIEWS_NONAME // should be used to set displayed text or one letter
	COLORREF clrText, // = RGB(255,255,255)
	GLfloat fAnglePlayStepPitch, // = C3DText::g_fAnglePlayStepPitchText
	GLfloat fAnglePlayStepYaw,   // = C3DText::g_fAnglePlayStepYawText
	GLfloat fAnglePlayStepRoll   // = C3DText::g_fAnglePlayStepRollText
	)
	: C3DObject( sName )
	, m_fRed(	GLfloat(GetRValue(clrText)) / 255.0f )
	, m_fGreen(	GLfloat(GetGValue(clrText)) / 255.0f )
	, m_fBlue(	GLfloat(GetBValue(clrText)) / 255.0f )
	, m_fAnglePlayStepPitch( fAnglePlayStepPitch )
	, m_fAnglePlayStepYaw( fAnglePlayStepYaw )
	, m_fAnglePlayStepRoll( fAnglePlayStepRoll )
{
}

C3DText::~C3DText()
{
}

void C3DText::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
	if( !g_bRenderTextObjects )
		return;
#ifdef GL_VIEWS_DRAW_ANIMS
	ASSERT_VALID( m_pParent );
	ASSERT_KINDOF( C3DModifier, m_pParent );
	m_pParent->LocalAdjustOrientation(
		m_fAnglePlayStepPitch,
		m_fAnglePlayStepYaw,
		m_fAnglePlayStepRoll
		);
#endif // GL_VIEWS_DRAW_ANIMS
}

void C3DText::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	lpvCookie;
	pObjMirror;
	if( !g_bRenderTextObjects )
		return;
	if( !pView3D->m_Font3D.IsFontCreated() )
		return;
LPCTSTR sText = GetName();
	ASSERT( sText != NULL );
	if( _tcslen(sText) == 0 )
		return;
	glEnable( GL_ALPHA_TEST );
	GL_VIEWS_CHECK_OPENGL_ERROR
	glAlphaFunc( GL_NOTEQUAL, 0.0 );
	GL_VIEWS_CHECK_OPENGL_ERROR
		glDisable( GL_CULL_FACE );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
			glMultMatrixf( m_mtxLastTransformation.arr );
				glColor3f( m_fRed, m_fGreen, m_fBlue );
					glEnable( GL_BLEND );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glBlendFunc( GL_SRC_ALPHA, GL_ONE );
					GL_VIEWS_CHECK_OPENGL_ERROR
						glPushMatrix();
							glScalef(
								g_fScaleModifier,
								g_fScaleModifier,
								g_fScaleModifier
								);
							pView3D->m_Font3D.TextOut( sText );
						glPopMatrix();
					glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDisable( GL_BLEND );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glColor3f( 1.0f, 1.0f, 1.0f ); 
			glPopMatrix();
		glDisable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glEnable( GL_CULL_FACE );
	GL_VIEWS_CHECK_OPENGL_ERROR
}

void C3DText::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	strTreeItemText.Format(
		_T("text \"%s\""),
		GetName()
		);
	ASSERT( !strTreeItemText.IsEmpty() );
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_TEXT_OBJ;
}

/////////////////////////////////////////////////////////////////////////////
// C3DWnd

IMPLEMENT_DYNCREATE( C3DWnd, C3DObject );

_v3t C3DWnd::g_arrDefPointsWndAviPlayer[4] =
{
	_v3t( -0.12f, -0.11f, -0.35f ),
	_v3t(  0.12f, -0.11f, -0.35f ),
	_v3t(  0.12f,  0.11f, -0.35f ),
	_v3t( -0.12f,  0.11f, -0.35f ),
};

_v3t C3DWnd::g_arrDefPointsWndMirror[4] =
{
	_v3t(  0.12f, -0.11f,  0.35f ),
	_v3t( -0.12f, -0.11f,  0.35f ),
	_v3t( -0.12f,  0.11f,  0.35f ),
	_v3t(  0.12f,  0.11f,  0.35f ),
};

C3DWnd::C3DWnd(
	LPCTSTR sName, // = GL_VIEWS_WND_AVI_PLAYER
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_AVI_PLAYER
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndAviPlayer[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndAviPlayer[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndAviPlayer[2]
	_v3rct pt3  // = C3DWnd::g_arrDefPointsWndAviPlayer[3]
	)
	: C3DObject( sName )
	, m_nPlaneTextureIndex( nPlaneTextureIndex )
{
	m_arrWndPoints[0] = pt0;
	m_arrWndPoints[1] = pt1;
	m_arrWndPoints[2] = pt2;
	m_arrWndPoints[3] = pt3;
	_RecalcNormalAndContentArea();
}

C3DWnd::~C3DWnd()
{
}

void C3DWnd::_RecalcNormalAndContentArea()
{
	ASSERT_VALID( this );
_v3t pt0(
	m_arrWndPoints[1].x - m_arrWndPoints[0].x,
	m_arrWndPoints[1].y - m_arrWndPoints[0].y,
	m_arrWndPoints[1].z - m_arrWndPoints[0].z
	);
_v3t pt1(
	m_arrWndPoints[2].x - m_arrWndPoints[0].x,
	m_arrWndPoints[2].y - m_arrWndPoints[0].y,
	m_arrWndPoints[2].z - m_arrWndPoints[0].z
	);
	m_lastNormal = pt0 ^ pt1;
	m_lastNormal.normalize();

	stat_CalcWindowContentPlane(
		m_arrWndPoints,
		m_arrContentPoints
		);
}

void C3DWnd::stat_CalcWindowContentPlane(
	_v3t * arrWndPoints,
	_v3t * arrContentPoints
	)
{
	ASSERT( arrWndPoints != NULL );
	ASSERT( arrContentPoints != NULL );
	for( int i = 0; i< 4; i++ )
	{
//		arrContentPoints[i].x = arrWndPoints[i].x * 0.9700f;
//		arrContentPoints[i].y = arrWndPoints[i].y * 0.8000f;
//		arrContentPoints[i].z = arrWndPoints[i].z - arrWndPoints[i].z * 0.0001f;
		arrContentPoints[i].x = arrWndPoints[i].x * 0.9650f;
		arrContentPoints[i].y = arrWndPoints[i].y * ( (i < 2 ) ? 0.7850f : 0.7600f );
		arrContentPoints[i].z = arrWndPoints[i].z - arrWndPoints[i].z * 0.0001f;
	}
}

void C3DWnd::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	pObjMirror;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glEnable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glAlphaFunc( GL_NOTEQUAL, 0.0f );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glEnable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
			GL_VIEWS_CHECK_OPENGL_ERROR
				glEnable( GL_TEXTURE_2D );
				GL_VIEWS_CHECK_OPENGL_ERROR
					glBindTexture(
						GL_TEXTURE_2D,
						pView3D->m_TextureIds[m_nPlaneTextureIndex]
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDisable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
						glBegin(GL_QUADS);
							glNormal3fv( m_lastNormal.arr );
							glTexCoord2f(0.0f, 0.0f); glVertex3fv( m_arrWndPoints[0].arr );
							glTexCoord2f(1.0f, 0.0f); glVertex3fv( m_arrWndPoints[1].arr );
							glTexCoord2f(1.0f, 1.0f); glVertex3fv( m_arrWndPoints[2].arr );
							glTexCoord2f(0.0f, 1.0f); glVertex3fv( m_arrWndPoints[3].arr );
						glEnd();
					glEnable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glDisable( GL_TEXTURE_2D );
				GL_VIEWS_CHECK_OPENGL_ERROR
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_BLEND );
			GL_VIEWS_CHECK_OPENGL_ERROR
		glDisable( GL_ALPHA_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glPopMatrix();
}

/////////////////////////////////////////////////////////////////////////////
// C3DMirror

IMPLEMENT_DYNCREATE( C3DMirror, C3DWnd );

volatile bool C3DMirror::g_bRenderMirrors = true;

C3DMirror::C3DMirror(
	LPCTSTR sName, // = GL_VIEWS_WND_MIRROR
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_MIRROR
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndMirror[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndMirror[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndMirror[2]
	_v3rct pt3  // = C3DWnd::g_arrDefPointsWndMirror[3]
	)
	: C3DWnd( sName, nPlaneTextureIndex, pt0, pt1, pt2, pt3 )
	, m_bRenderingThisMirror( false )
{
}

C3DMirror::~C3DMirror()
{
}

void C3DMirror::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	if( m_bRenderingThisMirror )
		return;
	C3DWnd::OnRender( pCam, pView3D, lpvCookie, pObjMirror );
	if( !g_bRenderMirrors )
		return;
	if( pObjMirror != NULL )
		return; // multiply mirrors currently not supported by this sample
	m_bRenderingThisMirror = true;
		// clear mirror (make it black)
		glDisable( GL_DEPTH_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
			glMultMatrixf( m_mtxLastTransformation.arr );
				glBegin( GL_QUADS );
					glColor3f( 0.0f, 0.0f, 0.0f );
						glVertex3fv( m_arrContentPoints[0].arr );
						glVertex3fv( m_arrContentPoints[1].arr );
						glVertex3fv( m_arrContentPoints[2].arr );
						glVertex3fv( m_arrContentPoints[3].arr );
					glColor3f( 1.0f, 1.0f, 1.0f );
				glEnd();
			glPopMatrix();
		glEnable( GL_DEPTH_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		// create content mask in the stencil buffer
		glEnable( GL_STENCIL_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilFunc( GL_ALWAYS, 1, 1 );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glDisable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
				glPushMatrix();
				glMultMatrixf( m_mtxLastTransformation.arr );
					glBegin( GL_QUADS );
						glVertex3fv( m_arrContentPoints[0].arr );
						glVertex3fv( m_arrContentPoints[1].arr );
						glVertex3fv( m_arrContentPoints[2].arr );
						glVertex3fv( m_arrContentPoints[3].arr );
					glEnd();
				glPopMatrix();
			glEnable( GL_DEPTH_TEST );
			GL_VIEWS_CHECK_OPENGL_ERROR
			glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
			GL_VIEWS_CHECK_OPENGL_ERROR
		// compute reflection matrix and render mirror content
		glStencilFunc( GL_EQUAL, 1, 1 );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glPushMatrix();
				_mt _mtxReflection;
// TODO: calc dynamically m_arrContentPoints[0].z for _ptEq (plane equation)
_v4t _ptEq( m_lastNormal.x, m_lastNormal.y, m_lastNormal.z, m_arrContentPoints[0].z );
//_v4t _ptEq( 0.0f, 0.0f, -1.0f, 0.35f );
				_mtxReflection.load_reflection( _ptEq );
				glMultMatrixf( _mtxReflection.arr );
				double _ptEqDouble[4] = { _ptEq.a, _ptEq.b, _ptEq.c, _ptEq.d };
				glClipPlane( GL_CLIP_PLANE0, _ptEqDouble );
				GL_VIEWS_CHECK_OPENGL_ERROR
				glEnable( GL_CLIP_PLANE0 );
				GL_VIEWS_CHECK_OPENGL_ERROR
					glCullFace( GL_BACK );
					GL_VIEWS_CHECK_OPENGL_ERROR
						the3DPipe.GetRoot()->WalkTree(
							C3DObject::EWTQ_RENDER,
							pCam,
							pView3D,
							lpvCookie,
							this
							);
					glCullFace( GL_FRONT );
					GL_VIEWS_CHECK_OPENGL_ERROR
				glDisable( GL_CLIP_PLANE0 );
				GL_VIEWS_CHECK_OPENGL_ERROR
			glPopMatrix();
		glDisable( GL_STENCIL_TEST );
		GL_VIEWS_CHECK_OPENGL_ERROR
	m_bRenderingThisMirror = false;
}

void C3DMirror::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_WND_MIRROR;
}

/////////////////////////////////////////////////////////////////////////////
// C3DAviPlayer

IMPLEMENT_DYNCREATE( C3DAviPlayer, C3DWnd );

volatile bool C3DAviPlayer::g_bRenderAviPlayers = true;

static LPCTSTR g_sAviPlayerTempFilePath = _T(".\\AVITEMP.AVI");

C3DAviPlayer::C3DAviPlayer(
	LPCTSTR sName, // = GL_VIEWS_WND_AVI_PLAYER
	int nPlaneTextureIndex, // = GL_VIEWS_TEXTURE_IDX_WND_AVI_PLAYER
	int nSurfaceTextureIndex, // = GL_VIEWS_TEXTURE_IDX_AVI_SURFACE
	_v3rct pt0, // = C3DWnd::g_arrDefPointsWndAviPlayer[0]
	_v3rct pt1, // = C3DWnd::g_arrDefPointsWndAviPlayer[1]
	_v3rct pt2, // = C3DWnd::g_arrDefPointsWndAviPlayer[2]
	_v3rct pt3, // = C3DWnd::g_arrDefPointsWndAviPlayer[3]
	int nRenderWidth, // = 256
	int nRenderHeight // = 256
	)
	: C3DWnd( sName, nPlaneTextureIndex, pt0, pt1, pt2, pt3 )
	, m_bInitComplete( false )
	, m_pAviStream( NULL )
	, m_pGetFrame( NULL )
	, m_nAviWidth( 0 )
	, m_nAviHeight( 0 )
	, m_nRenderWidth( nRenderWidth )
	, m_nRenderHeight( nRenderHeight )
	, m_nRenderSize( nRenderWidth*nRenderHeight )
	, m_hDrawDib( NULL )
	, m_nFrameNumber( 0 )
	, m_nFrameCount( 0 )
	, m_hDC( NULL )
	, m_hBitmap( NULL )
	, m_pDibRawData( NULL )
	, m_nSurfaceTextureIndex( nSurfaceTextureIndex )
{
	ATLASSERT( m_nRenderWidth > 0 );
	ATLASSERT( m_nRenderHeight > 0 );
	ATLASSERT( m_nRenderSize > 0 );
}

C3DAviPlayer::~C3DAviPlayer()
{
	ASSERT( !m_bInitComplete );
}

void C3DAviPlayer::stat_AlertDisplay( LPCTSTR sText )
{
	ASSERT( sText != NULL );
	::AfxMessageBox( sText, MB_OK|MB_ICONERROR );
}

void C3DAviPlayer::OnThreadInit( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	C3DObject::OnThreadInit( lpvCookie );
	ATLASSERT( m_nRenderWidth > 0 );
	ATLASSERT( m_nRenderHeight > 0 );
	ATLASSERT( m_nRenderSize > 0 );
	
	if( !stat_IsFileExists(g_sAviPlayerTempFilePath) )
	{
		LPVOID lpvBuffer = NULL;
		DWORD dwSize = 0;
		if(		!stat_LoadResourceToMemory(
					MAKEINTRESOURCE(IDR_AVIFILE_FOR_PLAYER),
					_T("AVI"),
					&lpvBuffer,
					&dwSize
					)
			||	lpvBuffer == NULL
			||	dwSize == 0
			)
		{
			ASSERT( FALSE );
			stat_AlertDisplay( _T("Failed to load AVI resource") );
			return;
		}
	
		bool bFileSaved = false;
		try
		{
			CFile _file(
				g_sAviPlayerTempFilePath,
				CFile::modeCreate
					|CFile::modeWrite
					|CFile::typeBinary
					|CFile::shareExclusive
				);
			_file.Seek( 0, CFile::begin );
			_file.Write( lpvBuffer, dwSize );
			_file.Close();
			bFileSaved = true;
		} // try
		catch( CException * pXept )
		{
			pXept->Delete();
			ASSERT( FALSE );
		} // catch( CException * pXept )
		catch( ... )
		{
			ASSERT( FALSE );
		} // catch( ... )

		::free( lpvBuffer );

		if( !bFileSaved )
		{
			ASSERT( FALSE );
			stat_AlertDisplay( _T("Failed to extract AVI resource") );
			return;
		}
	} // if( !statIsFileExists(sFilePath) )
	
	::AVIFileInit();
	if( ::AVIStreamOpenFromFile(
			&m_pAviStream,
			g_sAviPlayerTempFilePath,
			streamtypeVIDEO,
			0,
			OF_READ,
			NULL
			) != 0
		)
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("AVI stream initialization failed") );
		return;
	}
	::AVIStreamInfo(
		m_pAviStream,
		&m_pAviInfo,
		sizeof(m_pAviInfo)
		);
	m_nAviWidth =
		m_pAviInfo.rcFrame.right - m_pAviInfo.rcFrame.left;
	m_nAviHeight =
		m_pAviInfo.rcFrame.bottom - m_pAviInfo.rcFrame.top;
	m_nFrameCount =
		::AVIStreamLength( m_pAviStream );
	m_hDC =
		::CreateCompatibleDC( NULL );
	if( m_hDC == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("Failed to alloc HDC for AVI stream") );
		return;
	}
	::memset( &m_dataBmpInfoHdr, 0, sizeof(BITMAPINFOHEADER) );
	m_dataBmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
	m_dataBmpInfoHdr.biPlanes = 1;
	m_dataBmpInfoHdr.biBitCount = 32; //24;
	m_dataBmpInfoHdr.biWidth = m_nRenderWidth;
	m_dataBmpInfoHdr.biHeight = m_nRenderHeight;
	m_dataBmpInfoHdr.biCompression = BI_RGB;
	m_hBitmap =
		::CreateDIBSection(
			m_hDC,
			(BITMAPINFO*)( &m_dataBmpInfoHdr ),
			DIB_RGB_COLORS,
			(void**)(&m_pDibRawData),
			NULL,
			NULL
			);
	if( m_hBitmap == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("Failed to create DIB section") );
		return;
	}
	ATLASSERT( m_pDibRawData != NULL );
	::SelectObject( m_hDC, m_hBitmap );
	m_pGetFrame =
		::AVIStreamGetFrameOpen(
			m_pAviStream,
			NULL
			);
	if( m_pGetFrame == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("AVI frame initialization failed") );
		return;
	}
	m_hDrawDib = ::DrawDibOpen();
	if( m_hDrawDib == NULL )
	{
		ASSERT( FALSE );
		stat_AlertDisplay( _T("DrawDibOpen() failed") );
		return;
	}
	m_bInitComplete = true;
}

void C3DAviPlayer::OnThreadDone( LPVOID lpvCookie )
{
	ASSERT_VALID( this );
	C3DObject::OnThreadDone( lpvCookie );
	if( m_hBitmap != NULL )
		::DeleteObject( m_hBitmap );
	if( m_hDrawDib != NULL )
		::DrawDibClose( m_hDrawDib );
	if( m_pGetFrame != NULL )
		::AVIStreamGetFrameClose( m_pGetFrame );
	if( m_pAviStream != NULL )
		::AVIStreamRelease( m_pAviStream );
	::AVIFileExit();
	try
	{
		CFile::Remove( g_sAviPlayerTempFilePath );
	} // try
	catch( CException * pXept )
	{
		pXept->Delete();
		ASSERT( FALSE );
	} // catch( CException * pXept )
	catch( ... )
	{
		ASSERT( FALSE );
	} // catch( ... )
	m_bInitComplete = false;
}

void C3DAviPlayer::OnRender(
	C3DCamera * pCam,
	C3DView * pView3D,
	LPVOID lpvCookie,
	C3DMirror * pObjMirror
	)
{
	ASSERT_VALID( this );
	ASSERT_VALID( pCam );
	ASSERT_VALID( pView3D );
	pCam;
	pView3D;
	lpvCookie;
	C3DWnd::OnRender( pCam, pView3D, lpvCookie, pObjMirror );
	if( (!g_bRenderAviPlayers) || (!m_bInitComplete) )
		return;
	glPushMatrix();
	glMultMatrixf( m_mtxLastTransformation.arr );
		glEnable( GL_TEXTURE_2D );
		GL_VIEWS_CHECK_OPENGL_ERROR
			glBindTexture(
				GL_TEXTURE_2D,
				pView3D->m_TextureIds[m_nSurfaceTextureIndex]
				);
			GL_VIEWS_CHECK_OPENGL_ERROR
			////////// begin get AVI frame //////////
				LPBITMAPINFOHEADER lpbi;
				lpbi = (LPBITMAPINFOHEADER)
					::AVIStreamGetFrame(
						m_pGetFrame,
						m_nFrameNumber
						);
				unsigned char * pAviFrameRawData = (unsigned char *)
					lpbi
					+ lpbi->biSize
					+ lpbi->biClrUsed * sizeof(RGBQUAD);
				::DrawDibDraw(
					m_hDrawDib,
					m_hDC,
					0, 0, m_nRenderWidth, m_nRenderHeight,
					lpbi,
					pAviFrameRawData,
					0, 0, m_nAviWidth, m_nAviHeight,
					0
					);
				unsigned char * pBufferRGBA = m_pDibRawData;
				for( int i = 0; i < m_nRenderSize; i++ )
				{
					unsigned char _byte = *pBufferRGBA;
					*pBufferRGBA = *(pBufferRGBA+2);
					pBufferRGBA ++;
					pBufferRGBA ++;
					*pBufferRGBA = _byte;
					pBufferRGBA ++;
					*pBufferRGBA = 255;
					pBufferRGBA ++;
				}
				glTexSubImage2D(
					GL_TEXTURE_2D, 0,
					0, 0, m_nRenderWidth, m_nRenderHeight,
					GL_RGBA, GL_UNSIGNED_BYTE,
					m_pDibRawData
					);
				GL_VIEWS_CHECK_OPENGL_ERROR
			////////// end get AVI frame //////////
			glBegin( GL_QUADS );
				glTexCoord2f( 0.0f, 0.0f ); glVertex3fv( m_arrContentPoints[0].arr );
				glTexCoord2f( 1.0f, 0.0f ); glVertex3fv( m_arrContentPoints[1].arr );
				glTexCoord2f( 1.0f, 1.0f ); glVertex3fv( m_arrContentPoints[2].arr );
				glTexCoord2f( 0.0f, 1.0f ); glVertex3fv( m_arrContentPoints[3].arr );
			glEnd();
		glDisable( GL_TEXTURE_2D );
		GL_VIEWS_CHECK_OPENGL_ERROR
	glPopMatrix();
}

void C3DAviPlayer::OnQueryTreeDisplayParms(
	CString & strTreeItemText,
	int & nTreeImageIndex
	)
{
	ASSERT_VALID( this );
	C3DObject::OnQueryTreeDisplayParms(
		strTreeItemText,
		nTreeImageIndex
		);
	nTreeImageIndex = GL_VIEWS_TREE_IMG_IDX_WND_AVI;
}

void C3DAviPlayer::OnPlay(
	LPVOID lpvCookie
	)
{
	ASSERT_VALID( this );
	lpvCookie;
#ifdef GL_VIEWS_DRAW_ANIMS
	if( m_bInitComplete && g_bRenderAviPlayers )
	{
		m_nFrameNumber ++;
		if( m_nFrameNumber >= m_nFrameCount )
			m_nFrameNumber = 0;
	} // if( m_bInitComplete && g_bRenderAviPlayers )
#endif // GL_VIEWS_DRAW_ANIMS
}

/////////////////////////////////////////////////////////////////////////////
// C3DTexture

void C3DTexture::_Destroy()
{
	if( m_sizeTexture.cx == 0 )
	{
		ASSERT( m_sizeTexture.cy == 0 );
		ASSERT( m_pData == NULL );
		return;
	}
	ASSERT( m_sizeTexture.cy != 0 );
	ASSERT( m_pData != NULL );
	free( m_pData );
	m_sizeTexture.cx = m_sizeTexture.cy = 0;
	m_pData = NULL;
	m_bAlphaLayerExist = false;
}

C3DTexture::C3DTexture()
	: m_sizeTexture( 0, 0 )
	, m_pData( NULL )
	, m_bAlphaLayerExist( false )
{
}

C3DTexture::~C3DTexture()
{
	_Destroy();
}

bool C3DTexture::LoadResourceBitmapAs32Bit(
	LPCTSTR lpszResource,
	unsigned char nAlphaClearVal // = 255
	)
{
	Empty();
CBitmap _bmp, _dib, *pBmpOldSrc = NULL, *pBmpOldDst = NULL;
	if( !_bmp.LoadBitmap(lpszResource) )
	{
		ASSERT( FALSE );
		return false;
	}
BITMAP _bmp_info;
	_bmp.GetBitmap( &_bmp_info );
	if(		_bmp_info.bmPlanes != 1
		||	_bmp_info.bmWidth <= 0
		||	_bmp_info.bmHeight <= 0
		)
	{
		ASSERT( FALSE );
		return false;
	}
const int nBytesPerPixel = 4;
int nPixelCount = _bmp_info.bmWidth * _bmp_info.bmHeight;
int nBytesCount = nPixelCount * nBytesPerPixel;

CWindowDC dcDesktop( NULL );
CDC dcSrc, dcDst;
	if(		!dcSrc.CreateCompatibleDC(&dcDesktop)
		||	!dcDst.CreateCompatibleDC(&dcDesktop)
		)
	{
		ASSERT( FALSE );
		return false;
	}
BITMAPINFOHEADER bih;
	::memset( (LPVOID)&bih, 0, sizeof(BITMAPINFOHEADER) );
	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = _bmp_info.bmWidth;
	bih.biHeight = _bmp_info.bmHeight;
	bih.biPlanes = 1;
	bih.biBitCount = 32; /*_bmp_info.bmBitsPixel*/;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = nPixelCount;
LPVOID pColorSurface = NULL;
HBITMAP	hDIB =
		::CreateDIBSection(
			dcDesktop.GetSafeHdc(),
			(LPBITMAPINFO)&bih,
			DIB_RGB_COLORS,
			&pColorSurface,
			NULL,
			NULL
			);
	if( hDIB == NULL || pColorSurface == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
	_dib.Attach( hDIB );
	pBmpOldSrc = dcSrc.SelectObject( &_bmp );
	pBmpOldDst = dcDst.SelectObject( &_dib );
	dcDst.BitBlt(
		0, 0, _bmp_info.bmWidth, _bmp_info.bmHeight,
		&dcSrc, 
		0, 0,
		SRCCOPY
		);
	dcDst.SelectObject( pBmpOldDst );
	dcSrc.SelectObject( pBmpOldSrc );
	m_pData = ::malloc( nBytesCount );
	if( m_pData == NULL )
	{
		ASSERT( FALSE );
		return false;
	}
	::memcpy( m_pData, pColorSurface, nBytesCount );
	m_sizeTexture.cx = _bmp_info.bmWidth;
	m_sizeTexture.cy = _bmp_info.bmHeight;
	ASSERT( !m_bAlphaLayerExist );
	m_bAlphaLayerExist = true;
unsigned char
		_uc0, _uc1, _uc2,
		*pData = (unsigned char *)m_pData;
	for( int i = 0; i < nPixelCount; i++ )
	{
		_uc0 = pData[ 4*i + 0 ];
		_uc1 = pData[ 4*i + 1 ];
		_uc2 = pData[ 4*i + 2 ];
		pData[ 4*i + 0 ] = _uc2;
		pData[ 4*i + 1 ] = _uc1;
		pData[ 4*i + 2 ] = _uc0;
		pData[ 4*i + 3 ] = nAlphaClearVal;
	}
	return true;
}

bool C3DTexture::AddAlphaLayer(
	unsigned char nAlphaClearVal // = 255
	)
{
	if( IsEmpty() || m_bAlphaLayerExist )
		return false;
unsigned char * pDataOld = (unsigned char *)m_pData;
	ASSERT( pDataOld != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
unsigned char * pDataNew = new unsigned char[4*nPixelCount];
	for( int i = 0; i < nPixelCount; i++ )
	{
		pDataNew[ 4*i + 0 ] = pDataOld[ 3*i + 0 ];
		pDataNew[ 4*i + 1 ] = pDataOld[ 3*i + 1 ];
		pDataNew[ 4*i + 2 ] = pDataOld[ 3*i + 2 ];
		pDataNew[ 4*i + 3 ] = nAlphaClearVal;
	}
	delete [] pDataOld;
	pDataOld = pDataNew;
	return true;
}


bool C3DTexture::SetAlphaLayer(
	unsigned char nAlphaSetVal // = 255
	)
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
	for( int i = 0; i < nPixelCount; i++ )
		pData[ 4*i + 3 ] = nAlphaSetVal;
	return true;
}

bool C3DTexture::SetAlphaLayerIf(
	unsigned char nAlphaSetVal,
	COLORREF clr
	)
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
unsigned char byteR = GetRValue(clr);
unsigned char byteG = GetGValue(clr);
unsigned char byteB = GetBValue(clr);
	for( int i = 0; i < nPixelCount; i++ )
	{
		if(		byteR == pData[ 4*i + 0 ]
			&&	byteG == pData[ 4*i + 1 ]
			&&	byteB == pData[ 4*i + 2 ]
			)
			pData[ 4*i + 3 ] = nAlphaSetVal;
	}
	return true;
}

bool C3DTexture::SetAlphaLayerNB()
{
	if( IsEmpty() || (!m_bAlphaLayerExist) )
		return false;
unsigned char * pData = (unsigned char *)m_pData;
	ASSERT( pData != NULL );
	ASSERT( m_sizeTexture.cx > 0 && m_sizeTexture.cy > 0 );
int nPixelCount = m_sizeTexture.cx * m_sizeTexture.cy;
	for( int i = 0; i < nPixelCount; i++ )
	{
		unsigned char byteR = pData[ 4*i + 0 ];
		pData[ 4*i + 3 ] = byteR;
	}
	return true;
}

/////////////////////////////////////////////////////////////////////////////
// C3DView

IMPLEMENT_DYNCREATE( C3DView, CObject );

C3DView::C3DView(
	HWND hWndOutput // = NULL
	)
	: m_hWndOutput( hWndOutput )
	, m_sizeView( GL_VIEWS_MIN_VIEW_DX, GL_VIEWS_MIN_VIEW_DY )
	, m_sizeViewNE( GL_VIEWS_MIN_VIEW_DX, GL_VIEWS_MIN_VIEW_DY )
	, m_hOpenGlContext( NULL )
	, m_nCameraIndex( 0L )
	, m_strViewName( _T("noname") )
{
	ASSERT( m_hWndOutput != NULL );
	ASSERT( ::IsWindow(m_hWndOutput) );
	_Init();
}

C3DView::~C3DView()
{
	_Done();
}

bool C3DView::IsViewVisible() const
{
	ASSERT_VALID( this );
	ASSERT( m_hWndOutput != NULL );
	ASSERT( ::IsWindow( ((HWND)m_hWndOutput) ) );
	if( ::IsWindowVisible( ((HWND)m_hWndOutput) ) )
	{
		return (
			::SendMessage(
				m_hWndOutput,
				GL_VIEWS_WM_QUERY_VIEW_VISIBILITY,
				0L,
				0L
				) != 0
			) ? true : false;
//		HWND hWndParent = ::GetParent( m_hWndOutput );
//		ASSERT( hWndParent != NULL );
//		ASSERT( ::IsWindow( hWndParent ) );
//		if( ::IsWindowVisible( hWndParent ) )
//			return true;
	}
	return false;
}

void C3DView::Lock()
{
	ASSERT_VALID( this );
	m_csGDI.Lock();
}

void C3DView::Unlock()
{
	ASSERT_VALID( this );
	m_csGDI.Unlock();
}

void C3DView::_Init()
{
	m_dc.SetViewSize( m_sizeView, false );
	m_dc.GetInternalBitmap();
	ASSERT( m_dc.GetSafeHdc() != NULL );
	if( m_hOpenGlContext != NULL )
		return;
static PIXELFORMATDESCRIPTOR pfd =
{
	sizeof(PIXELFORMATDESCRIPTOR),
	1,									
	PFD_SUPPORT_OPENGL|PFD_DRAW_TO_BITMAP,
	PFD_TYPE_RGBA,
	OnGlGetBufferBits(__EBB_COLOR),
	0, 0, 0, 0, 0, 0,
	OnGlGetBufferBits(__EBB_ALPHA),
	0,
	OnGlGetBufferBits(__EBB_ACCUM),
	0, 0, 0, 0,
	OnGlGetBufferBits(__EBB_Z),
	OnGlGetBufferBits(__EBB_STENCIL),
	OnGlGetBufferBits(__EBB_AUXILIARY),
	PFD_MAIN_PLANE,
	0,
	0, 0, 0
};
GLuint PixelFormat = ChoosePixelFormat( m_dc.GetSafeHdc(), &pfd );
	if(	!PixelFormat )
	{
		OnGlAlertDisplay( _T("Failed to choose the pixel format") );
		ASSERT( FALSE );
		return;
	}
	if( !SetPixelFormat( m_dc.GetSafeHdc(), PixelFormat, &pfd ) )
	{
		OnGlAlertDisplay( _T("Failed to set the pixel format") );
		ASSERT( FALSE );
		return;
	}
	m_hOpenGlContext = wglCreateContext( m_dc.GetSafeHdc() );
	if( m_hOpenGlContext == NULL )
	{
		OnGlAlertDisplay( _T("Failed to create OpenGL rendering context") );
		ASSERT( FALSE );
		return;
	}

	if( !wglMakeCurrent( m_dc.GetSafeHdc(), m_hOpenGlContext ) )
	{
		OnGlAlertDisplay( _T("Failed to activate OpenGL rendering context") );
		ASSERT( FALSE );
		return;
	}
struct
{
	LPCTSTR m_lpszResource;
} _texture_init_data[GL_VIEWS_TEXTURE_COUNT] =
{
	{ MAKEINTRESOURCE(IDB_BITMAP_CUBE_ORANGE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_CUBE_WHITE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_RING) },
	{ MAKEINTRESOURCE(IDB_BITMAP_BOTTOM_PLANE) },
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_AVI_PLAYER) },
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_AVI_PLAYER) }, // for surface
	{ MAKEINTRESOURCE(IDB_BITMAP_WND_MIRROR) },
	{ MAKEINTRESOURCE(IDB_BITMAP_EARTH) },
	{ MAKEINTRESOURCE(IDB_BITMAP_MOON) },
};
	
	glEnable( GL_TEXTURE_2D );
	GL_VIEWS_CHECK_OPENGL_ERROR

	for( int nTextureIdx = 0; nTextureIdx < GL_VIEWS_TEXTURE_COUNT; nTextureIdx++ )
	{
		C3DTexture objTexture;
		if( !objTexture.LoadResourceBitmapAs32Bit(
				_texture_init_data[nTextureIdx].m_lpszResource
				)
			)
		{
			ASSERT( FALSE );
			return;
		}
		ASSERT(	objTexture.IsAlphaLayerExist() );
		objTexture.SetAlphaLayerIf( 180, RGB(245,21,3) );
		objTexture.SetAlphaLayerIf( 0, RGB(0,0,0) );
		glGenTextures( 1, &m_TextureIds[nTextureIdx] );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glBindTexture( GL_TEXTURE_2D, m_TextureIds[nTextureIdx] );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexImage2D(
			GL_TEXTURE_2D,
			0,
			GL_RGBA,
			objTexture.GetWidth(),
			objTexture.GetHeight(),
			0,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
			objTexture.GetData()
			);
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		GL_VIEWS_CHECK_OPENGL_ERROR
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		GL_VIEWS_CHECK_OPENGL_ERROR
	}

	glDisable( GL_TEXTURE_2D );
	GL_VIEWS_CHECK_OPENGL_ERROR

	ASSERT( !m_Font3D.IsFontCreated() );
	VERIFY( m_Font3D.CreateFont( m_dc.GetSafeHdc() ) );
	
	if( !wglMakeCurrent( NULL, NULL ) )
	{
		OnGlAlertDisplay( _T("Failed to release contexts") );
		ASSERT( FALSE );
		return;
	}
}

void C3DView::_Done()
{
	m_Font3D.DeleteFont();
	if( m_hOpenGlContext != NULL )
	{
		if( !wglDeleteContext( m_hOpenGlContext ) )
		{
			OnGlAlertDisplay( _T("Failed to release rendering context") );
			ASSERT( FALSE );
		}
		m_hOpenGlContext = NULL;
	} // if( m_hOpenGlContext != NULL )
	m_dc.DeleteDC();
}

void C3DView::SetViewSize( CSize sizeView )
{
	ASSERT_VALID( this );
	ASSERT( m_sizeView.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeView.cy >= GL_VIEWS_MIN_VIEW_DY );
	ASSERT( m_sizeViewNE.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeViewNE.cy >= GL_VIEWS_MIN_VIEW_DY );
	Lock();
	m_sizeViewNE.cx = max( GL_VIEWS_MIN_VIEW_DX, sizeView.cx );
	m_sizeViewNE.cy = max( GL_VIEWS_MIN_VIEW_DY, sizeView.cy );
	Unlock();
}

void C3DView::AdjustViewSize(
	bool bLockView // = true
	)
{
	ASSERT_VALID( this );
	if(	m_sizeView == m_sizeViewNE )
		return;
	if( bLockView )
		Lock();
	ASSERT( m_sizeView.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeView.cy >= GL_VIEWS_MIN_VIEW_DY );
	ASSERT( m_sizeViewNE.cx >= GL_VIEWS_MIN_VIEW_DX && m_sizeViewNE.cy >= GL_VIEWS_MIN_VIEW_DY );
	_Done();
	m_sizeView = m_sizeViewNE;
	_Init();
	if( bLockView )
		Unlock();
}

void C3DView::DoStepLeft( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepLeft( fStepSize );
	Unlock();
}

void C3DView::DoStepUp( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepUp( fStepSize );
	Unlock();
}

void C3DView::DoStepForward( GLfloat fStepSize )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoStepForward( fStepSize );
	Unlock();
}

void C3DView::DoLookLeft( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookLeft( fAngleDegrees );
	Unlock();
}

void C3DView::DoLookUp( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookUp( fAngleDegrees );
	Unlock();
}

void C3DView::DoLookOwnAxis( GLfloat fAngleDegrees )
{
	ASSERT_VALID( this );
	Lock();
C3DCamera * pCam = the3DPipe.GetCamera( m_nCameraIndex );
	ASSERT_VALID( pCam );
	pCam->DoLookOwnAxis( fAngleDegrees );
	Unlock();
}

/////////////////////////////////////////////////////////////////////////////
// C3DPipeThread

C3DPipeThread the3DPipe;

C3DPipeThread::C3DPipeThread()
	: m_bInitComplete( false )
	, m_eventShutdownStart( FALSE, FALSE, NULL, NULL )
	, m_eventShutdownComplete( FALSE, FALSE, NULL, NULL )
	, m_eventRenderViews( FALSE, FALSE, NULL, NULL )
	, m_pRoot( new C3DObject(GL_VIEWS_ROOTNAME) )
	, m_bTimerAnimationEnabled( false )
{
	m_bAutoDelete = FALSE;
}

C3DPipeThread::~C3DPipeThread()
{
	ASSERT( !m_bAutoDelete );
	_ResourcesFree();
	m_arrCams.RemoveAll();
	delete m_pRoot;
}

bool C3DPipeThread::_ResourcesInit()
{
	return true;
}

void C3DPipeThread::_ResourcesFree()
{
int nViewCount = m_arrViews.GetSize();
	for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
	{
		C3DView * pView3D = m_arrViews[nViewIdx];
		ASSERT_VALID( pView3D );
		delete pView3D;
	}
	m_arrViews.RemoveAll();

	m_bInitComplete = false;
}

C3DObject * C3DPipeThread::GetRoot()
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_pRoot;
}

const C3DObject * C3DPipeThread::GetRoot() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_pRoot;
}

void C3DPipeThread::AddView( C3DView * pView3D )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT_VALID( pView3D );
	m_arrViews.Add( pView3D );
}

int C3DPipeThread::GetViewCount() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_arrViews.GetSize();
}

C3DView * C3DPipeThread::GetView( int nIndex )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT( 0 <= nIndex && nIndex < m_arrViews.GetSize() );
C3DView * pView3D = m_arrViews[nIndex];
	ASSERT_VALID( pView3D );
	return pView3D;
}


void C3DPipeThread::AddCamera( C3DCamera * pCam )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT_VALID( pCam );
	ASSERT( !m_bInitComplete );
	m_pRoot->AddChild( pCam );
	m_arrCams.Add( pCam );
}

int C3DPipeThread::GetCameraCount() const
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	return m_arrCams.GetSize();
}

C3DCamera * C3DPipeThread::GetCamera( int nIndex )
{
	ASSERT_VALID( this );
	ASSERT_VALID( m_pRoot );
	ASSERT( 0 <= nIndex && nIndex < m_arrCams.GetSize() );
C3DCamera * pCam = m_arrCams[nIndex];
	ASSERT_VALID( pCam );
	return pCam;
}

void C3DPipeThread::Render()
{
	m_eventRenderViews.SetEvent();
}

void C3DPipeThread::ShutdownAndWaitFor()
{
	if( !m_bInitComplete )
		return;
	m_eventShutdownStart.SetEvent();
	for(	CExtPopupMenuWnd::PassMsgLoop(false);
			!m_eventShutdownComplete.Lock(500);
			CExtPopupMenuWnd::PassMsgLoop(false)
		);
}

BOOL C3DPipeThread::InitInstance()
{
	if( !_ResourcesInit() )
	{
		ASSERT( FALSE );
		return FALSE;
	}
	ASSERT( m_arrViews.GetSize() > 0 );
	m_bInitComplete = true;

	return TRUE;
}

void C3DPipeThread::Delete()
{
	ASSERT( !m_bAutoDelete );
	_ResourcesFree();
	CWinThread::Delete();
}

int C3DPipeThread::Run()
{
	ASSERT( m_bInitComplete );

	ASSERT_VALID( m_pRoot );
	ObjectWriteAccessSet( true );
		m_pRoot->WalkTree(
			C3DObject::EWTQ_THREAD_INIT,
			NULL,
			NULL,
			NULL,
			NULL
			);
	ObjectWriteAccessSet( false );

int nViewCount = m_arrViews.GetSize();
	ASSERT( nViewCount > 0 );

CSyncObject * arrToWait[2] =
{
	&m_eventShutdownStart,
	&m_eventRenderViews
};
CMultiLock _ml( arrToWait, 2, FALSE );
	for( ; true; )
	{
		DWORD dwWaitResult = _ml.Lock( INFINITE, FALSE, 0 );
		if( dwWaitResult != (WAIT_OBJECT_0 + 1) )
			break;
		bool bResetWriteAccess = false;
		for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
		{
			C3DView * pView3D = m_arrViews[nViewIdx];
			ASSERT_VALID( pView3D );
			if( !pView3D->IsViewVisible() )
				continue;
			bool bSendMsgComplete = false;
			pView3D->Lock();
			ASSERT( !pView3D->m_strViewName.IsEmpty() );
			pView3D->AdjustViewSize( false );
			ASSERT( pView3D->Get3DDC().GetSafeHdc() != NULL );
			if( pView3D->Get3DDC().GetSafeHdc() != NULL )
			{
				if( !bResetWriteAccess )
				{
					ObjectWriteAccessSet( true );
					bResetWriteAccess = true;
				}
				ASSERT( GetCameraCount() > 0 );
				C3DCamera * pCam = GetCamera( pView3D->GetCameraIndex() );
				ASSERT_VALID( pCam );
				m_pRoot->WalkTree(
					C3DObject::EWTQ_TRANSFORM,
					pCam,
					pView3D,
					NULL,
					NULL
					);
				if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
				{
					pView3D->OnGlAlertDisplay( _T("Failed to activate OpenGL rendering context") );
					ASSERT( FALSE );
					continue;
				} // if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
				else
				{
#ifdef _DEBUG
					static int g_nRenderCounter = 0;
					TRACE2( "    >>> 3D-PIPE: rendering %s images (counter=%d)\n", (LPCTSTR)pView3D->m_strViewName, g_nRenderCounter );
					g_nRenderCounter++;
#endif // _DEBUG
					glShadeModel( GL_SMOOTH );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearDepth( 1.0f );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glEnable( GL_DEPTH_TEST );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDepthFunc( GL_LEQUAL );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glDepthMask( GL_TRUE );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClearStencil( 0 );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glEnable( GL_CULL_FACE );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glCullFace( GL_FRONT );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glFrontFace( GL_CW );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glClear(
						GL_COLOR_BUFFER_BIT
						|GL_DEPTH_BUFFER_BIT
						|GL_STENCIL_BUFFER_BIT
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glColorMask( 1, 1, 1, 1 );
					GL_VIEWS_CHECK_OPENGL_ERROR
					CSize _sizeView3D = pView3D->GetViewSize();
					pCam->m_fAspect =
						((GLfloat)_sizeView3D.cx)
						/ ((GLfloat)_sizeView3D.cy)
						;
					glViewport(
						0,
						0,
						_sizeView3D.cx,
						_sizeView3D.cy
						);
					GL_VIEWS_CHECK_OPENGL_ERROR
					glMatrixMode( GL_PROJECTION );
					GL_VIEWS_CHECK_OPENGL_ERROR
					_mt mtxPerspective;
					mtxPerspective.load_perspective(
						pCam->GetFov(),
						pCam->m_fAspect,
						pCam->m_fNearPlane,
						pCam->m_fFarPlane
						);
					glLoadMatrixf( mtxPerspective.arr );
					GL_VIEWS_CHECK_OPENGL_ERROR
					glMatrixMode( GL_MODELVIEW );
					GL_VIEWS_CHECK_OPENGL_ERROR
					_mt _mtxCameraInversion( pCam->m_mtxLastTransformation );
					_mtxCameraInversion.load_inversion();
					glLoadMatrixf(_mtxCameraInversion.arr);
					GL_VIEWS_CHECK_OPENGL_ERROR
					m_pRoot->WalkTree(
						C3DObject::EWTQ_RENDER,
						pCam,
						pView3D,
						NULL,
						NULL
						);
					glFinish();
					GL_VIEWS_CHECK_OPENGL_ERROR
					if( !wglMakeCurrent( NULL, NULL ) )
					{
						pView3D->OnGlAlertDisplay( _T("Failed to release contexts") );
						ASSERT( FALSE );
					}
					bSendMsgComplete = true;
				} // else from if( !wglMakeCurrent( pView3D->Get3DDC().GetSafeHdc(), pView3D->GetOpenGlContext() ) )
			} // if( pView3D->Get3DDC().GetSafeHdc() != NULL )
			pView3D->Unlock();
			if( bSendMsgComplete )
			{
				if( bResetWriteAccess )
				{
					ObjectWriteAccessSet( false );
					bResetWriteAccess = false;
				}
				::SendMessage(
					pView3D->GetOutputHWND(),
					GL_VIEWS_WM_RENDER_FRAME_COMPLETE,
					0L,
					0L
					);
			}
		} // for( int nViewIdx = 0; nViewIdx < nViewCount; nViewIdx++ )
		if( m_bTimerAnimationEnabled )
		{
#ifdef _DEBUG
			static int g_nRecalcPosCounter = 0;
			TRACE1( "    >>> 3D-PIPE: recalculating object positions (counter=%d)\n", g_nRecalcPosCounter );
			g_nRecalcPosCounter++;
#endif // _DEBUG
			if( !bResetWriteAccess )
			{
				ObjectWriteAccessSet( true );
				bResetWriteAccess = true;
			}
			m_pRoot->WalkTree(
				C3DObject::EWTQ_PLAY,
				NULL,
				NULL,
				NULL,
				NULL
				);
		} // if( m_bTimerAnimationEnabled )
		if( bResetWriteAccess )
		{
			ObjectWriteAccessSet( false );
			bResetWriteAccess = false;
		}
	} // for( ; true; )
	
	ObjectWriteAccessSet( true );
		m_pRoot->WalkTree(
			C3DObject::EWTQ_THREAD_DONE,
			NULL,
			NULL,
			NULL,
			NULL
			);
	ObjectWriteAccessSet( false );
	
	_ResourcesFree();
	m_eventShutdownComplete.SetEvent();
	return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CCameraSelectionComboBox

IMPLEMENT_DYNAMIC( CCameraSelectionComboBox, CExtComboBox )

CCameraSelectionComboBox::CCameraSelectionComboBox()
{
}

CCameraSelectionComboBox::~CCameraSelectionComboBox()
{
}

BEGIN_MESSAGE_MAP( CCameraSelectionComboBox, CExtComboBox )
	//{{AFX_MSG_MAP(CCameraSelectionComboBox)
	//}}AFX_MSG_MAP
	ON_CONTROL_REFLECT(CBN_SELENDOK,OnReflectCbnSelEndOK)
END_MESSAGE_MAP()

void CCameraSelectionComboBox::SyncCameraWithSelectedItem(
	bool bSetFocusToView // = false
	)
{
	ASSERT_VALID( this );
CWnd * pWnd = GetParent();
	ASSERT_VALID( pWnd );
	ASSERT_KINDOF( CExtToolControlBar, pWnd );
CChildView * pChildView = STATIC_DOWNCAST( CChildView, pWnd->GetParent() );
	ASSERT_VALID( pChildView );
C3DView * pView3D = pChildView->m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
int nCurSel = GetCurSel();
	ASSERT( 0 <= nCurSel && nCurSel < GL_VIEWS_CAMERA_COUNT );
int nCamIdxOld = pView3D->GetCameraIndex();
	ASSERT( 0 <= nCamIdxOld && nCamIdxOld < GL_VIEWS_CAMERA_COUNT );
	if( nCamIdxOld != nCurSel )
	{
		pView3D->SetCameraIndex( nCurSel );
	} // if( nCamIdxOld != nCurSel )
	if( bSetFocusToView )
		pChildView->SetFocus();
	if( nCamIdxOld != nCurSel )
	{
		CFrameWnd * pFrame = GetParentFrame();
		ASSERT_VALID( pFrame );
		CMainFrame * pMainFrame =
			DYNAMIC_DOWNCAST( CMainFrame, pFrame );
		if( pMainFrame == NULL )
		{
			pMainFrame =
				DYNAMIC_DOWNCAST( CMainFrame, pFrame->GetParentFrame() );
			ASSERT_VALID( pMainFrame );
		}
		pMainFrame->SyncCameraFovValue();
	} // if( nCamIdxOld != nCurSel )
	the3DPipe.Render();
}

int CCameraSelectionComboBox::SetCurSel(
	int nSelect,
	bool bSetFocusToView // = false
	)
{
	ASSERT( 0 <= nSelect && nSelect < GL_VIEWS_CAMERA_COUNT );
int nRetVal = CExtComboBox::SetCurSel( nSelect );
	SyncCameraWithSelectedItem( bSetFocusToView );
	return nRetVal;
}

void CCameraSelectionComboBox::OnReflectCbnSelEndOK()
{
	ASSERT_VALID( this );
bool bSetFocusToView = false;
HWND hWndFocus = ::GetFocus();
	if( hWndFocus == m_hWnd )
		bSetFocusToView = true;
	SyncCameraWithSelectedItem( bSetFocusToView );
}


/////////////////////////////////////////////////////////////////////////////
// CCameraFovComboBox

IMPLEMENT_DYNAMIC( CCameraFovComboBox, CExtComboBox )

CCameraFovComboBox::CCameraFovComboBox()
{
}

CCameraFovComboBox::~CCameraFovComboBox()
{
}

BEGIN_MESSAGE_MAP( CCameraFovComboBox, CExtComboBox )
	//{{AFX_MSG_MAP(CCameraFovComboBox)
	//}}AFX_MSG_MAP
	ON_CONTROL_REFLECT(CBN_SELENDOK,OnReflectCbnSelEndOK)
END_MESSAGE_MAP()

void CCameraFovComboBox::SyncCameraWithSelectedItem(
	bool bSetFocusToView // = false
	)
{
	ASSERT_VALID( this );
CWnd * pWnd = GetParent();
	ASSERT_VALID( pWnd );
	ASSERT_KINDOF( CExtToolControlBar, pWnd );
CChildView * pChildView = STATIC_DOWNCAST( CChildView, pWnd->GetParent() );
	ASSERT_VALID( pChildView );
C3DView * pView3D = pChildView->m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
int nCamIdx = pView3D->GetCameraIndex();
	ASSERT( 0 <= nCamIdx && nCamIdx < GL_VIEWS_CAMERA_COUNT );
C3DCamera * pCam = the3DPipe.GetCamera( nCamIdx );
	ASSERT_VALID( pCam );

int nFovIdx = GetCurSel();
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );

bool bRender = false;
	the3DPipe.ObjectWriteAccessSet( true );
		if( pCam->m_nFovIndex != nFovIdx ) 
		{
			pCam->m_nFovIndex = nFovIdx;
			bRender = true;
		}
	the3DPipe.ObjectWriteAccessSet( false );

	if( bSetFocusToView )
		pChildView->SetFocus();
	if( bRender )
	{
		CFrameWnd * pFrame = GetParentFrame();
		ASSERT_VALID( pFrame );
		CMainFrame * pMainFrame =
			DYNAMIC_DOWNCAST( CMainFrame, pFrame );
		if( pMainFrame == NULL )
		{
			pMainFrame =
				DYNAMIC_DOWNCAST( CMainFrame, pFrame->GetParentFrame() );
			ASSERT_VALID( pMainFrame );
		}
		pMainFrame->SyncCameraFovValue( nCamIdx, nFovIdx );
		the3DPipe.Render();
	}
}

int CCameraFovComboBox::SetCurSel(
	int nSelect,
	bool bSetFocusToView // = false
	)
{
	ASSERT( 0 <= nSelect && nSelect < GL_VIEWS_FOV_COUNT );
int nRetVal = CExtComboBox::SetCurSel( nSelect );
	SyncCameraWithSelectedItem( bSetFocusToView );
	return nRetVal;
}

void CCameraFovComboBox::OnReflectCbnSelEndOK()
{
	ASSERT_VALID( this );
bool bSetFocusToView = false;
HWND hWndFocus = ::GetFocus();
	if( hWndFocus == m_hWnd )
		bSetFocusToView = true;
	SyncCameraWithSelectedItem( bSetFocusToView );
}


/////////////////////////////////////////////////////////////////////////////
// CObjectHierarchyTreeCtrl

IMPLEMENT_DYNAMIC( CObjectHierarchyTreeCtrl, CTreeCtrl )

CObjectHierarchyTreeCtrl::CObjectHierarchyTreeCtrl()
{
}

CObjectHierarchyTreeCtrl::~CObjectHierarchyTreeCtrl()
{
}

BEGIN_MESSAGE_MAP( CObjectHierarchyTreeCtrl, CTreeCtrl )
	//{{AFX_MSG_MAP(CObjectHierarchyTreeCtrl)
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CObjectHierarchyTreeCtrl message handlers

void CObjectHierarchyTreeCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CTreeCtrl::OnLButtonDblClk(nFlags, point);
UINT htFlags = 0L;
HTREEITEM _hti = CTreeCtrl::HitTest( point, &htFlags );
	if(		_hti != NULL
		&&	(htFlags&(TVHT_ONITEM)) != 0
		)
	{
		C3DObject * pObj = (C3DObject *)
			CTreeCtrl::GetItemData( _hti );
		ASSERT( pObj != NULL );
		ASSERT_VALID( pObj );
		ASSERT_KINDOF( C3DObject, pObj );
		pObj->OnTreeItemDblClick( this );
	}

}

/////////////////////////////////////////////////////////////////////////////
// CChildView

IMPLEMENT_DYNAMIC( CChildView, CWnd )

GLfloat CChildView::g_fStepRotationAngle = 2.0f;
GLfloat CChildView::g_fStepWalkSize = 0.02f;

CChildView::CChildView(
	UINT nIdResourceCursor
	)
	: m_wndGlPanel( nIdResourceCursor )
{
	ASSERT( (ID_SELCAM9) == (ID_SELCAM8 + 1) );
	ASSERT( (ID_SELCAM8) == (ID_SELCAM7 + 1) );
	ASSERT( (ID_SELCAM7) == (ID_SELCAM6 + 1) );
	ASSERT( (ID_SELCAM6) == (ID_SELCAM5 + 1) );
	ASSERT( (ID_SELCAM5) == (ID_SELCAM4 + 1) );
	ASSERT( (ID_SELCAM4) == (ID_SELCAM3 + 1) );
	ASSERT( (ID_SELCAM3) == (ID_SELCAM2 + 1) );
	ASSERT( (ID_SELCAM2) == (ID_SELCAM1 + 1) );
	ASSERT( (ID_SELCAM1) == (ID_SELCAM0 + 1) );
}

CChildView::~CChildView()
{
}

BEGIN_MESSAGE_MAP( CChildView, CWnd )
	//{{AFX_MSG_MAP(CChildView)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_COMMAND(ID_ROTATE_ABOUT_X0, OnRotateAboutX0)
	ON_COMMAND(ID_ROTATE_ABOUT_X1, OnRotateAboutX1)
	ON_COMMAND(ID_ROTATE_ABOUT_Y0, OnRotateAboutY0)
	ON_COMMAND(ID_ROTATE_ABOUT_Y1, OnRotateAboutY1)
	ON_COMMAND(ID_ROTATE_ABOUT_Z0, OnRotateAboutZ0)
	ON_COMMAND(ID_ROTATE_ABOUT_Z1, OnRotateAboutZ1)
	ON_COMMAND(ID_TRANSLATE_X0, OnTranslateX0)
	ON_COMMAND(ID_TRANSLATE_X1, OnTranslateX1)
	ON_COMMAND(ID_TRANSLATE_Y0, OnTranslateY0)
	ON_COMMAND(ID_TRANSLATE_Y1, OnTranslateY1)
	ON_COMMAND(ID_TRANSLATE_Z0, OnTranslateZ0)
	ON_COMMAND(ID_TRANSLATE_Z1, OnTranslateZ1)
	ON_WM_SETFOCUS()
	ON_WM_CONTEXTMENU()
	ON_UPDATE_COMMAND_UI(ID_BTN_MENU_ROTATION, OnUpdateEnabledBtnInBar)
	ON_COMMAND(ID_CAM_FOV_INC, OnCamFovInc)
	ON_UPDATE_COMMAND_UI(ID_CAM_FOV_INC, OnUpdateCamFovInc)
	ON_COMMAND(ID_CAM_FOV_DEC, OnCamFovDec)
	ON_UPDATE_COMMAND_UI(ID_CAM_FOV_DEC, OnUpdateCamFovDec)
	//}}AFX_MSG_MAP
	
	ON_COMMAND_RANGE( ID_SELCAM0, ID_SELCAM9, OnSelectCamera )
	ON_UPDATE_COMMAND_UI_RANGE( ID_SELCAM0, ID_SELCAM9, OnUpdateSelectCamera )
	
	ON_UPDATE_COMMAND_UI( ID_CAMERA_SELECTION_COMBO, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_CAMERA_FOV_COMBO, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_BTN_MENU_ROTATION, OnUpdateEnabledBtnInBar )
	ON_UPDATE_COMMAND_UI( ID_BTN_MENU_TRANSLATION, OnUpdateEnabledBtnInBar )
	
	ON_REGISTERED_MESSAGE(
		CExtPopupMenuWnd::g_nMsgPrepareMenu,
		OnExtMenuPrepare
		)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChildView message handlers

LRESULT CChildView::OnExtMenuPrepare(WPARAM wParam, LPARAM lParam)
{
	lParam;
CExtPopupMenuWnd::MsgPrepareMenuData_t * pData =
		reinterpret_cast
			< CExtPopupMenuWnd::MsgPrepareMenuData_t * >
			( wParam );
	ASSERT( pData != NULL );
CExtPopupMenuWnd * pPopup = pData->m_pPopup;
	ASSERT( pPopup != NULL );

	// remove camera selection combo command
INT nPos = pPopup->ItemFindPosForCmdID(ID_CAMERA_SELECTION_COMBO);
	if( nPos >= 0 )
		pPopup->ItemRemove( nPos );
	if(		pPopup->ItemGetCount() > 0
		&&	pPopup->ItemGetInfo(0).IsSeparator()
		)
		pPopup->ItemRemove( 0 );

	// replace camera FOV combo command
	nPos = pPopup->ItemFindPosForCmdID(ID_CAMERA_FOV_COMBO);
	if( nPos >= 0 )
	{
		pPopup->ItemRemove( nPos );
		if( pPopup->ItemInsert(
				(UINT)CExtPopupMenuWnd::TYPE_POPUP,
				nPos,
				_T("Camera FOV"),
				NULL,
				m_hWnd
				)
			)
		{
			CExtPopupMenuWnd * pSubMenu =
				pPopup->ItemGetPopup( nPos );
			ASSERT_VALID( pSubMenu );
			VERIFY(
				pSubMenu->ItemInsert(
					ID_CAM_FOV_INC,
					-1,
					NULL,
					NULL,
					m_hWnd
					)
				);
			VERIFY(
				pSubMenu->ItemInsert(
					ID_CAM_FOV_DEC,
					-1,
					NULL,
					NULL,
					m_hWnd
					)
				);
			UpdateDialogControls( this, TRUE );
		}
#ifdef _DEBUG
		else
		{
			ASSERT( FALSE );
		}
#endif // _DEBUG
	}
	
	// remove leading separator if exist
	if(		pPopup->ItemGetCount() > 0
		&&	pPopup->ItemGetInfo(0).IsSeparator()
		)
		pPopup->ItemRemove( 0 );

	return TRUE;
}

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if( ! (CExtWRB < CWnd > ::PreCreateWindow(cs) ) )
		return FALSE;

	cs.dwExStyle &= ~(WS_EX_STATICEDGE|WS_EX_CLIENTEDGE);
	cs.style &= ~WS_BORDER;
	cs.style |= WS_CLIPSIBLINGS;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL);

	return TRUE;
}

void CChildView::OnPaint() 
{
CPaintDC dcPaint( this );
	CExtPaintManager::stat_ExcludeChildAreas(
		dcPaint.GetSafeHdc(),
		GetSafeHwnd()
		);
}

BOOL CChildView::OnEraseBkgnd(CDC* pDC) 
{
	pDC;
	return TRUE;
}


int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if( CExtWRB < CWnd >::OnCreate(lpCreateStruct) == -1 )
	{
		ASSERT( FALSE );
		return -1;
	}

	m_wndToolbar.m_bPresubclassDialogMode = true;
	if( !m_wndToolbar.Create(
			_T(""),
			this,
			AFX_IDW_DIALOGBAR,
			WS_CHILD|WS_VISIBLE
				|CBRS_ALIGN_TOP|CBRS_TOOLTIPS
				|CBRS_FLYBY|CBRS_SIZE_DYNAMIC
			)
		)
	{
		ASSERT( FALSE );
		return -1;
	}
	if( !m_wndToolbar.LoadToolBar(IDR_TOOLBAR_INVIEW) )
	{
		ASSERT( FALSE );
		return -1;
	}
	if( !m_wndToolbar.InitNavigationBar() )
		return -1;
	
	if(	!m_wndGlPanel.Create(
			this,
			GL_VIEWS_ID_VIEW_DLG_CTRL_ID
			)
		)
	{
		ASSERT( FALSE );
		return -1;
	}
	m_wndGlPanel.SetFont( CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT)) );

	CWnd::RepositionBars( 0, 0xFFFF, GL_VIEWS_ID_VIEW_DLG_CTRL_ID );
	return 0;
}


void CChildView::OnSize(UINT nType, int cx, int cy) 
{
	CExtWRB < CWnd > ::OnSize(nType, cx, cy);
	if( nType != SIZE_MINIMIZED )
		CWnd::RepositionBars( 0, 0xFFFF, GL_VIEWS_ID_VIEW_DLG_CTRL_ID );
}

void CChildView::OnUpdateEnabledBtnInBar(CCmdUI* pCmdUI) 
{
	ASSERT( pCmdUI != NULL );
	pCmdUI->Enable();
}

void CChildView::OnSetFocus(CWnd* pOldWnd) 
{
	CWnd ::OnSetFocus(pOldWnd);
	m_wndGlPanel.SetFocus();
}

void CChildView::OnContextMenu(CWnd* pWnd, CPoint point) 
{
	pWnd;
	point;
}

void CChildView::OnRotateAboutX0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookUp( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutX1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookUp( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnRotateAboutY0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookLeft( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutY1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookLeft( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnRotateAboutZ0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookOwnAxis( g_fStepRotationAngle );
	the3DPipe.Render();
}
void CChildView::OnRotateAboutZ1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoLookOwnAxis( -g_fStepRotationAngle );
	the3DPipe.Render();
}

void CChildView::OnTranslateX0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepLeft( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateX1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepLeft( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnTranslateY0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepUp( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateY1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepUp( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnTranslateZ0() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepForward( g_fStepWalkSize );
	the3DPipe.Render();
}
void CChildView::OnTranslateZ1() 
{
	ASSERT_VALID( this );
C3DView * pView3D = m_wndGlPanel.GetView3D();
	if( pView3D == NULL )
		return;
	ASSERT_VALID( pView3D );
	pView3D->DoStepForward( -g_fStepWalkSize );
	the3DPipe.Render();
}

void CChildView::OnSelectCamera( UINT nCmdID )
{
	ASSERT_VALID( this );
	ASSERT( ID_SELCAM0 <= nCmdID && nCmdID <= ID_SELCAM9 );
int nCameraIndex = nCmdID - ID_SELCAM0;
	ASSERT( 0 <= nCameraIndex && nCameraIndex < GL_VIEWS_CAMERA_COUNT );
	m_wndToolbar.m_wndCameraSelCombo.SetCurSel( nCameraIndex );
}
void CChildView::OnUpdateSelectCamera(CCmdUI* pCmdUI)
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
	ASSERT( ID_SELCAM0 <= pCmdUI->m_nID && pCmdUI->m_nID <= ID_SELCAM9 );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
	ASSERT( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT );
int nCameraIndexToCmp = pCmdUI->m_nID - ID_SELCAM0;
	ASSERT( 0 <= nCameraIndexToCmp && nCameraIndexToCmp < GL_VIEWS_CAMERA_COUNT );
	pCmdUI->Enable( TRUE );
	pCmdUI->SetRadio(
		(nCameraIndexReal == nCameraIndexToCmp)
			? TRUE
			: FALSE
		);
}


void CChildView::OnCamFovInc() 
{
	ASSERT_VALID( this );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
		return;
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	if( nFovIdx == (GL_VIEWS_FOV_COUNT-1) )
		return;
	m_wndToolbar.m_wndCameraFovCombo.SetCurSel( nFovIdx+1, false );
}
void CChildView::OnUpdateCamFovInc(CCmdUI* pCmdUI) 
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	pCmdUI->Enable(
		(nFovIdx < (GL_VIEWS_FOV_COUNT-1)) ? TRUE : FALSE
		);
}

void CChildView::OnCamFovDec() 
{
	ASSERT_VALID( this );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
		return;
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	if( nFovIdx == 0 )
		return;
	m_wndToolbar.m_wndCameraFovCombo.SetCurSel( nFovIdx-1, false );
}
void CChildView::OnUpdateCamFovDec(CCmdUI* pCmdUI) 
{
	ASSERT_VALID( this );
	ASSERT( pCmdUI != NULL );
int nCameraIndexReal = m_wndToolbar.m_wndCameraSelCombo.GetCurSel();
	ASSERT(
			( 0 <= nCameraIndexReal && nCameraIndexReal < GL_VIEWS_CAMERA_COUNT )
		||	nCameraIndexReal < 0
		);
	if( nCameraIndexReal < 0 )
	{
		pCmdUI->Enable( FALSE );
		return;
	}
C3DCamera * pCam = the3DPipe.GetCamera( nCameraIndexReal );
	ASSERT_VALID( pCam );
int nFovIdx = pCam->m_nFovIndex;
	ASSERT( 0 <= nFovIdx && nFovIdx < GL_VIEWS_FOV_COUNT );
	pCmdUI->Enable(
		(nFovIdx > 0) ? TRUE : FALSE
		);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Foss Software Inc
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions