photo.cpp

00001 
00002 #include "main.h"
00003 #include "photo.h"
00004 #include "videoTexture.h"
00005 
00006 
00007 // default constructor
00008 Photo::Photo()
00009 : nxActor(NULL),
00010   node(NULL),
00011   texture(NULL),
00012   videoTexture(NULL),
00013   particelNode(NULL),
00014   particelEmitter(NULL),
00015   particelAffector(NULL),
00016   zoomStartPosition(0.0f),
00017   zoomDirection(0.0f),
00018   zoomTimeStart(0),
00019   zoomTimeForWay(0),
00020   zoomTimeStep(0),
00021   zoomState(PZS_ZOOMING_FINISHED)
00022 {
00023         logger->log( "Photo ctor()" );
00024 }
00025 
00026 // destructor
00027 Photo::~Photo()
00028 {
00029         if ( NULL != this->nxActor )
00030         {
00031                 // todo: Avoid release calls while the scene is simulating (in between simulate() and fetchResults() calls)
00032                 nxScene->releaseActor( *this->nxActor );
00033                 this->nxActor = NULL;
00034         }
00035         if ( NULL != this->texture )
00036         {
00037                 // do not remove texture with "driver->removeTexture()" -> other Photos might still use this texture
00038                 this->texture = NULL;
00039         }
00040         if ( NULL != this->particelEmitter )
00041         {
00042                 this->particelEmitter->drop();
00043                 this->particelEmitter = NULL;
00044         }
00045         if ( NULL != this->particelAffector )
00046         {
00047                 this->particelAffector->drop();
00048                 this->particelAffector = NULL;
00049         }
00050         if ( NULL != this->particelNode )
00051         {
00052                 this->particelNode->removeAllAffectors();
00053                 this->particelNode->remove();
00054                 this->particelNode = NULL;
00055         }
00056         if ( NULL != this->node )
00057         {
00058                 this->node->remove();
00059                 this->node = NULL;
00060         }
00061         if ( NULL != this->videoTexture )
00062         {
00063                 delete this->videoTexture;
00064                 this->videoTexture = NULL;
00065         }
00066 
00067         logger->log( "Photo dtor()" );
00068 }
00069 
00070 
00071 // reorient/reposition the irrlicht sceneNode from data from physX nxActor
00072 // play video
00073 // animate zoom
00074 ERR_TYPE Photo::update( u32 timeMS )
00075 {
00076         if ( (NULL == this->nxActor) || (NULL == this->node) )
00077         {
00078                 return ERR_PHOTO_CREATEACTOR_FAILED;
00079         }
00080 
00081         // transform physx pose to irrlicht
00082         if ( ! this->nxActor->isSleeping() )
00083         {
00084                 // this code's from the Beginner Lesson 101 tutorial
00085                 // modified to use for a irrlicht matrix4
00086                 NxMat34 pose = this->nxActor->getGlobalPose();
00087 
00088                 const NxVec3 pos = pose.t;
00089                 const NxMat33 orient = pose.M;
00090                 matrix4 irrMat; // a 4x4 matrix in the irrlicht engine
00091                 orient.getColumnMajorStride4( &irrMat[0] );
00092                 pos.get( &irrMat[12] );
00093                 //clear the elements we don't need:
00094                 irrMat[3] = irrMat[7] = irrMat[11] = 0.0f;
00095                 irrMat[15] = 1.0f;
00096 
00097                 // with that newly made matrix, let's use it to transform/rotate the node
00098                 if ( NULL != this->node )
00099                 {
00100                         this->node->setPosition( irrMat.getTranslation() );
00101                         this->node->setRotation( irrMat.getRotationDegrees() );
00102                 }
00103         }
00104 
00105         // render video into videoTexture, if present
00106         if ( NULL != this->videoTexture )
00107         {
00108                 this->videoTexture->update( timeMS );
00109         }
00110 
00111         // animate zoom, if active
00112         if ( PZS_ZOOMING_FINISHED != this->zoomState )
00113         {
00114                 // zoom is active, check if time for way is over
00115                 NxU32 t = ( timeMS - this->zoomTimeStart );
00116                 NxVec3 pos = this->zoomStartPosition;
00117 
00118                 if ( t < this->zoomTimeForWay )
00119                 {
00120                         // not over yet, animate
00121                         pos += this->zoomDirection * ((NxF32) t) * this->zoomTimeStep;
00122                         this->nxActor->moveGlobalPosition( pos );
00123                 }
00124                 else
00125                 {
00126                         // time is over (either to camera or to table)
00127                         // check if zoom should finish
00128                         if ( PZS_ZOOMING_TO_TABLE == this->zoomState )
00129                         {
00130                                 // reset zoom
00131                                 this->zoomState = PZS_ZOOMING_FINISHED;
00132 
00133                                 // re-enable dynamic behavior
00134                                 this->nxActor->clearActorFlag( NX_AF_DISABLE_COLLISION );
00135                                 this->nxActor->clearBodyFlag( NX_BF_KINEMATIC );
00136 
00137                                 // clear all added forces while zoom was active
00138                                 this->nxActor->setAngularVelocity( NxVec3(0.0f) );
00139                                 this->nxActor->setLinearVelocity( NxVec3(0.0f) );
00140                                 this->nxActor->wakeUp();
00141                         }
00142                 }
00143         }
00144 
00145         return ERR_OK;
00146 }
00147 
00148 
00149 // apply mesh and texture files and make physX actor
00150 ERR_TYPE Photo::load( stringc meshFileName, stringc textureFileName, vector3df scale )
00151 {
00152         ERR_TYPE status = ERR_OK;
00153         stringc err("");
00154 
00155         this->node = smgr->addAnimatedMeshSceneNode( smgr->getMesh( meshFileName.c_str() ) );
00156         if ( NULL == this->node )
00157         {
00158                 return ERR_PHOTO_LOAD_FAILED;
00159         }
00160 
00161         // this checks also if fileName indicates to video or bitmap
00162         status = this->createTextureWithBoarder( textureFileName );
00163         if ( (ERR_OK != status) || (NULL == this->texture) )
00164         {
00165                 err  = "Photo::load() cannot load ";
00166                 err += textureFileName;
00167                 err += " as bitmap. Trying to create as video.";
00168                 logger->log( err.c_str() );
00169 
00170                 // irrlicht refused to load bitmap -> try to load as video
00171                 this->videoTexture = new VideoTexture();
00172                 status = this->videoTexture->openStream( textureFileName );
00173                 if ( ERR_OK != status )
00174                 {
00175                         err  = "Photo::load() cannot load ";
00176                         err += textureFileName;
00177                         err += " as video stream.";
00178                         logger->log( err.c_str() );
00179 
00180                         delete this->videoTexture;
00181                         this->videoTexture = NULL;
00182                         this->node->remove();
00183                         this->node = NULL;
00184                         return ERR_PHOTO_LOAD_FAILED;
00185                 }
00186 
00187                 this->texture = this->videoTexture->getTexture();
00188 
00189                 // we cannot handle dynamic light on video textures
00190                 // dynamic lighting NO
00191                 this->node->setMaterialFlag( EMF_LIGHTING, false );
00192 
00193         }
00194         else
00195         {
00196                 // irrlicht loaded bitmap with success  
00197                 // dynamic lighting YES
00198                 this->node->setMaterialFlag( EMF_LIGHTING, true );
00199         }
00200 
00201         // attach (video) texture to mesh
00202         this->node->setMaterialTexture( 0, this->texture );
00203 
00204         // only allow texture to appear either on front or on back side of cube mesh
00205         // do not display texture on "side polygons"
00206         // get the meshBuffer and iterate all vertices
00207         S3DVertex* vertices = (S3DVertex*) this->node->getMesh()->getMesh(0)->getMeshBuffer(0)->getVertices();
00208         u32 vertexCount = this->node->getMesh()->getMesh(0)->getMeshBuffer(0)->getVertexCount();
00209         for ( u32 i=0; i<vertexCount; i++ )
00210         {
00211                 // check if the normal of the vertex faces not up OR down
00212                 if ( false == vertices[i].Normal.equals( vector3df(0.0f,  1.0f, 0.0f ) ) && 
00213                          false == vertices[i].Normal.equals( vector3df(0.0f, -1.0f, 0.0f ) ) )
00214                 {
00215                         // disable texture on side ploygon
00216                         vertices[i].TCoords.X = 0.0f;
00217                         vertices[i].TCoords.Y = 0.0f;
00218                 }
00219         }
00220 
00221         // set irrlicht scale
00222         this->node->setScale( scale );
00223 
00224         // we want photos to cast shadows 
00225         // note: irrlicht device must be created with option "use StencilBuffer"
00226         this->node->addShadowVolumeSceneNode();
00227 
00228         // get irrlicht boundingbox and create a NxActor with its dimension
00229         this->node->OnAnimate( 0 );     // ensure recalculation of node's pos/rot/scale
00230         aabbox3df boundingBox = this->node->getTransformedBoundingBox();
00231         vector3df sizeMax = boundingBox.MaxEdge;
00232         vector3df sizeMin = boundingBox.MinEdge;
00233         vector3df size = sizeMax + (-1.0f * sizeMin);
00234 
00235         status = this->createNxActor( size );
00236         if ( ERR_OK != status )
00237         {
00238                 this->node->remove();
00239                 this->node = NULL;
00240                 logger->log( "*** Error *** Photo::load() cannot create physX actor" );
00241                 return status;
00242         }
00243 
00244         return ERR_OK;
00245 }
00246 
00247 
00248 // this zooms photo either to camera or back to table,
00249 // depending on destinantionPos
00250 ERR_TYPE Photo::zoomStart( NxVec3 destinationPos, NxU32 timeForWayMS )
00251 {
00252         if ( (NULL == this->nxActor) || (NULL == this->node) )
00253         {
00254                 return ERR_PHOTO_CREATEACTOR_FAILED;
00255         }
00256 
00257         // setup zoom, done in update()
00258         this->zoomStartPosition = this->nxActor->getGlobalPosition();
00259         this->zoomTimeStart = timer->getRealTime();
00260         this->zoomTimeForWay = timeForWayMS;
00261         this->zoomDirection = destinationPos - this->zoomStartPosition;
00262         NxF32 zoomLength = this->zoomDirection.magnitude();
00263         this->zoomDirection.normalize();
00264         this->zoomTimeStep = zoomLength / timeForWayMS;
00265 
00266         // make photo undynmaic
00267         this->nxActor->raiseActorFlag( NX_AF_DISABLE_COLLISION );
00268         this->nxActor->raiseBodyFlag( NX_BF_KINEMATIC );
00269 
00270         this->zoomState = PZS_ZOOMING_TO_CAMERA;
00271 
00272         return ERR_OK;
00273 }
00274 
00275 
00276 // drops photo on table
00277 ERR_TYPE Photo::zoomEnd()
00278 {
00279         // just zoom back from where we started, but with double speed
00280         this->zoomStart( this->zoomStartPosition, this->zoomTimeForWay / 2 );
00281         // overwrite zoom state
00282         this->zoomState = PZS_ZOOMING_TO_TABLE;
00283 
00284         return ERR_OK;
00285 }
00286 
00287 
00288 // start to emitt particels around position of this photo
00289 ERR_TYPE Photo::particelStart( irr::core::stringc particelFileName )
00290 {
00291         // check if particel system is already running
00292         if ( NULL == this->particelNode )
00293         {
00294                 // load particel texture
00295                 ITexture* particelTexture = driver->getTexture( particelFileName.c_str() );
00296                 if ( NULL == particelTexture )
00297                 {
00298                         stringc err( "particelStart() cannot find texture: " );
00299                         err += particelFileName;
00300                         logger->log( err.c_str() );
00301                         return ERR_PHOTO_CREATEPARTICEL_FAILED;
00302                 }
00303 
00304                 // setup particel system
00305                 this->particelNode = smgr->addParticleSystemSceneNode();
00306                 this->particelNode->setParticleSize( dimension2d<f32>(100.0f, 100.0f) );
00307                 this->particelNode->setMaterialTexture( 0, particelTexture );
00308                 this->particelNode->setMaterialFlag( EMF_LIGHTING, false );
00309                 this->particelNode->setMaterialType( EMT_TRANSPARENT_ADD_COLOR );
00310 
00311                 // create particel emitter, deleted in destrucor
00312                 aabbox3d<f32> emittterBox(-8.0f, -1.0f, -8.0f, 8.0f, 1.0f, 8.0f);
00313                 vector3df emitterDirection(0.0f, 0.02f, 0.0f);
00314                 this->particelEmitter = this->particelNode->createBoxEmitter(   emittterBox,
00315                                                                                                                                                 emitterDirection,
00316                                                                                                                                                 5,                                                      // min particels per second
00317                                                                                                                                                 10,                                                     // max particels per second
00318                                                                                                                                                 SColor(255,255,255,255),        // start color
00319                                                                                                                                                 SColor(255,255,255,255),        // end color
00320                                                                                                                                                 1100,                                           // min life time
00321                                                                                                                                                 2000 );                                         // max life time
00322 
00323                 // create particel affector
00324                 this->particelAffector = this->particelNode->createFadeOutParticleAffector();
00325                 this->particelNode->addAffector( this->particelAffector );
00326         }
00327 
00328         // add some X and some (minor) Z position to final emitting position
00329         // in order to respect table holes' position
00330         vector3df particelNodeOffsetPos = this->node->getPosition();
00331         particelNodeOffsetPos.normalize();
00332         particelNodeOffsetPos.X *= 20.0f;
00333         particelNodeOffsetPos.Z *= 2.0f;
00334 
00335         this->particelNode->setPosition( this->node->getPosition() + particelNodeOffsetPos );
00336         this->particelNode->setEmitter( this->particelEmitter );
00337 
00338         return ERR_OK;
00339 }
00340 
00341 
00342 // stop emitting particels
00343 ERR_TYPE Photo::particelEnd()
00344 {
00345         if ( NULL != this->particelNode )
00346         {
00347                 this->particelNode->setEmitter( NULL );
00348         }
00349 
00350         return ERR_OK;
00351 }
00352 
00353 
00354 ERR_TYPE Photo::applyForce( NxVec3 forceVector )
00355 {
00356         if ( NULL != this->nxActor )
00357         {
00358                 this->nxActor->addForce( forceVector );
00359         }
00360         return ERR_OK;
00361 }
00362 
00363 ERR_TYPE Photo::setPosition( NxVec3 newPosition )
00364 {
00365         if ( NULL != this->nxActor )
00366         {
00367                 this->nxActor->setGlobalPosition( newPosition );
00368         }
00369         return ERR_OK;
00370 }
00371 
00372 ERR_TYPE Photo::getPosition( NxVec3* outPosition )
00373 {
00374         if ( NULL != this->nxActor )
00375         {
00376                 *outPosition = this->nxActor->getGlobalPosition();
00377         }
00378         return ERR_OK;
00379 }
00380 
00381 
00382 
00383 
00384 // *** private: ***
00385 
00386 
00387 
00388 ERR_TYPE Photo::createNxActor( vector3df size )
00389 {
00390         // box dimension in [m] on a side
00391         NxReal irrToPhysxScaler = 0.5f;         // one unit in irrlicht is 1/2 unit in physx. always.
00392         NxVec3 boxDim( size.X * irrToPhysxScaler, size.Y * irrToPhysxScaler, size.Z * irrToPhysxScaler );
00393 
00394     // Add a single-shape actor to the scene
00395     NxActorDesc actorDesc;
00396     NxBodyDesc bodyDesc;
00397 
00398     // The actor has one shape, a box
00399     NxBoxShapeDesc boxDesc;
00400         boxDesc.dimensions.set( boxDim );
00401 
00402         actorDesc.shapes.pushBack( &boxDesc );
00403 
00404     actorDesc.body = &bodyDesc;
00405     actorDesc.density = 20.0f;
00406 
00407         actorDesc.userData = (void*) this;
00408 
00409     this->nxActor = nxScene->createActor( actorDesc );
00410         if ( NULL == this->nxActor )
00411         {
00412                 return ERR_TABLE_CREATEACTOR_FAILED;
00413         }
00414 
00415         this->nxActor->setName( "Photo" );
00416 
00417         return ERR_OK;
00418 }
00419 
00420 
00421 // 1. loads image from file and copies it into center of hand made, white texture
00422 // 2. works only if video card is capable of creating ECF_A8R8G8B8 textures
00423 // 3. keeps original size of loaded image, if video card is capable of creating
00424 //    "non power(2)" textures. else scaling loaded image up to power(2) size (looks messy)
00425 ERR_TYPE Photo::createTextureWithBoarder( stringc imageFileName )
00426 {
00427         IImage* image = driver->createImageFromFile( imageFileName.c_str() );
00428         if ( NULL == image )
00429         {
00430                 return ERR_PHOTO_LOAD_FAILED;
00431         }
00432 
00433         // get size of image
00434         dimension2d<s32> imageSize( image->getDimension() );
00435 
00436         // setup final texture size
00437         dimension2d<s32> textureSize( imageSize );
00438         // white border size depends on size of loaded image
00439         s32 borderX = imageSize.Width / 8;
00440         s32 borderY = imageSize.Height / 8;
00441         // add border to final texture size
00442         textureSize.Width  += borderX;
00443         textureSize.Height += borderY;
00444         
00445         // older graphic cards cannot handle odd texture sizes
00446         if ( false == driver->queryFeature(EVDF_TEXTURE_NPOT) )
00447         {
00448                 // make width power 2 based
00449                 s32 optimalTextureWidth = 0x01;
00450                 while ( optimalTextureWidth < textureSize.Width )
00451                 {
00452                         optimalTextureWidth <<= 1;
00453                 }
00454                 textureSize.Width = optimalTextureWidth;
00455 
00456                 // make height power 2 based
00457                 s32 optimalTextureHeight = 0x01;
00458                 while ( optimalTextureHeight < textureSize.Height )
00459                 {
00460                         optimalTextureHeight <<= 1;
00461                 }
00462                 textureSize.Height = optimalTextureHeight;
00463 
00464                 // resize image in order to fit into texture nicely with a white border
00465                 dimension2d<s32> scaledImageSize( textureSize.Width - borderX, textureSize.Height - borderY );
00466                 u8* scaledData = (u8*) malloc( scaledImageSize.Width * scaledImageSize.Height * 4 );
00467                 if ( NULL != scaledData )
00468                 {
00469                         IImage* scaledImage = driver->createImageFromData( ECF_A8R8G8B8, scaledImageSize, scaledData );
00470                         image->copyToScaling( scaledImage );
00471                         free( scaledData );
00472                         // dispose loaded image
00473                         image->drop();
00474                         // assign scaled image for copy routine below
00475                         image = scaledImage;
00476                         imageSize = scaledImageSize;
00477                 }
00478                 else
00479                 {
00480                         // out of memory, just copy loaded image into center of (too big) white texture
00481                         borderX = textureSize.Width - imageSize.Width;
00482                         borderY = textureSize.Height - imageSize.Height;
00483                 }
00484         }       // end of old graphic cards
00485 
00486         // hand made texture, fileName just for irrlicht internal "getTexture() by name"
00487         this->texture = driver->addTexture( textureSize, imageFileName.c_str(), ECF_A8R8G8B8 );
00488         if ( NULL == this->texture )
00489         {
00490                 image->drop();
00491                 return ERR_PHOTO_LOAD_FAILED;
00492         }
00493 
00494         // check if irrlicht could request right color format at device
00495         ECOLOR_FORMAT colorFormat = this->texture->getColorFormat();
00496         if ( colorFormat != ECF_A8R8G8B8 )
00497         {
00498                 logger->log( "createTextureWithBoarder() Warning, texture color format mismatch" );
00499                 image->drop();
00500                 return ERR_PHOTO_LOAD_FAILED;
00501         }
00502         
00503         // need read access from imageData
00504         u8* imageData = (u8*) image->lock();
00505         if ( NULL == imageData )
00506         {
00507                 logger->log( "createTextureWithBoarder() cannot access image data" );
00508                 image->drop();
00509                 return ERR_PHOTO_LOAD_FAILED;
00510         }
00511 
00512         // need write access to textureData
00513         u8* textureData = (u8*) this->texture->lock();
00514         if ( NULL == textureData )
00515         {
00516                 logger->log( "createTextureWithBoarder() cannot access texture data" );
00517                 image->drop();
00518                 return ERR_PHOTO_LOAD_FAILED;
00519         }
00520 
00521         // make final texture all white
00522         memset( textureData, 0xFF, textureSize.Height * textureSize.Width * 4 );
00523 
00524         // clip loaded image into handmade texture and offset some pixels (leave border white)
00525         // taken from setPixel() @ CImage.cpp in irrlicht source code
00526         u32 pitch = this->texture->getPitch();
00527         for (s32 y=0; y<imageSize.Height; y++)
00528         {
00529                 for (s32 x=0; x<imageSize.Width; x++)
00530                 {
00531                         s32 color = image->getPixel( x, y ).color;
00532                         u32* dest = (u32*) ((u8*) textureData + ( (y+borderY/2) * pitch ) + ( (x+borderX/2) << 2 ));
00533                         *dest = color;
00534                 }
00535         }
00536 
00537         this->texture->unlock();
00538         image->unlock();
00539 
00540         // we create it, we destroy it
00541         image->drop();
00542 
00543         return ERR_OK;
00544 }

Generated on Sun Dec 2 03:10:23 2007 for TableTop by  doxygen 1.5.4