#include "stdafx.h"

#include <vector>
#include <algorithm>
#include <functional> 
#include <iostream>

#include <math.h>

#include "Keys.h"

#include "Timer.h"
#include "TArc.h"
#include "Vec.h"
#include "Matrix.h"

#include "EngineIO.h"
#include "Camera.h"
#include "Tile.h"
#include "Zone.h"
#include "Map.h"

#include "MaterialManager.h"
#include "LightManager.h"
#include "Frustum.h"

#include "AnimationManager.h"

#include "Surface.h"
#include "SurfaceCache.h"

#include "Engine.h"

// MFC - Assists in finding memory leaks:
#ifdef MFC_DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#pragma warning( disable : 4996 )

bool sortZOrder      (Tile* t1, Tile* t2);
bool solveZConflicts (void);
bool doSolveZ        (Tile* t1, Tile* t2);
bool checkSides      (Tile* t1, Tile* t2);

Engine* Engine::_instance = 0;

/****************************************************************************/
/** \brief Liefert die Singleton-Instanz
    \return Pointer auf die Instanz
*****************************************************************************/
Engine* Engine::getInstance()
{
 if (_instance == 0) 
   _instance = new Engine();
 return _instance;
}

/****************************************************************************/
/** \brief Konstruktor
*****************************************************************************/
Engine::Engine()
  {
  m_drawInfo = false;
  m_partikelCount = 0;
  m_usePortalFrustum = true;
  m_isRendererInitialized = false;

  m_map              = Map::getInstance();
  m_camera           = Camera::getInstance(); 
  m_engineIO         = EngineIO::getInstance();
  m_lightManager     = LightManager::getInstance();
  m_skyBox           = SkyBox::getInstance(); 
  m_animationManager = AnimationManager::getInstance();
  m_partikelManager  = PartikelManager::getInstance();
  m_surfaceCache     = SurfaceCache::getInstance();
  m_renderer         = Renderer::getInstance();
  
  m_height = 0;
  m_width  = 0;
  
  m_text = new Text();
  m_textList.push_back(m_text);
  }

/****************************************************************************/
/** \brief Destruktor
*****************************************************************************/
Engine::~Engine()
{
delete (m_map);
delete (m_camera);
delete (m_engineIO);
delete (m_lightManager);

delete (m_frustum);

TArc* arc = TArc::getInstance();
delete (arc);

delete (m_skyBox);
delete (m_animationManager);
delete (m_partikelManager);
delete (m_surfaceCache);
delete (m_renderer);

m_textList.clear();
delete (m_text);

_instance = 0; // Singleton !!!
}

/******************************************************************************/
/** \brief Initialisiert den Renderer
    \param width Breite der Canvas (Bildschirmfenster)
    \param height Hoehe der Canvas (Bildschirmfenster)
    \param data Referenz auf den Canvas-Datenbereich 
*******************************************************************************/
void Engine::initRenderer(int width, int height, BYTE* data)
{
m_frustum = new Frustum(width, height);
m_renderer->initRenderer(width, height, data);
m_isRendererInitialized = true;

m_height = height;
m_width  = width;
}

/******************************************************************************/
/** \brief Rendert genau ein Frame  
*******************************************************************************/
void Engine::renderFrame()
  {
  if (m_isRendererInitialized == false)
     return;
  
  m_camera->action(); 
  
  m_rendering = true; // Keine Eingaben erlaubt
  m_timer.start();

  m_camdir.set(m_camera->getDir());
  m_campos.set(m_camera->getPos());

  int w = m_camdir.winkel();

  m_matTranslate.clear();
  m_matRotate.clear();

  m_campos.m_x = (-1) * m_campos.m_x;
  m_campos.m_y = (-1) * m_campos.m_y;
  m_campos.m_z = (-1) * m_campos.m_z;

  m_matTranslate.translate(m_campos);
  m_matRotate.rotateY(360 - w);
  
  //clearPlane(); // WEGOPTIMIERT
  m_skyBox->reset();

  m_tileList.clear();
  m_partikelList.clear(); 

  culling(m_camera->getZone(), *m_frustum);
 
  // Sortiere die Tiles Back2Front
  // sortBack2Front();
  
  animate();

  m_renderer->clearZBuffer();

  if (m_skyBox->test() == true)
     paintSkyBox();  // SkyBox Zuerst die Skybox rendern

  m_renderer->clearZBuffer();

  paintZone(); // 3D-Geometrien rendern (Walls, Floors, Portals..)
  paint2D(m_camera->getZone(), *m_frustum);   // 2D-Objekte rendern (Sprites, Partikel...)

if (m_drawInfo == true)
   {
   char msg[512];
   m_frameRate =  1.0 / m_timer.end();

   sprintf(msg, "Frames=%.1lf, Tiles=%d, Surfaces=%d", m_frameRate, m_tileList.size(), m_surfaceCache->getCount()); 
   m_text->set(10, 10, msg);
   m_renderer->renderText(m_textList);

   //m_charset->drawText(m_PlanePixels, msg, 10, 10);
   }

  m_rendering = false; // Eingabe wieder erlaubt
  }

/******************************************************************************/
/** \brief Animiert alle scrollbaren Texturen
*******************************************************************************/
void Engine::animate (void)
{
MaterialManager* materialManager = MaterialManager::getInstance();

for (unsigned int i=0; i < m_tileList.size(); i++)
   {
   Tile* tile = (Tile*)m_tileList[i];
   if (tile->getScrollFlag() != 0)
      {
      Texture* texture = (Texture*)materialManager->get(tile->m_textureID);
      texture->scroll(tile->getScrollFlag());
      }
   }

m_animationManager->animate();
}

/******************************************************************************/
/** \brief Culling 
    \param zoneNr Es werden nur Tiles berucksichtigt die zu dieser Zone gehoeren
    \param frust  Das Frustum. Es kann das normale Frustum sein, oder ein verkleinertes,
                  welches durch ein Portal guckt. 
*******************************************************************************/
void Engine::culling (int zoneNr, Frustum& frust)
{
unsigned int i;
m_skyBox->test(zoneNr);

Tile* tile; 

Zone* zone = 0;

// Suche die Zone aus der Map
for (i=0; i < m_map->zoneList.size(); i++)
   {
   if (m_map->zoneList[i]->m_zone == zoneNr)
      {
      zone = m_map->zoneList[i];
      break;
      }
   }

// Falls es keine Zone zu dieser ZoneNr gibt -->zurueck
if (zone == 0)
  {
  // TODO: Fehlerbehandlung einpflegen
  return;
  }

// Gehe ueber alle Tiles dieser Zone
for (i=0; i < zone->tileList.size(); i++)
   {
   tile = (Tile*) zone->tileList[i];
   
   tile->resetVertices();

   if (tile->m_animationID != -1)
       {
       Animation* anim = m_animationManager->get(tile->m_animationID);
       m_matAnimate.clear();
       m_matAnimate.translate(anim->m_dirVec);
       tile->transform(m_matAnimate);
       }

   tile->transform(m_matTranslate);
   tile->transform(m_matRotate);

   // Backface Culling: Alle Tiles, die nicht in Richtung Camera, aka 
   // Frustumspitze schauen, werden verworfen
   if (tile->check(m_frustum->Origin) == Plane::OUTSIDE)
      continue; 
   
   // Frustum Culling: Teste, ob die Tile jetzt im Frustum liegt:
   if (frust.inside(*tile) == false)
      continue;  // Tile liegt nicht im Frustum. 
   
   // Test auf ein Portal:
   if ((zoneNr != -1) && (tile->m_typ == Tile::PORTAL))
      {
      frust.clip(*tile); // Portal gegen das 'Parent'-Frustum clippen
      if (m_usePortalFrustum == true)
         {
         Frustum* portalFrustum = new Frustum(m_width, m_height);
         portalFrustum->set(*tile);
         culling(tile->m_zoneOTHER, *portalFrustum);
         delete (portalFrustum);
         }
      else
         {
         culling(tile->m_zoneOTHER, frust);
         }
      }
   
   m_tileList.push_back(tile);   
   }
}

/******************************************************************************/
/** \brief Render alle Tiles aus der Zone
*******************************************************************************/
void Engine::paintZone()
{
Tile* tile = 0; 

// Gehe ueber alle Tiles, die gerendert werden muessen 
for (unsigned int i=0; i < m_tileList.size(); i++)
   {
   tile = (Tile*)m_tileList[i];
   
   // Tile liegt im Frustum. Jetzt also die Tile gegen 
   // das Frustum clippen:
   m_frustum->clip(*tile);

   // Wenn textureID != 0 dann hat das Portal eine Texture
   // (um z.B. den Eingang zu einem geheimen Raum zu maskieren)
   if ((tile->m_typ == Tile::PORTAL) && (tile->m_textureID == 0))
      continue;

   Texture* tex = MaterialManager::getInstance()->get(tile->m_textureID);
   if (tex !=0)
   if (m_surfaceCache->getSurface(tile->m_sid) == 0)
      {
      BYTE intensity = 0;
      float iii = intensity; 

      iii += tile->m_ambientLight;
      iii += tile->m_directionalLight;
   
      // iii kann/darf max. 1.0 sein!!
      intensity = (BYTE)((255.0 * iii) + 0.5);

      tile->m_sid = m_surfaceCache->addSurface(*tex, (tile->m_lightMap), intensity);
      }
   else if (tile->getScrollFlag() != 0)
      {
      BYTE intensity = 0;
      float iii = intensity; 

      iii += tile->m_ambientLight;
      iii += tile->m_directionalLight;
   
      // iii kann/darf max. 1.0 sein!!
      intensity = (BYTE)((255.0 * iii) + 0.5);
      tile->m_sid = m_surfaceCache->updateSurface(tile->m_sid, *tex, (tile->m_lightMap), intensity);
      }

   // Aus der geclippten Vertexliste die Texelkoordinaten ermitteln
   tile->calcTexelVertices();
   }
   
m_renderer->renderTiles(m_tileList);
m_surfaceCache->refresh();   
}

/******************************************************************************/
/** \brief Rendert Partikel, Sprites, etc
    \param zoneNr 
    \param frustum 
*******************************************************************************/
void Engine::paint2D(int zoneNr, Frustum& frustum)
{
m_partikelCount = 0;
for (unsigned int i=0; i < m_partikelManager->m_partikelSystemList.size(); i++)
   {
   PartikelSystem* ps = m_partikelManager->m_partikelSystemList[i];
   if (ps->m_zone != zoneNr)
      continue;

   ps->animate();
   ps->transform(m_matTranslate, m_matRotate);
   ps->project((float)m_frustum->NearPlaneDist, (float)m_frustum->FarPlaneDist);
   
   for (unsigned int n=0; n < ps->m_partikelList.size(); n++)
      {
      Partikel* partikel = ps->m_partikelList[n];
      if (frustum.inside(*partikel) == true)      
         {
         m_partikelCount++;
         m_partikelList.push_back(partikel);   
         }
      }
   }
   
   m_renderer->renderParticles(m_partikelList);
}

/******************************************************************************/
/** \brief Rendert die SkyBox
*******************************************************************************/
void Engine::paintSkyBox()
{
Tile *tile;

std::vector<Tile*>  m_skyboxList;

for (int i=0; i < SkyBox::TILE_COUNT; i++)
   {
   tile = m_skyBox->m_tiles[i];
   tile->resetVertices();

   // Drehe(!) die temporaere Tile in die Kameraposition
   tile->transform(m_matRotate);
   
   // Backface Culling: Alle Skybox-Tiles, die nicht in Richtung Camera, aka Frustumspitze
   // schauen, werden verworfen
   if (tile->check(m_frustum->Origin) == Plane::OUTSIDE)
      {
   // System.out.println("Backface Culling: " + tt.m_name);
      continue; 
      }
      
   // Teste, ob die Tile jetzt im Frustum liegt:
   if (m_frustum->inside(*tile) == false)
     continue; // Tile liegt nicht im Frustum. 

   // Tile liegt im Frustum. Jetzt also die Tile gegen 
   // das Frustum clippen:
   m_frustum->clip(*tile);
   
   // Aus der geclippten Vertexliste die Texelkoordinaten ermitteln
   tile->calcTexelVertices();
   
   Texture* tex = MaterialManager::getInstance()->get(tile->m_textureID);
   if (m_surfaceCache->getSurface(tile->m_sid) == 0)
      {
      tile->m_sid = m_surfaceCache->addSurface(*tex);
      }
      
   m_skyboxList.push_back(tile);
   }
   
   m_renderer->renderTiles(m_skyboxList);
}

#define PAINTER_ALGORITHM_NEW

#ifdef PAINTER_ALGORITHM_NEW
/******************************************************************************/
/** \brief Tiefensortierung, auch Painter's Algorithm genannt:
           Funktioniert leider nur bei einfachen, statischen maps. Deshalb
           wurde mittlerweile auf den Z-Buffer umgestellt. 
*******************************************************************************/
void Engine::sortBack2Front()
{
  unsigned int i;
  Tile* t1 = 0;
  
  if (m_tileList.size() <= 1)
     return; 

  // Zuerst die min/max Distanzen berechnen:
  for (i=0; i < m_tileList.size(); i++)
     {
     t1 = (Tile*) m_tileList[i];
     t1->calcDistances((float)m_frustum->NearPlaneDist);
     }

  // Simple Z-Sortierung (Achtung: std::sort stuerz ab!!!)
  std::stable_sort(m_tileList.begin(), m_tileList.end(), sortZOrder);

  // Ueberlappungen aufloesen (Achtung: std::sort stuerz ab!!!)
  // std::stable_sort(m_tileList.begin(), m_tileList.end(), doSolveZ);
  // solveZConflicts();
  
}

/******************************************************************************/
/** \brief Sortiert in Z-Order Back2Front
    \param t1 Tile #1
    \param t2 Tile #2
    \return true-->OK
            false-->Swap 
*******************************************************************************/
bool sortZOrder ( Tile* t1, Tile* t2 )
{
if (t1->m_maxZDist3D < t2->m_maxZDist3D)    // Back2Front
//if (t1->m_maxZDist3D > t2->m_maxZDist3D)  // Front2Back
  return (false);
return (true);
}

/******************************************************************************/
/** \brief Versucht Z-Konflikte zu loesen. Leider mit maessigem Erfolg 
*******************************************************************************/
void Engine::solveZConflicts (void)
{
  unsigned int i, k;
  bool status;  
  Tile* t1 = 0;
  Tile* t2 = 0;

  for (i=0; i < m_tileList.size() -1 ; i++)
    {
    for (k=i+1; k < m_tileList.size(); k++)
        {
        t1 = (Tile*) m_tileList[i];
        t2 = (Tile*) m_tileList[k];
        
        status = doSolveZ(t1, t2);
        if (status == false)
           {
           m_tileList[i]= t2; // ---- Swap ---- 
           m_tileList[k]= t1; // ---- Swap ----
           }
        }
    }

}

/*******************************************************************************/
/** \brief Sortiert in Z-Order Back2Front
    \param t1 Tile #1
    \param t2 Tile #2
    \return true-->OK
            false-->Swap 
*******************************************************************************/
bool doSolveZ ( Tile* t1, Tile* t2 )
{
  // -----------------------------------------------------------
  // Case 1: Ueberschneiden sich die (projizierten) BR's von t1 und t2 ?
  if (t1->m_maxXDist2D <= t2->m_minXDist2D) // t1 ist links von t2
     return (true); 
  if (t2->m_maxXDist2D <= t1->m_minXDist2D) // t2 ist links von t1
     return (true);

  if (t1->m_maxYDist2D <= t2->m_minYDist2D) // t1 ist unterhalb von t2
     return (true);
  if (t2->m_maxYDist2D <= t1->m_minYDist2D) // t2 ist unterhalb von t1
     return (true);

  if (t2->m_maxZDist3D < t1->m_minZDist3D) // t2 ist vor t1
     return (true);

  bool status;  
  if (t1->m_maxZDist3D >= t2->m_maxZDist3D)
     status = checkSides(t1, t2);
  else
     status = checkSides(t2, t1);
  return (status);
}

/****************************************************************************/
/** \brief  Loest Konflikte
    \param t1 Tile #1
    \param t2 Tile #2
    \return true-->OK
            false-->Swap 
*****************************************************************************/
bool checkSides(Tile* t1, Tile* t2)
{
   bool hitA1, hitA2;
   int a, n;
   hitA1 = true;
   for (n=0; n < t2->m_numVertices; n++) // Test A1: Alle Punkte von t2 muessen VOR oder IN t1 sein
      {
      if (t1->check(t2->m_vertex[n]) == Plane::OUTSIDE) // HAL Wenn nicht, Abbruch
         {
         hitA1 = false;
         break;
         }
      }
   
   if (hitA1 == true) // Test A1: bestanden --> Return
      {
//      System.out.println("Test A1: bestanden --> Return");
      return (true);
      }
   
   hitA2 = true;
   for (n=0; n < t1->m_numVertices; n++) // Test A2: Alle Punkte von t1 muessen HINTER oder IN t2 sein
      {
      a = t2->check(t1->m_vertex[n]);
      if ((a != Plane::INPLANE) && (a != Plane::OUTSIDE)) // HAL Wenn nicht, Abbruch
         {
         hitA2 = false;
         break;
         }
      }
   
   if (hitA2 == true) // Test A2: bestanden --> Return
      {
//      System.out.println("Test A2: bestanden --> Return");
      return (true);
      }

//   System.out.println("SWAP");
   return (false); // Swap
}


#else
/******************************************************************************/
/** \brief Tiefensortierung, auch Painter's Algorithm genannt: 
*******************************************************************************/
void Engine::sortBack2Front()
{
  int i, start=0;
  Tile* t1 = 0;
  Tile* t2 = 0;
  
  int bla = m_tileList.size();   
  
  if (m_tileList.size() <= 1)
     return; 

  // Zuerst die min/max Distanzen berechnen:
  for (i=0; i < m_tileList.size(); i++)
     {
     t1 = (Tile*) m_tileList[i];
     t1->calcDistances(m_frustum->NearPlaneDist);
     }

  std::stable_sort(m_tileList.begin(), m_tileList.end(), sortZOrder);
  //Achtung: std::sort stuerz ab!!!  
}

/******************************************************************************/
/** \brief Tiefensortierung, auch Painter's Algorithm genannt: Sortiert die Tiles
    \param t1 Tile #1
    \param t2 Tile #2
    \return true-->OK
            false-->Swap 
*******************************************************************************/
bool sortZOrder ( Tile* t1, Tile* t2 )
{
bool retCode=true;
bool status=true;

if (t1->m_maxZDist3D == t2->m_maxZDist3D)
  {
  if (t1->m_minZDist3D > t2->m_minZDist3D)
     {
     retCode = false;
     }
  }
else if (t1->m_maxZDist3D < t2->m_maxZDist3D)
  {
  retCode = false;
  }

return (retCode);
}
#endif

/******************************************************************************/
/** \brief Speichert die Map in eine (binaere) Mapdatei
    \param fname Pfad+Dateiname+Extension der Map-Datei 
*******************************************************************************/
void Engine::saveAsMapfile(char* fname)
{
m_engineIO->save(fname);
}

/******************************************************************************/
/** \brief laedt die (binaere) Mapdatei
    \param fname Pfad+Dateiname+Extension der Map-Datei 
*******************************************************************************/
bool Engine::loadMapfile(char* fname)
{
return (m_engineIO->load(fname));
}

/******************************************************************************/
/** \brief Berechnung des Animationsverhalten
*******************************************************************************/
void Engine::calcAnimation(void)
{
m_map->calcAnimation();
}

/******************************************************************************/
/** \brief Berechnet die Lightmap-Grids 
*******************************************************************************/
void Engine::calcLightMapGrid()
{
m_map->calcLightMapGrid();
}

/******************************************************************************/
/** \brief Berechnet die Lightmaps 
*******************************************************************************/
void Engine::calcLightMap()
{
m_map->calcLightMap();
}

/****************************************************************************/
/** \brief Wertet die Tastatureingabe aus. Tasten sind in Keys.h definiert
           Bei Bewegungstasten wird die Cameraposition neu berechnet
           Einige Tasten veraendern die Lichteinstellung
           Einige Tasten loesen Spezialfunktionen aus
           Alle anderen Tasten werden ignoriert
     \param pressed true-->Key pressed, false-->Key released      
     \param key Tastaturcode 
     \return true->Frame muss neu berechnet werden, false->Keine Aktion noetig
*****************************************************************************/
bool Engine::inputKey(bool pressed, int key)
{
bool status = false;

if (m_isRendererInitialized == false)
   return(status);

if (m_rendering == true)
   return (false);

// Forward und Backward
if ((key == C4_KEY_UP) && (pressed==true) && (m_camera->isMoving()==Camera::STOP))
   m_camera->move(Camera::STEP_FORWARD);
else if ((key == C4_KEY_DOWN) && (pressed==true) && (m_camera->isMoving()==Camera::STOP))
   m_camera->move(Camera::STEP_BACKWARD);   

if ((key==C4_KEY_UP) && (pressed==false) && (m_camera->isMoving()==Camera::STEP_FORWARD))
   m_camera->move(Camera::STOP);
else if ((key==C4_KEY_DOWN) && (pressed==false) && (m_camera->isMoving()==Camera::STEP_BACKWARD))
   m_camera->move(Camera::STOP);

// Strafe left, Strafe right
if ((key == C4_KEY_LEFT) && (pressed==true) && (m_camera->isStrafing()==Camera::STOP))
   m_camera->strafe(Camera::STRAFE_LEFT);
else if ((key == C4_KEY_DOWN) && (pressed==true) && (m_camera->isStrafing()==Camera::STOP))
   m_camera->strafe(Camera::STRAFE_RIGHT);   

if ((key==C4_KEY_UP) && (pressed==false) && (m_camera->isStrafing()==Camera::STRAFE_LEFT))
   m_camera->strafe(Camera::STOP);
else if ((key==C4_KEY_DOWN) && (pressed==false) && (m_camera->isStrafing()==Camera::STRAFE_RIGHT))
   m_camera->strafe(Camera::STOP);

// lift up, lift down
if ((key == C4_KEY_NEXT) && (pressed==true) && (m_camera->isLifting()==Camera::STOP))
   m_camera->lift(Camera::STEP_UP);
else if ((key == C4_KEY_PRIOR) && (pressed==true) && (m_camera->isLifting()==Camera::STOP))
   m_camera->lift(Camera::STEP_DOWN);   

if ((key==C4_KEY_NEXT) && (pressed==false) && (m_camera->isLifting()==Camera::STEP_UP))
   m_camera->lift(Camera::STOP);
else if ((key==C4_KEY_PRIOR) && (pressed==false) && (m_camera->isLifting()==Camera::STEP_DOWN))
   m_camera->lift(Camera::STOP);

// Rotate left, Rotate right
if ((key == C4_KEY_LEFT) && (pressed==true) && (m_camera->isRotating()==Camera::STOP))
   m_camera->rotate(Camera::ROTATE_LEFT);
else if ((key == C4_KEY_RIGHT) && (pressed==true) && (m_camera->isRotating()==Camera::STOP))
   m_camera->rotate(Camera::ROTATE_RIGHT);   

if ((key==C4_KEY_LEFT) && (pressed==false) && (m_camera->isRotating()==Camera::ROTATE_LEFT))
   m_camera->rotate(Camera::STOP);
else if ((key==C4_KEY_RIGHT) && (pressed==false) && (m_camera->isRotating()==Camera::ROTATE_RIGHT))
   m_camera->rotate(Camera::STOP);

if (pressed==true)
   {
   switch (key)
      {
      case C4_KEY_F1: m_drawInfo = !m_drawInfo;                  status = true;  break;   
      case C4_KEY_F2: m_camera->dump();                          status = false; break;
      case C4_KEY_F3: m_lightManager->toggleAmbientLight();      status = true;  break;
      case C4_KEY_F4: m_lightManager->toggleDirectionalLight();  status = true;  break;
      case C4_KEY_F5: m_lightManager->toggleLightMap();          status = true;  break;
      case C4_KEY_F6: m_lightManager->stepGamma(true);           status = true;  break;
      case C4_KEY_F7: m_lightManager->stepGamma(false);          status = true;  break;
      }
   }
return(status);
}

/****************************************************************************/
/** \brief Getter fuer die Framerate
    \return frames per second
*****************************************************************************/
double Engine::getFrameRate(void)
  {
  return (m_frameRate);
  }

/****************************************************************************/
/** \brief Getter fuer die Anzahl der gerenderten Tiles im aktuellen Frame
    \return tiles per frame
*****************************************************************************/
int Engine::getTilesPerFrame(void)
  {
  return (m_tileList.size());
  }

/****************************************************************************/
/** \brief Getter fuer die Anzahl der gerenderten Partikel im aktuellen Frame
    \return partikel per second
*****************************************************************************/
int Engine::getPartikelsPerFrame(void)
  {
  return (m_partikelCount);
  }
