DXA.CPP

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: dxa.cpp
*
* This file contains functions that make use of DirectX Animation
* to render animation to a ddraw surface.
*
***************************************************************************/

#include "dxa.h"

IDAViewPtr DXA::_view = NULL;

/*
* StartOle
*
* Start the Ole services.
*/
struct StartOle {
StartOle() { CoInitialize( NULL ); }
~StartOle() { CoUninitialize(); }
} _inst_StartOle;

/*
* dump_com_error
*
* Dump the com error should an exception occurs in InitDXAViewObj.
* This is mainly for debugging purpose.
*/
void dump_com_error( _com_error &e )
{
char buf[2048];

sprintf(buf, _T( "Oops - hit an error!\n\tCode = %08lx\n\tCode meaning = %s\n" ),
e.Error(), e.ErrorMessage());
OutputDebugString(buf);
}

/*
* initDXAViewObj
*
* Create the DAView object, construct the model, then start the model.
*
*/
BOOL DXA::initDXAViewObj(IUnknown *ddsurf)
{
try {
IDAStaticsPtr e;

// Create the statics object
e.CreateInstance( L"DirectAnimation.DAStatics" );

// Create and establish the view
_view.CreateInstance( L"DirectAnimation.DAView" );

// Import Media (images and sound in this case). The
// GetCurrentDirectory() (which will return the path to either
// the debug or release sub directory) is used as a starting
// point for relative file importing.
TCHAR szMediaBase[_MAX_PATH];
TCHAR szImg[_MAX_PATH];
TCHAR szGeo[_MAX_PATH];
TCHAR szSnd[_MAX_PATH];

GetModuleFileName(GetModuleHandle("Union.exe"),
szMediaBase,sizeof(szMediaBase));
char *pos = strrchr( szMediaBase, (int)'\\' );
int result = pos - szMediaBase + 1;
szMediaBase[result]= NULL;

_tcscat(szMediaBase,_T("../../../../../media/"));

_tcscpy(szImg,szMediaBase);
_tcscpy(szGeo,szMediaBase);
_tcscpy(szSnd,szMediaBase);

_tcscat(szImg,_T("image/"));
_tcscat(szGeo,_T("geometry/"));
_tcscat(szSnd,_T("sound/"));

IDAGeometryPtr rawSphere =
e->ImportGeometry( _bstr_t(szGeo) + _bstr_t( "sphere.x" ) );
IDAGeometryPtr rawCube =
e->ImportGeometry( _bstr_t(szGeo) + _bstr_t( "cube.x" ) );
IDAGeometryPtr rawCone =
e->ImportGeometry( _bstr_t(szGeo) + _bstr_t( "cone.x" ) );
IDAGeometryPtr rawCylinder =
e->ImportGeometry( _bstr_t(szGeo) + _bstr_t( "cylinder.x" ) );

// Import a sound, supply null as the second argument since we don't
// want a length.
IDASoundPtr snd = e->ImportSound( _bstr_t(szSnd) + _bstr_t( "etherial.mp2" ) )->Sound->Loop();

// Size the objects down.
IDATransform3Ptr scaler = e->Scale3UniformAnim( e->DANumber( 0.25 ) );
IDAGeometryPtr sphere = rawSphere->Transform( scaler );
IDAGeometryPtr cube = rawCube->Transform( scaler );
IDAGeometryPtr cone = rawCone->Transform( scaler );
IDAGeometryPtr cylinder = rawCylinder->Transform( scaler );

IDAGeometryPtr xcone = cone->Transform( e->Rotate3Anim(
e->GetXVector3(), e->DANumber( 3.1415927 ) ) );

IDAColorPtr color1 = myColor( 0, 2, e );
IDAColorPtr color2 = myColor( 0.25, 3, e );
IDAColorPtr color3 = myColor( 0.5, 4, e );
IDAColorPtr color4 = myColor( 0.75, 1, e );

// Shapes to start with (many with time varying colors)
IDAGeometryPtr shapes[] = {
sphere->DiffuseColor( color1 ),
sphere->DiffuseColor( color1 ),
cone->DiffuseColor( color2 ),
xcone->DiffuseColor( color2 ),
cube->DiffuseColor( color3 ),
cube->DiffuseColor( color3 ),
cylinder->DiffuseColor( color4 ) };

// Places to Translate them to.
double tls[][3] = { { 0.75, 0, 0 },
{ -0.75, 0, 0 },
{ 0, 0.75, 0 },
{ 0, -0.75, 0 },
{ 0, 0, 0.75 },
{ 0, 0, -0.75 },
{ 0, 0, 0 } };

// Go ahead and accumulate up the Translated geometry by cycling
// through the arrays.
IDAGeometryPtr geo = e->EmptyGeometry;
for ( int i = 0; i < sizeof( shapes ) / sizeof( shapes[0] ); i++ ) {
IDATransform3Ptr xf = e->Translate3Anim( e->DANumber( tls[i][0] ),
e->DANumber( tls[i][1] ), e->DANumber( tls[i][2] ) );
geo = e->UnionGeometry( geo, shapes[i]->Transform( xf ) );
}

// Set the whole thing in motion
IDATransform3Ptr xf =
e->Compose3( e->Rotate3Anim( e->GetXVector3(), e->LocalTime ),
e->Compose3( e->Rotate3Anim( e->GetZVector3(), e->Mul( e->LocalTime, e->DANumber( 1.9 ) ) ),
e->Compose3( e->Rotate3Anim( e->GetYVector3(), e->Mul( e->LocalTime, e->DANumber( 3.1415927 ) ) ),
e->Scale3UniformAnim( e->DANumber( 0.75 ) ) ) ) );

geo = geo->Transform( xf );

// Render into an image, and overlay atop a solid image.
IDAImagePtr geoIm = geometryImage( e->UnionGeometry( geo, e->DirectionalLight ), e );

IDAImagePtr model = e->Overlay( geoIm, e->SolidColorImage( e->White ) );

// Set the size of the viewport. Not doing this (as done in this
// case) will render the animation accross the entire primary surface.
_view->SetViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
// Let the view draw to the primary surface.
_view->IDirectDrawSurface = ddsurf;
_view->CompositeDirectlyToTarget = TRUE;
// Start the model on the view.
_view->StartModel(model, snd, 0);

} catch (_com_error &e) {
dump_com_error( e );
return false;
}
return true;
}

BOOL DXA::resetDXASurfaces(IUnknown *ddsurf)
{
try {
_view->IDirectDrawSurface = ddsurf;
} catch (_com_error &e) {
dump_com_error( e );
return false;
}
return true;
}

/*
* geometryImage
* Renders a geometry as an image
*/
IDAImagePtr DXA::geometryImage( IDAGeometryPtr geo, IDAStaticsPtr e ) {
IDACameraPtr cam = e->PerspectiveCameraAnim( e->DANumber( 1 ),
e->DANumber( 0 ) )->Transform( e->Translate3Anim(
e->DANumber( 0 ), e->DANumber( 0 ), e->DANumber( 2 ) ) );
IDATransform2Ptr sc = e->Scale2UniformAnim( e->DANumber( 0.1 ) );

return geo->Render( cam )->Transform( sc );
}

/*
* myColor
* Create a animated color whose saturation varies over time.
*/
IDAColorPtr DXA::myColor( double hue, double saturationRate,
IDAStaticsPtr e ) {
IDANumberPtr sat = e->Add( e->Mul( e->Sin( e->Mul( e->LocalTime,
e->DANumber( saturationRate ) ) ), e->DANumber( 0.5 ) ), e->DANumber( 0.5 ) );

return e->ColorHslAnim( e->DANumber( hue ), sat, e->DANumber( 0.5 ) );
}

/*
* tick
* Ask DA to sample and display the model.
*/
void DXA::tick() {
static double startTime = 0;
struct _timeb timebuffer;

if (_view != NULL) {
_ftime( &timebuffer );
double thisTime = timebuffer.time + ( timebuffer.millitm / 1000.0 );

if ( startTime == 0 ) {
startTime = thisTime;
thisTime = 0;
}
else
thisTime -= startTime;

_view->Tick(thisTime);
_view->Render();
}
}