/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: AknPhysics engine
*
*/
#include <e32debug.h>
#include <aknphysics.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>
#include <alf/alfdirectclient.h>
#include "aknphysicsconstants.h"
#include "aknphysicsengine.h"
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::NewL
// ---------------------------------------------------------------------------
//
CAknPhysicsEngine* CAknPhysicsEngine::NewL( CAknPhysics* aPhysics )
{
CAknPhysicsEngine* self = CAknPhysicsEngine::NewLC( aPhysics );
CleanupStack::Pop( self );
return self;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::NewLC
// ---------------------------------------------------------------------------
//
CAknPhysicsEngine* CAknPhysicsEngine::NewLC( CAknPhysics* aPhysics )
{
CAknPhysicsEngine* self =
new ( ELeave ) CAknPhysicsEngine( aPhysics );
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::~CAknPhysicsEngine
// ---------------------------------------------------------------------------
//
CAknPhysicsEngine::~CAknPhysicsEngine()
{
DeletePhysics();
delete iAlfClient;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::WorldExists
// ---------------------------------------------------------------------------
//
TBool CAknPhysicsEngine::WorldExists() const
{
return iWorldId != NULL;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::CreateWorld
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::CreateWorld( const TInt& aGravity )
{
dInitODE();
iWorldId = dWorldCreate();
dWorldSetERP( iWorldId, REAL( KErpFactor ) );
dWorldSetCFM( iWorldId, REAL( KCfmFactor ) );
dWorldSetGravity( iWorldId, 0, 0, -REAL( aGravity ) );
dWorldSetAutoDisableFlag(iWorldId, 1);
iSpace = dSimpleSpaceCreate(0);
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::CreatePlanes
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::CreatePlanes( const TInt64& aIh,
const TInt64& aX,
const TInt64& aY,
const TInt64& aR )
{
// Create bounding planes
iPlaneTop = dCreatePlane( iSpace, REAL( aX ), REAL( aY ), 0, REAL( 0 ) );
iPlaneBottom = dCreatePlane( iSpace, REAL( -aX ), REAL( -aY ), 0, REAL( -aIh ) );
iPlaneLeft = dCreatePlane( iSpace, REAL( aY ), REAL( aX ), 0, REAL( 0 ) );
iPlaneRight = dCreatePlane( iSpace, REAL( -aY ), REAL( -aX ), 0, REAL( -aR ) );
// create z-plane to apply friction
// bottom, z axle
iPlaneFriction = dCreatePlane( iSpace, 0, 0, REAL( 1 ), REAL( -KZDepth ) );
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::CreateViewBody
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::CreateViewBody(
const TInt64& aWidth, const TInt64& aHeight, const TInt64& aMass )
{
// create viewbox
iViewBox = dCreateBox( iSpace, REAL( aWidth ), REAL( aHeight ), REAL( KZDepth ) );
iViewBody = dBodyCreate( iWorldId );
dBodySetAutoDisableFlag( iViewBody, 1 );
dGeomSetBody( iViewBox, iViewBody );
// Set mass for body
dMass mass;
dMassSetBoxTotal( &mass,
REAL( aMass ), REAL( aWidth ), REAL( aHeight ), REAL( KZDepth ) );
dBodySetMass( iViewBody, &mass );
// Create JointGroup to handle contacts
iContactGroup = dJointGroupCreate( 0 );
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::SetViewBodyPosition
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::SetViewBodyPosition( const TPoint& aPosition )
{
if ( iViewBody )
{
iViewPosition = aPosition;
TInt64 x, y;
x = iViewPosition.iX;
y = iViewPosition.iY;
dBodySetPosition(
iViewBody,
REAL( x ),
REAL( y ),
REAL( -KZDepth/2 ) );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::GetViewBodyPosition
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::GetViewBodyPosition( TPoint& aPosition )
{
const dReal *pos = dBodyGetPosition( iViewBody );
iViewPosition.iX = dFLOAT( pos[0] );
iViewPosition.iY = dFLOAT( pos[1] );
aPosition = iViewPosition;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::ResetViewBodyForceAndVelocity
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::ResetViewBodyForceAndVelocity()
{
if ( iViewBody )
{
dBodySetForce( iViewBody, 0, 0, 0);
dBodySetLinearVel( iViewBody, 0, 0, 0 );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::EnableViewBody
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::EnableViewBody()
{
if ( iViewBody )
{
dBodyEnable( iViewBody );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::IsViewBodyEnabled
// ---------------------------------------------------------------------------
//
TBool CAknPhysicsEngine::IsViewBodyEnabled() const
{
if ( iViewBody )
{
return dBodyIsEnabled( iViewBody );
}
return EFalse;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::ApplyDragForce
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::ApplyDragForce(
const TPoint& aDrag, const TInt& aMoveTime )
{
if ( iViewBody )
{
TReal step = aMoveTime * KStepFactor;
// calculate and apply force from drag
dVector3 force;
dWorldImpulseToForce(
iWorldId,
REAL( step ),
REAL( TInt64( aDrag.iX / 3 ) ),
REAL( TInt64( aDrag.iY / 3 ) ),
0,
force );
dBodyAddForce( iViewBody, force[0], force[1], force[2] );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::CollisionState
// ---------------------------------------------------------------------------
//
const CAknPhysicsEngine::TAknPhysicsCollision& CAknPhysicsEngine::CollisionState()
{
return iCollision;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::ResetCollisionState
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::ResetCollisionState()
{
iCollision = EAknPhysicsNoCollision;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::TakePhysicsStep
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::TakePhysicsStep()
{
// Call collision detection (step
dSpaceCollide ( iSpace, this , &CAknPhysicsEngine::CallbackFunc );
// Take a simulation step
dWorldQuickStep( iWorldId, REAL( 0.06 ) );
// Remove all joints in the contact
dJointGroupEmpty( iContactGroup );
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::DeletePhysics
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::DeletePhysics()
{
if(iWorldId)
{
// Remove all joints in the contact
dJointGroupEmpty( iContactGroup );
dJointGroupDestroy( iContactGroup );
iContactGroup = NULL;
dBodyDestroy( iViewBody );
iViewBody = NULL;
dGeomDestroy( iPlaneTop );
iPlaneTop = NULL;
dGeomDestroy( iPlaneBottom );
iPlaneBottom = NULL;
dGeomDestroy( iPlaneLeft );
iPlaneLeft = NULL;
dGeomDestroy( iPlaneRight );
iPlaneRight = NULL;
dGeomDestroy( iPlaneFriction );
iPlaneFriction = NULL;
dGeomDestroy( iViewBox );
iViewBox = NULL;
dSpaceDestroy( iSpace );
iSpace = NULL;
dWorldDestroy( iWorldId );
iWorldId = NULL;
dCloseODE(); // final clean-up;
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::SetFriction
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::SetFriction( TReal aFriction )
{
iFriction = aFriction;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::ResetFriction
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::ResetFriction()
{
iFriction = KDefaultFriction;
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::StartFpsLogging
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::StartFpsLogging()
{
if ( !iAlfClient )
{
TRAP_IGNORE( iAlfClient = new ( ELeave ) RAlfDirectClient );
}
if ( iAlfClient )
{
if ( iLogUid == KNullUid )
{
CEikonEnv* env = CEikonEnv::Static();
if ( env )
{
CEikAppUi* appUi = static_cast<CEikAppUi*>( env->AppUi() );
if ( appUi && appUi->Application() )
{
iLogUid = appUi->Application()->AppDllUid();
}
}
}
iAlfClient->MeasureFPS( iLogUid.iUid, ETrue );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::StopFpsLogging
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::StopFpsLogging()
{
if ( iAlfClient )
{
iAlfClient->MeasureFPS( iLogUid.iUid, EFalse );
}
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::CAknPhysicsEngine
// ---------------------------------------------------------------------------
//
CAknPhysicsEngine::CAknPhysicsEngine( CAknPhysics* aPhysics )
: iWorldId( NULL ),
iFriction( KDefaultFriction ),
iPhysics( aPhysics ),
iLogUid( KNullUid )
{
}
// ---------------------------------------------------------------------------
// CAknPhysicsEngine::ConstructL
// ---------------------------------------------------------------------------
//
void CAknPhysicsEngine::ConstructL()
{
}
// -----------------------------------------------------------------------------
// CAknPhysicsEngine::CallbackFunc()
// -----------------------------------------------------------------------------
//
void CAknPhysicsEngine::CallbackFunc( void* aData, dGeomID o0, dGeomID o1 )
{
static_cast<CAknPhysicsEngine*>( aData )->HandleCollisionBetween( o0,o1 );
}
// -----------------------------------------------------------------------------
// CAknPhysicsEngine::HandleCollisionBetween()
// -----------------------------------------------------------------------------
//
void CAknPhysicsEngine::HandleCollisionBetween(
dGeomID aObject1, dGeomID aObject2 )
{
TSize viewSize( iPhysics->ViewSize() );
TSize worldSize( iPhysics->WorldSize() );
TInt surfaceErp( iPhysics->SurfaceErp() );
TInt surfaceCfm( iPhysics->SurfaceCfm() );
dBodyID b1 = dGeomGetBody(aObject1);
dBodyID b2 = dGeomGetBody(aObject2);
dContact contacts[KMaxContacts];
TInt numc = dCollide(aObject1,
aObject2,
KMaxContacts,
&contacts[0].geom,
sizeof(dContact));
for (TInt i = 0 ; i < numc; i++)
{
contacts[i].surface.mode = dContactApprox1;
if(aObject1 == iPlaneFriction || aObject2 == iPlaneFriction)
{
if( iCollision == EAknPhysicsNoCollision )
{
contacts[i].surface.mu = REAL( iFriction );
}
else
{
contacts[i].surface.mu = 0;
}
}
else if(aObject1 == iPlaneTop
|| aObject2 == iPlaneTop)
{
contacts[i].surface.mu = 0; // No friction
TInt offScreen = 0;
if( iPhysics->Landscape() )
{
offScreen = -(iViewPosition.iX - viewSize.iWidth/2);
offScreen =
offScreen > viewSize.iWidth ? viewSize.iWidth : offScreen;
}
else
{
offScreen = -(iViewPosition.iY - viewSize.iHeight/2);
offScreen =
offScreen > viewSize.iHeight ? viewSize.iHeight : offScreen;
}
if( offScreen )
{
contacts[i].surface.mode |= dContactSoftCFM | dContactSoftERP;
contacts[i].surface.soft_erp =
REAL( TReal( surfaceErp )/offScreen );
contacts[i].surface.soft_cfm =
REAL( TReal( surfaceCfm )/offScreen);
iCollision = EAknPhysicsTopCollision;
}
else
iCollision = EAknPhysicsNoCollision;
RDebug::Print(_L("TOP COLLISION"));
}
else if(aObject1 == iPlaneBottom
|| aObject2 == iPlaneBottom )
{
contacts[i].surface.mu = 0; // No friction
TInt offScreen = 0;
if(iPhysics->Landscape())
{
offScreen =
(iViewPosition.iX + viewSize.iWidth/2) - viewSize.iWidth;
offScreen =
offScreen > viewSize.iWidth ? viewSize.iWidth : offScreen;
}
else
{
offScreen =
(iViewPosition.iY + viewSize.iHeight/2) - worldSize.iHeight;
offScreen =
offScreen > viewSize.iHeight ? viewSize.iHeight : offScreen;
}
if( offScreen )
{
contacts[i].surface.mode |= dContactSoftCFM | dContactSoftERP;
contacts[i].surface.soft_erp =
REAL( TReal( surfaceErp )/offScreen );
contacts[i].surface.soft_cfm =
REAL( TReal( surfaceCfm )/offScreen);
iCollision = EAknPhysicsBottomCollision;
}
else
iCollision = EAknPhysicsNoCollision;
RDebug::Print(_L("BOTTOM COLLISION"));
}
else
{
iCollision = EAknPhysicsNoCollision;
contacts[i].surface.mu = 0; // No friction
contacts[i].surface.mode |=
dContactBounce | dContactSoftCFM | dContactSoftERP;
contacts[i].surface.soft_cfm = REAL(KCfmSoftFactor);
contacts[i].surface.soft_erp = REAL(KErpFactor);
contacts[i].surface.bounce = REAL(KBounceFactor); // small bounce
}
dJointID c = dJointCreateContact(iWorldId, iContactGroup, &contacts[i]);
dJointAttach(c,b1,b2);
}
}