MercuryLoadableModel.cpp

Go to the documentation of this file.
00001 #include "MercuryShapes.h"
00002 #include "MercuryLoadableModel.h"
00003 #include "MercuryPoly.h"
00004 #include "MercuryLog.h"
00005 
00006 
00007 void MercuryLoadableModel::Update( const float dTime )
00008 {
00009     static MQuaternion NoRot( 1,0,0,0 );
00010     unsigned i;
00011 
00012     //First, run through and update where all the animations are on their cycle.
00013     for ( i = 0; i < m_vAnimationInfos.size(); ++i )
00014         m_vAnimationInfos[i].fPlace += dTime * m_vAnimationInfos[i].fRate;
00015 
00016     for ( i = 0; i < m_vBones.size(); ++i )
00017     {
00018         if ( m_vAnimationInfos.empty() )
00019             ResetBoneMatrix( i );
00020         else
00021         {
00022             //Generate all info for the bone.
00023             MercuryPoint Pos;
00024             MQuaternion Rot;
00025 
00026             Pos = m_vBones[i].Pos;
00027             Rot = m_vBones[i].Rot;
00028 
00029             float fTotalPercent = 0;
00030             
00031             for ( unsigned j = 0; j < m_vAnimationInfos.size(); ++j )
00032             {
00033                 float fPercent = m_vAnimationInfos[j].fPercent;
00034                 fTotalPercent += fPercent;
00035                 float fPlace = m_vAnimationInfos[j].fPlace;
00036                 float fStart, fEnd;
00037                 bool bHadToForceExit = false;
00038 
00039                 MLBoneAnimation * pMLP = m_vBones[i].m_mAnimations.get( m_vAnimationInfos[j].sAnimationName );
00040 
00041                 if( !pMLP )
00042                     continue;
00043 
00044                 bool bDoTween = false;
00045                 float fTweenLocation;
00046                 MVector<MLAnimationKey> * pAnim = &pMLP->vKeys;
00047 
00048                 int iStartPlace = pMLP->iCurrentPlace;
00049                 int iEndPlace;
00050 
00051 
00052                 //Tricky: When we're over the current animation's end, 
00053                 //  we have to start over at the beginning.
00054                 //  if we've gone multiple animations without a cycle, don't screw up!
00055                 // NOTE: This has to be run in its own if statement because the next one
00056                 //  may or may not be run depending on what goes on here.
00057                 float fDuration = pMLP->fAnimationLength;
00058                 if ( pMLP->vKeys.size() != 0 && m_vAnimationInfos[j].bRunning )
00059                 {
00060                     while( fDuration < fPlace )
00061                     {
00062                         fPlace = fPlace - fDuration;
00063                         if( !m_vAnimationInfos[j].bLoop )
00064                             m_vAnimationInfos[j].bRunning = false;
00065                     }
00066                 }
00067 
00068                 
00069                 if ( pMLP->vKeys.size() != 0 && m_vAnimationInfos[j].bRunning )
00070                 {
00071 
00072                     //Next, see where to start and end the tweening.
00073                     int iKeyMax = pMLP->vKeys.size();
00074 
00075                     //We keep the original start, just so we know if we'll get stuck in the loop that's comming up
00076                     int iOriginalStart = iStartPlace;
00077 
00078                     //iEndPlace is the logical next place after iStartPlace.
00079                     iEndPlace = ( iStartPlace + 1 )%iKeyMax;
00080 
00081                     while (!( (*pAnim)[iStartPlace].fSeconds < fPlace  && (*pAnim)[iEndPlace].fSeconds >= fPlace ))
00082                     {
00083                         iStartPlace = ( iStartPlace + 1 )%iKeyMax;
00084                         iEndPlace = ( iStartPlace + 1 )%iKeyMax;
00085 
00086                         //Something's pretty wrong if this happens...
00087                         if ( iStartPlace == iOriginalStart )
00088                         {
00089                             bHadToForceExit = true;
00090                             break;
00091                         }
00092                     }
00093 
00094                     if ( !bHadToForceExit )
00095                     {
00096                         //End place could be the beginning, after the start.
00097                         iEndPlace = ( iStartPlace + 1 )%iKeyMax;
00098                         pMLP->iCurrentPlace = iStartPlace;
00099 
00100                         //Now we've located the start and end places.
00101                         fStart = (*pAnim)[iStartPlace].fSeconds;
00102                         fEnd = (*pAnim)[iEndPlace].fSeconds;
00103 
00104                         if ( fEnd < fStart )
00105                             fEnd+=fDuration;
00106 
00107                         fTweenLocation = ( fPlace-fStart ) / ( fEnd-fStart );
00108                     } else
00109                         fTweenLocation = 0;
00110 
00111                     m_vAnimationInfos[j].fPlace = fPlace;
00112                     bDoTween = true;
00113                 }
00114 
00115                 if ( bDoTween )
00116                 {
00117                     if ( !m_vAnimationInfos[j].bLoop )
00118                     {
00119                         //Hmm... we need to figure out a way to smoothly tween back to the nothing
00120                         //state by using fPercent.  How to do it, I'm not sure yet.
00121                         if ( iStartPlace == 0 )
00122                             fPercent = fTweenLocation;
00123                         if ( iEndPlace == (int)pMLP->vKeys.size() - 1 )
00124                             fPercent = ( 1-fTweenLocation );
00125                     }
00126 
00127                     Pos += (*pAnim)[iStartPlace].Pos * fPercent * (1-fTweenLocation);
00128                     Pos += (*pAnim)[iEndPlace].Pos * fPercent * fTweenLocation;
00129                     Rot = Rot * SLERP( 
00130                         NoRot,
00131                         SLERP( (*pAnim)[iStartPlace].Rot, (*pAnim)[iEndPlace].Rot, fTweenLocation ),
00132                         fPercent );
00133                 }
00134             }
00135             
00136             MakeBoneMatrix( i, Pos, Rot );
00137         }
00138 
00139         MercuryPoint out;
00140         VectorMultiply( m_vBones[i].mXF, gpZero, out );
00141 
00142         for ( unsigned int iVert = 0; iVert < m_vBones[i].m_vDissassembledVerts.size(); iVert++ )
00143         {
00144             //Ok, this is kind of a nasty line. 
00145             //We take the completed bone matrix, and input the disassembled position
00146             //The output goes into the packet vert's x,y, and z directly.
00147             if (  m_vBones[i].mXF, m_vBones[i].m_vDissassembledVerts[iVert].fPercent > 0.5 )
00148                 VectorMultiply( m_vBones[i].mXF, m_vBones[i].m_vDissassembledVerts[iVert].pos, 
00149                     m_meshes[ m_vBones[i].m_vDissassembledVerts[iVert].iMappedMesh ]->
00150                     GetVertex(m_vBones[i].m_vDissassembledVerts[iVert].iMappedVert)->GetPointHandle() );
00151         }
00152 
00153 //      m_vVisualJoints[i]->SetPosition( out );
00154 
00155         if ( m_vBones[i].iExclusiveMesh != -1 )
00156         {
00157             m_meshes[m_vBones[i].iExclusiveMesh]->SetRotationMode( RM_MATRIX );
00158             m_meshes[m_vBones[i].iExclusiveMesh]->SetPosition( MercuryPoint( 0,0,0 ) );
00159             m_meshes[m_vBones[i].iExclusiveMesh]->SetAfterMatrix( m_vBones[i].mXF.Ptr() );
00160         }
00161     }
00162 
00163     for ( i = 0; i < m_meshes.size(); ++i )
00164         if ( m_meshes[i]->IsAnimated() )
00165             m_meshes[i]->CalculateVertexNormals();
00166 
00167     MercuryModel::Update( dTime );
00168 }
00169 
00170 void MercuryLoadableModel::LoadModel()
00171 {
00172     int sType;
00173     unsigned int i;
00174     unsigned int iVersion;
00175     unsigned int iNumMeshes;
00176     unsigned int iNumBones;
00177     unsigned int iNumAnimations;
00178     unsigned int iNumMaterials;
00179 
00180     m_file->Read( &sType, 4 );
00181     TO_ENDIAN(sType);
00182     if (sType != 0x464d424d)
00183     {
00184         LOG.Warn(ssprintf( "%s is not a valid HGMDL file. It has headder %08X", m_file->GetName().c_str(),sType));
00185         return;
00186     }
00187 
00188     m_file->Read( &iVersion, 4 );
00189     TO_ENDIAN(iVersion);
00190 
00191     m_file->Read( &iNumMeshes, 4 );
00192     TO_ENDIAN(iNumMeshes);
00193 
00194     m_meshes.resize( 0 );
00195     m_vMeshMaterialAssignments.resize( iNumMeshes );
00196     for ( i = 0; i < iNumMeshes; ++i )
00197     {
00198         LoadMesh( i );
00199         m_meshes[i]->CalculateVertexNormals();
00200         m_meshes[i]->BuildVBO();
00201     }
00202 
00203     m_file->Read( &iNumBones, 4 );
00204     TO_ENDIAN(iNumBones);
00205 //  m_vVisualJoints.resize( iNumBones );
00206     m_vBones.resize( iNumBones );
00207     for ( i = 0; i < iNumBones; ++i )
00208         LoadBone( i );
00209 
00210     m_file->Read( &iNumAnimations, 4 );
00211     TO_ENDIAN(iNumAnimations);
00212     for ( i = 0; i < iNumAnimations; ++i )
00213         LoadAnimation( i );
00214 
00215     m_file->Read( &iNumMaterials, 4 );
00216     TO_ENDIAN(iNumMaterials);
00217     m_materials.resize( iNumMaterials );
00218     for ( i = 0; i < iNumMaterials; ++i )
00219         LoadMaterial( i );
00220 
00221     //Set up materials
00222     for( i = 0; i < iNumMeshes; ++i )
00223         if( m_vMeshMaterialAssignments[i] >= 0 )
00224             m_meshes[i]->SetMaterial( m_materials[m_vMeshMaterialAssignments[i]] );
00225 
00226     m_vMeshMaterialAssignments.resize(0);
00227     SAFE_DELETE( m_file );
00228 }
00229 
00230 bool MercuryLoadableModel::LoadMesh( int iMesh )
00231 {
00232     unsigned int    iNameLen;
00233     char *          sName;
00234     unsigned int    iNumVerts;
00235     unsigned int    iNumStrips;
00236     unsigned int    iNumFans;
00237     unsigned int    iNumTriangles;
00238     bool            bCache;
00239     unsigned int    i;
00240 
00241     m_file->Read( &iNameLen, 4 );
00242     TO_ENDIAN(iNameLen);
00243     sName = new char[iNameLen+1];
00244     m_file->Read( sName, iNameLen );
00245     sName[iNameLen] = 0; //Null terminate
00246 
00247     MString sFullName = GetName() + sName + ssprintf("%d", iMesh);
00248     SAFE_DELETE(sName);
00249 
00250     m_file->Read( &iNumVerts, 4 );
00251     TO_ENDIAN(iNumVerts);
00252     m_file->Read( &iNumStrips, 4 );
00253     TO_ENDIAN(iNumStrips);
00254     m_file->Read( &iNumFans, 4 );
00255     TO_ENDIAN(iNumFans);
00256     m_file->Read( &iNumTriangles, 4 );
00257     TO_ENDIAN(iNumTriangles);
00258     m_file->Read( &m_vMeshMaterialAssignments[iMesh], 4 );
00259     TO_ENDIAN(m_vMeshMaterialAssignments[iMesh]);
00260     m_file->Read( &bCache, 1 );
00261 
00262     MercuryMesh * m = new MercuryMesh;
00263     if( MESHMAN.RegMesh( m, sFullName, bCache ) )
00264     {
00265 //      AddMesh( m );
00266 //      return true;
00267     }
00268 
00269     m->SetIsAnimated( !( bCache || m_vMeshMaterialAssignments[iMesh] == 0 ));
00270     m->ToggleUseVBOs( bCache );
00271 
00272     m->SetNumVertices(iNumVerts);
00273 
00274     //Tricky! Looks like we're doing something really nasty here, however we're doing
00275     //something equivelant to &something[0], to acquire the address of all of the data
00276     m_file->Read( m->GetVerticePtr(), iNumVerts * 32 );
00277 
00278     for( i = 0; i < m->NumVertices(); ++i )
00279     {
00280         TO_ENDIAN( *( (unsigned*) & (*m)[i].x ) );
00281         TO_ENDIAN( *( (unsigned*) & (*m)[i].y ) );
00282         TO_ENDIAN( *( (unsigned*) & (*m)[i].z ) );
00283         TO_ENDIAN( *( (unsigned*) & (*m)[i].m_uv[0] ) );
00284         TO_ENDIAN( *( (unsigned*) & (*m)[i].m_uv[1] ) );
00285 
00286         if( m_fVisRadius < (*m)[i].GetPointHandle().Magnitude() )
00287             m_fVisRadius = (*m)[i].GetPointHandle().Magnitude();
00288     }
00289 
00290     for ( i = 0; i < iNumStrips; ++i)
00291     {
00292         unsigned int length;
00293         m_file->Read( &length, 4 );
00294         TO_ENDIAN( length );
00295         m->SetDrawType(MGL_STRIP);
00296         m->SetNumIndices( length );
00297         m_file->Read( m->GetIndicesPtr(), length*4 );
00298         for( unsigned j = 0; j < length; j++ )
00299             TO_ENDIAN( m->GetIndicesPtr()[j] );
00300     }
00301 
00302     for ( i = 0; i < iNumFans; ++i)
00303     {
00304         unsigned int length;
00305         m_file->Read( &length, 4 );
00306         TO_ENDIAN( length );
00307         m->SetDrawType(MGL_FAN);
00308         m->SetNumIndices( length );
00309         m_file->Read( m->GetIndicesPtr(), length*4 );
00310         for( unsigned j = 0; j < length; j++ )
00311             TO_ENDIAN( m->GetIndicesPtr()[j] );
00312     }
00313 
00314     if ( iNumTriangles > 0)
00315     {
00316         m->SetDrawType(MGL_TRIANGLE);
00317         m->SetNumIndices( iNumTriangles*3 );
00318         m_file->Read( m->GetIndicesPtr(), m->NumIndices()*4 );
00319         for( unsigned j = 0; j < m->NumIndices(); j++ )
00320             TO_ENDIAN( m->GetIndicesPtr()[j] );
00321     }
00322 
00323     if ( iNumStrips != 0 || iNumFans != 0 )
00324         LOG.Warn( "Cannot load model.  Model has fans and strips.  They are not yet supported." );
00325 
00326 
00327     AddMesh( m );
00328     return true;
00329 }
00330 
00331 void MercuryLoadableModel::LoadBone( int iBone )
00332 {
00333 //  bool bHide = false;
00334     unsigned int iTmp;
00335     unsigned i;
00336     m_file->Read( &iTmp, 4 );
00337     TO_ENDIAN( iTmp );
00338     char * sName = new char[iTmp+1];
00339     m_file->Read( sName, iTmp );
00340     sName[iTmp] = 0;
00341 
00342     m_file->Read( &m_vBones[iBone].iParent, 4 );
00343     TO_ENDIAN( m_vBones[iBone].iParent );
00344     m_file->Read( &m_vBones[iBone].iExclusiveMesh, 4 );
00345     TO_ENDIAN( m_vBones[iBone].iExclusiveMesh );
00346     m_file->Read( &m_vBones[iBone].Pos, 4 * 3 );
00347     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Pos.x ) );
00348     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Pos.y ) );
00349     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Pos.z ) );
00350     m_file->Read( &m_vBones[iBone].Rot, 4 * 4 );
00351     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Rot.w ) );
00352     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Rot.x ) );
00353     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Rot.y ) );
00354     TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].Rot.z ) );
00355     m_file->Read( &iTmp, 4 );
00356     TO_ENDIAN( iTmp );
00357 
00358     ResetBoneMatrix( iBone );
00359 
00360     //We now have the bone's xform matrix
00361 
00362     m_vBones[iBone].m_vDissassembledVerts.resize( iTmp );
00363 
00364     for ( i = 0; i < iTmp; i++ )
00365     {
00366         MLQVert * tVert = &m_vBones[iBone].m_vDissassembledVerts[i];
00367         m_file->Read( &tVert->iMappedMesh, 4 );
00368         TO_ENDIAN( tVert->iMappedMesh );
00369         m_file->Read( &tVert->iMappedVert, 4 );
00370         TO_ENDIAN( tVert->iMappedVert );
00371         m_file->Read( &tVert->fPercent, 4 );
00372         TO_ENDIAN( *( (unsigned*) & (tVert->fPercent) ) );
00373 
00374         MercuryPoint G = m_meshes[tVert->iMappedMesh]->GetVertex(tVert->iMappedVert)->GetPointHandle();
00375         MercuryMatrix P;
00376         InvertMatrix( m_vBones[iBone].mXF, P );
00377 
00378         VectorMultiply( P, G, tVert->pos );
00379     }
00380 
00381     SAFE_DELETE(sName);
00382 
00383 /*  m_vVisualJoints[iBone] = new MercuryShape;
00384     m_vVisualJoints[iBone]->SetName( ssprintf( "BONE:%s:%d", GetName().c_str(), iBone ) );
00385     m_vVisualJoints[iBone]->MakeSphere();
00386     m_vVisualJoints[iBone]->SetScale( MercuryPoint( 20,20,20 ) );
00387     m_vVisualJoints[iBone]->SetHide( true );
00388     AddObject( m_vVisualJoints[iBone], true );*/
00389 }
00390 
00391 void MercuryLoadableModel::LoadAnimation( int iAnimation )
00392 {
00393     unsigned int    iTmp = 0;
00394     unsigned int    iNumBones = 0;
00395     char *          sAnimationName;
00396     float           fDuration = 0.0f;
00397     m_file->Read( &iTmp, 4 );
00398     TO_ENDIAN( iTmp );
00399     sAnimationName = new char [iTmp + 1];
00400     m_file->Read( sAnimationName, iTmp );
00401     sAnimationName[iTmp] = '\0';
00402 
00403     m_file->Read( &fDuration,4);
00404     TO_ENDIAN( *( (unsigned*) & (fDuration) ) );
00405     m_file->Read( &iNumBones, 4 );
00406     TO_ENDIAN( iNumBones );
00407     
00408     for ( unsigned int i = 0; i < iNumBones; ++i )
00409     {
00410         int iBone;
00411         int iNumKeys;
00412         m_file->Read( &iBone, 4 );
00413         TO_ENDIAN( iBone );
00414         m_file->Read( &iNumKeys, 4 );
00415         TO_ENDIAN( iNumKeys );
00416 
00417         m_vBones[iBone].m_mAnimations[sAnimationName].fAnimationLength = fDuration;
00418         m_vBones[iBone].m_mAnimations[sAnimationName].vKeys.resize( iNumKeys );
00419         m_vBones[iBone].m_mAnimations[sAnimationName].iCurrentPlace = 0;
00420 
00421         for ( int j = 0; j < iNumKeys; ++j )
00422         {
00423             m_file->Read( &m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].fSeconds, 4 );
00424             m_file->Read( &m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Pos, 4 * 3 );
00425             m_file->Read( &m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Rot, 4 * 4 );
00426             TO_ENDIAN( *( (unsigned*) & (m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].fSeconds) ) );
00427             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Pos.x ) );
00428             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Pos.y ) );
00429             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Pos.z ) );
00430             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Rot.w ) );
00431             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Rot.x ) );
00432             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Rot.y ) );
00433             TO_ENDIAN( *( (unsigned*) & m_vBones[iBone].m_mAnimations[sAnimationName].vKeys[j].Rot.z ) );
00434         }
00435     }
00436 
00437     SAFE_DELETE( sAnimationName );
00438 }
00439 
00440 void MercuryLoadableModel::LoadMaterial( int iMaterial )
00441 {
00442     unsigned int    iNameLen;
00443     char *          sName;
00444     m_file->Read( &iNameLen, 4 );
00445     TO_ENDIAN( iNameLen );
00446     sName = new char[iNameLen+1];
00447     m_file->Read( sName, iNameLen );
00448     sName[iNameLen] = 0;
00449     MercuryMaterial * m = new MercuryMaterial;
00450     m->LoadMaterial( MString( sName, iNameLen ), m_path );
00451     SAFE_DELETE(sName);
00452     m_materials[iMaterial] = m;
00453 }
00454 
00455 
00456 
00457 //Now, for the embedded functions
00458 
00459 void MercuryLoadableModel::ResetBoneMatrix( int iBone )
00460 {
00461     MercuryMatrix m,n,o;
00462     MLBone * thisbone = &m_vBones[iBone];
00463 
00464     (thisbone->Rot).toMatrix4( o );
00465     o[0][3] = thisbone->Pos.x;
00466     o[1][3] = thisbone->Pos.y;
00467     o[2][3] = thisbone->Pos.z;
00468 
00469     if ( thisbone->iParent >= 0 )
00470     {
00471         m = m_vBones[m_vBones[iBone].iParent].mXF * o;
00472         Copy16f( &thisbone->mXF, &m);
00473     }
00474     else
00475         Copy16f(&thisbone->mXF, &o);
00476 }
00477 
00478 void MercuryLoadableModel::MakeBoneMatrix( int iBone, const MercuryPoint & Pos, const MQuaternion & Rot )
00479 {
00480     MercuryMatrix m,n,o;
00481     MLBone * thisbone = &m_vBones[iBone];
00482 
00483     Rot.toMatrix4( o );
00484     o[0][3] = Pos.x;
00485     o[1][3] = Pos.y;
00486     o[2][3] = Pos.z;
00487 
00488     if ( thisbone->iParent >= 0 )
00489     {
00490         m = m_vBones[m_vBones[iBone].iParent].mXF * o;
00491         Copy16f( &thisbone->mXF, &m );
00492     }
00493     else
00494         Copy16f( &thisbone->mXF, &o );
00495 }
00496 
00497 #include "MercuryObjectFactory.h"
00498 REGISTER_OBJECT_TYPE( MercuryLoadableModel );
00499 
00500 
00501 /* 
00502  * Copyright (c) 2005-2006, Charles Lohr
00503  * All rights reserved.
00504  *
00505  * Redistribution and use in source and binary forms, with or
00506  * without modification, are permitted provided that the following
00507  * conditions are met:
00508  *  -   Redistributions of source code must retain the above
00509  *      copyright notice, this list of conditions and the following disclaimer.
00510  *  -   Redistributions in binary form must reproduce the above copyright
00511  *      notice, this list of conditions and the following disclaimer in
00512  *      the documentation and/or other materials provided with the distribution.
00513  *  -   Neither the name of the Mercury Engine nor the names of its
00514  *      contributors may be used to endorse or promote products derived from
00515  *      this software without specific prior written permission.
00516  *
00517  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00518  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00519  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00520  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
00521  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00522  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00523  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00524  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00525  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00526  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00527  */

Hosted by SourceForge.net Logo