Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/particles/particle_system.cc @ 6612

Last change on this file since 6612 was 6612, checked in by bensch, 18 years ago

orxonox/trunk: particle_system is now loaded differently

ParticleEngine is NOT needed anymore, this is faster and better

File size: 18.5 KB
RevLine 
[4597]1/*
[1853]2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
[1855]10
11   ### File Specific:
[3925]12   main-programmer: Benjamin Grauer
[1855]13   co-programmer: ...
[1853]14*/
15
[5357]16#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS
[1853]17
[3925]18#include "particle_system.h"
[1853]19
[3930]20#include "particle_emitter.h"
21#include "particle_engine.h"
[4338]22
23#include "field.h"
[5511]24#include "model.h"
[4338]25
[5155]26#include "load_param.h"
[5652]27#include "factory.h"
[3942]28#include "material.h"
[4338]29#include "state.h"
[5446]30#include "shell_command.h"
[3930]31
[5944]32#include "parser/tinyxml/tinyxml.h"
[6612]33#include <algorithm>
[4338]34
[6612]35
[5750]36CREATE_FACTORY(ParticleSystem, CL_PARTICLE_SYSTEM);
[5446]37SHELL_COMMAND(texture, ParticleSystem, setMaterialTexture)
38    ->defaultValues(1, "maps/evil-flower.png");
[4725]39
[1856]40using namespace std;
[1853]41
[3245]42/**
[4836]43 *  standard constructor
44 * @param maxCount the Count of particles in the System
45 * @param type The Type of the ParticleSystem
[3245]46*/
[4762]47ParticleSystem::ParticleSystem (unsigned int maxCount, PARTICLE_TYPE type)
[3365]48{
[4602]49  this->init();
[4597]50
[4727]51  this->setMaxCount(maxCount);
[4338]52  this->setType(type, 1);
[3365]53}
[1853]54
[4677]55/**
[5445]56 * @brief creates a Particle System out of a XML-element
57 * @param root: the XML-element to load from
[4677]58 */
[4762]59ParticleSystem::ParticleSystem(const TiXmlElement* root)
[4602]60{
61  this->init();
[4725]62
[4602]63  this->loadParams(root);
64}
[1853]65
[3245]66/**
[4836]67 *  standard deconstructor
[3245]68*/
[4176]69ParticleSystem::~ParticleSystem()
[3543]70{
71  // delete what has to be deleted here
[3935]72   ParticleEngine::getInstance()->removeSystem(this);
[4123]73
74   // deleting all the living Particles
75   while (this->particles)
76     {
77       Particle* tmpDelPart = this->particles;
78       this->particles = this->particles->next;
79       delete tmpDelPart;
80     }
81
82   // deleting all the dead particles
83   while (this->deadList)
84     {
85       Particle* tmpDelPart = this->deadList;
86       this->deadList = this->deadList->next;
87       delete tmpDelPart;
88     }
[4176]89
90   if (this->material)
91     delete this->material;
[3543]92}
[1853]93
[3945]94/**
[4602]95  \brief initializes the ParticleSystem with default values
96*/
[4746]97void ParticleSystem::init()
[4602]98{
99  this->setClassID(CL_PARTICLE_SYSTEM, "ParticleSystem");
100
101  this->material = NULL;
[4727]102  this->setMaxCount(PARTICLE_DEFAULT_MAX_COUNT);
[4602]103  this->count = 0;
104  this->particles = NULL;
105  this->deadList = NULL;
106  this->setConserve(1);
107  this->setLifeSpan(1);
108  this->glID = NULL;
109  this->setType(PARTICLE_DEFAULT_TYPE, 1);
110  ParticleEngine::getInstance()->addSystem(this);
[6612]111
112  this->toList(OM_ENVIRON);
[4602]113}
114
115
116/**
117 * loads Parameters from a TiXmlElement
118 * @param root the XML-element to load from.
119 */
120void ParticleSystem::loadParams(const TiXmlElement* root)
121{
[6512]122  WorldEntity::loadParams(root);
123  PhysicsInterface::loadParams(root);
[4602]124
[5671]125  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
[4727]126      .describe("the maximal count of Particles, that can be emitted into this system");
127
[5671]128  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
[4725]129      .describe("sets the life-span of the Particles.");
[4602]130
[5671]131  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
[5652]132      .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
[4725]133
[5671]134  LoadParam(root, "type", this, ParticleSystem, setType)
[4732]135      .describe("sets the type of the Particles, (dot, spark, sprite or model)");
136
[6222]137  LoadParam(root, "texture", this, ParticleSystem, setMaterialTexture);
[6612]138  LoadParamXML(root, "emitter", this, ParticleSystem, addEmitterXML);
[6222]139
[5654]140  LOAD_PARAM_START_CYCLE(root, element);
[4727]141  {
[5654]142    element->ToText();
[4725]143  // PER-PARTICLE-ATTRIBUTES:
[5654]144    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
[4727]145        .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
[4725]146
[5654]147    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
[4727]148        .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
[4725]149
[5654]150    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
[4727]151        .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
152  }
[5654]153  LOAD_PARAM_END_CYCLE(element);
[4602]154}
[4727]155
[4725]156/**
[4836]157* @param maxCount the maximum count of particles that can be emitted
[4727]158 */
159void ParticleSystem::setMaxCount(int maxCount)
160{
161  this->maxCount = maxCount;
162}
163
164
165/**
[4836]166 * @param particleType the type of particles in this System
167 * @param count how many particles (in PARTICLE_MULTI-mode)
168   @todo this will be different
[4725]169*/
170void ParticleSystem::setType(const char* particleType)
171{
[4726]172  if (!strcmp(particleType, "dot"))
[4725]173    this->setType(PARTICLE_DOT);
[4726]174  else if(!strcmp(particleType, "spark"))
[4725]175    this->setType(PARTICLE_SPARK);
[4726]176  else if(!strcmp(particleType, "model"))
[4725]177    this->setType(PARTICLE_MODEL);
178  else // if (strcmp(particleType, "SPRITE"))
179    this->setType(PARTICLE_SPRITE);
180}
[4602]181
182/**
[4836]183 * @param particleType the type of particles in this System
184 * @param count how many particles (in PARTICLE_MULTI-mode)
[5446]185   @todo this MUST be different
[3945]186*/
[3942]187void ParticleSystem::setType(PARTICLE_TYPE particleType, int count)
188{
189  this->particleType = particleType;
190  this->dialectCount = count;
[4338]191  //  if (glID != NULL)
192  //    delete glID;
[3942]193
[4338]194  //  glID = new GLuint[count];
195  //  for (int i = 0; i< count; i++)
196  //    glID[i] = 0;
[3942]197
[4338]198  //  glID[0] = glGenLists(count);
199  if (this->material)
200    delete this->material;
201  this->material = NULL;
[3946]202
[4663]203  switch (this->particleType)
[4338]204    {
[4663]205      case PARTICLE_SPRITE:
206        this->material = new Material("transperencyMap");
[5445]207        this->material->setDiffuseMap("maps/radialTransparency.png");
[4338]208      //  material->setTransparency(.5);
[4663]209        break;
[4726]210  }
[3942]211}
212
[3932]213// setting properties
[4478]214/**
[4836]215 *  sets the material to an external material
216 * @param material: the material to set this material to.
[4478]217
218   !! important if the extern material gets deleted it MUST be unregistered here or segfault !!
219*/
[3932]220void ParticleSystem::setMaterial(Material* material)
221{
[3935]222  this->material = material;
[3932]223}
[3931]224
[5446]225void ParticleSystem::setMaterialTexture(const char* textureFile)
226{
227  if (this->material != NULL)
228    this->material->setDiffuseMap(textureFile);
229}
230
[3938]231/**
[4836]232 *  Sets the lifespan of newly created particles
[4597]233*/
[3932]234void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
235{
[3934]236  this->lifeSpan = lifeSpan;
237  this->randomLifeSpan = randomLifeSpan;
[3932]238}
239
[3945]240/**
[4836]241 *  sets the conserve Factor of newly created particles
[3945]242*/
[4430]243void ParticleSystem::setConserve(float conserve)
[3932]244{
[4430]245  if (conserve > 1.0)
246    this->conserve = 1.0;
247  else if (conserve < 0.0)
248    this->conserve = 0.0;
249  else
250    this->conserve = conserve;
[3932]251}
252
[4430]253/////////////////////////////
254/* Per-Particle Attributes */
255/////////////////////////////
[3945]256/**
[4836]257 *  sets a key in the radius-animation on a per-particle basis
258 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
259 * @param radius the radius at this position
260 * @param randRadius the randRadius at this position
[4378]261*/
[4430]262void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
[4378]263{
[4649]264  this->radiusAnim.changeEntry(lifeCycleTime, radius);
265  this->randRadiusAnim.changeEntry(lifeCycleTime, randRadius);
[4378]266}
267
268/**
[4836]269 *  sets a key in the mass-animation on a per-particle basis
270 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
271 * @param mass the mass at this position
272 * @param randMass the randomMass at this position
[3945]273*/
[4430]274void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
[3932]275{
[4649]276  this->massAnim.changeEntry(lifeCycleTime, mass);
277  this->randMassAnim.changeEntry(lifeCycleTime, randMass);
[3932]278}
279
[3945]280/**
[4836]281 *  sets a key in the color-animation on a per-particle basis
282 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
283 * @param red: red
284 * @param green: green
285 * @param blue: blue
286 * @param alpha: alpha
[4338]287*/
[4725]288void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
[4338]289{
[4649]290  this->colorAnim[0].changeEntry(lifeCycleTime, red);
291  this->colorAnim[1].changeEntry(lifeCycleTime, green);
292  this->colorAnim[2].changeEntry(lifeCycleTime, blue);
293  this->colorAnim[3].changeEntry(lifeCycleTime, alpha);
[4338]294}
295
[6612]296
297void ParticleSystem::addEmitter(ParticleEmitter* emitter)
298{
299  this->emitters.push_back(emitter);
300}
301
302void ParticleSystem::addEmitterXML(const TiXmlElement* emitterRoot)
303{
304  ParticleEmitter* emitter = new ParticleEmitter(emitterRoot);
305  this->addEmitter(emitter);
306}
307
308
309void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
310{
311  std::list<ParticleEmitter*>::iterator it = std::find(this->emitters.begin(), this->emitters.end(), emitter);
312  if (it != this->emitters.end())
313    this->emitters.erase(it);
314}
315
[4338]316/**
[4836]317 *  ticks the system.
318 * @param dt the time to tick all the Particles of the System
[3932]319
[3945]320   this is used to get all the particles some motion
321*/
[3931]322void ParticleSystem::tick(float dt)
323{
[3934]324  Particle* tickPart = particles;  // the particle to Tick
[4692]325  Particle* prevPart = NULL;
[3934]326  while (likely(tickPart != NULL))
[3932]327    {
[4378]328      // applying force to the System.
[4385]329      if (likely (tickPart->mass > 0.0))
[4597]330        tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
[4378]331      // rendering new position.
[4687]332      tickPart->position += tickPart->velocity * dt;
333      tickPart->orientation += tickPart->momentum *dt;
334
[4434]335      tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
[4597]336        + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
[4434]337
338      tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
[4597]339        + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
340
[4338]341      tickPart->extForce = Vector(0,0,0);
342
343      // applying Color
[4431]344      tickPart->color[0] = this->colorAnim[0].getValue(tickPart->lifeCycle);
345      tickPart->color[1] = this->colorAnim[1].getValue(tickPart->lifeCycle);
346      tickPart->color[2] = this->colorAnim[2].getValue(tickPart->lifeCycle);
347      tickPart->color[3] = this->colorAnim[3].getValue(tickPart->lifeCycle);
348
[3932]349      // many more to come
350
[3935]351      if (this->conserve < 1.0)
[4691]352      {
353        tickPart->velocity *= this->conserve;
354        tickPart->momentum *= this->conserve;
355      }
[3934]356      // find out if we have to delete tickPart
[4385]357      if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
[4597]358        {
359          // remove the particle from the list
360          if (likely(prevPart != NULL))
361            {
362              prevPart->next = tickPart->next;
363              tickPart->next = this->deadList;
364              this->deadList = tickPart;
365              tickPart = prevPart->next;
366            }
367          else
368            {
369              prevPart = NULL;
370              this->particles = tickPart->next;
371              tickPart->next = this->deadList;
372              this->deadList = tickPart;
373              tickPart = this->particles;
374            }
375          --this->count;
376        }
[3934]377      else
[4597]378        {
379          prevPart = tickPart;
380          tickPart = tickPart->next;
381        }
[3932]382    }
[6612]383
384    std::list<ParticleEmitter*>::iterator emitter;
385    for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
386      (*emitter)->tick(dt, this);
[3932]387}
388
[4597]389/**
[4836]390  *  applies some force to a Particle.
391  * @param field the Field to apply.
[4338]392 */
[4395]393void ParticleSystem::applyField(Field* field)
[4338]394{
395  Particle* tickPart = particles;
396  while (tickPart)
397    {
[4395]398      tickPart->extForce += field->calcForce(tickPart->position);
[4338]399      tickPart = tickPart->next;
400    }
401}
402
403
[3945]404/**
[4836]405 * @returns the count of Faces of this ParticleSystem
[4677]406 */
[4746]407unsigned int ParticleSystem::getFaceCount() const
[4677]408{
409  switch (this->particleType)
410  {
411    case PARTICLE_SPRITE:
412      return this->count;
413      break;
414    case PARTICLE_MODEL:
[5994]415      if (this->getModel(0))
[6308]416        return this->count * this->getModel()->getTriangleCount();
[4677]417      break;
418  }
419}
420
421
422/**
[4836]423 *  draws all the Particles of this System
[4338]424
425   The Cases in this Function all do the same:
426   Drawing all the particles with the appropriate Type.
427   This is just the fastest Way to do this, but will most likely be changed in the future.
[4663]428 */
[4746]429void ParticleSystem::draw() const
[3932]430{
[4176]431  glPushAttrib(GL_ENABLE_BIT);
[4338]432
[4176]433  Particle* drawPart = particles;
[4597]434
[4176]435  switch (this->particleType)
[4663]436  {
[4338]437    default:
[4176]438    case PARTICLE_SPRITE:
[4667]439      glDisable(GL_LIGHTING);
[4176]440      glMatrixMode(GL_MODELVIEW);
[4515]441      glDepthMask(GL_FALSE);
[4338]442
[4597]443      material->select();
[4863]444      glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
445
[4357]446      //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
[4338]447
[4597]448
[3934]449      while (likely(drawPart != NULL))
[4663]450      {
451        glColor4fv(drawPart->color);
[4836]452          //! @todo implement a faster code for the look-at Camera algorithm.
[4338]453
[4836]454        const PNode* camera = State::getCamera();  //!< @todo MUST be different
[4663]455        Vector cameraPos = camera->getAbsCoor();
[4827]456        Vector cameraTargetPos = State::getCameraTarget()->getAbsCoor();
[4663]457        Vector view = cameraTargetPos - cameraPos;
458        Vector up = Vector(0, 1, 0);
459        up = camera->getAbsDir().apply(up);
460        Vector h = up.cross(view);
461        Vector v = h.cross(view);
462        h.normalize();
463        v.normalize();
464        v *= .5 * drawPart->radius;
465        h *= .5 * drawPart->radius;
[4338]466
[4663]467        glBegin(GL_TRIANGLE_STRIP);
468        glTexCoord2i(1, 1);
469        glVertex3f(drawPart->position.x - h.x - v.x,
470                   drawPart->position.y - h.y - v.y,
471                   drawPart->position.z - h.z - v.z);
472        glTexCoord2i(0, 1);
473        glVertex3f(drawPart->position.x - h.x + v.x,
474                   drawPart->position.y - h.y + v.y,
475                   drawPart->position.z - h.z + v.z);
476        glTexCoord2i(1, 0);
477        glVertex3f(drawPart->position.x + h.x - v.x,
478                   drawPart->position.y + h.y - v.y,
479                   drawPart->position.z + h.z - v.z);
480        glTexCoord2i(0, 0);
481        glVertex3f(drawPart->position.x + h.x + v.x,
482                   drawPart->position.y + h.y + v.y,
483                   drawPart->position.z + h.z + v.z);
[4338]484
[4663]485        glEnd();
[4597]486
[4663]487        drawPart = drawPart->next;
488      }
[4515]489      glDepthMask(GL_TRUE);
[4176]490      break;
491
492    case PARTICLE_SPARK:
[4667]493      glDisable(GL_LIGHTING);
[4716]494      glDepthMask(GL_FALSE);
495      //glEnable(GL_LINE_SMOOTH);
496      glEnable(GL_BLEND);
497
[4176]498      glBegin(GL_LINES);
499      while (likely(drawPart != NULL))
[4663]500      {
501        glColor4fv(drawPart->color);
502        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
[4716]503        glVertex3f(drawPart->position.x - drawPart->velocity.x * drawPart->radius,
504                   drawPart->position.y - drawPart->velocity.y * drawPart->radius,
505                   drawPart->position.z - drawPart->velocity.z * drawPart->radius);
[4663]506        drawPart = drawPart->next;
507      }
[4176]508      glEnd();
509      break;
[4597]510
[4663]511    case PARTICLE_MODEL:
512      {
[4687]513        GLfloat matrix[4][4];
[4663]514
[5994]515        if (likely(this->getModel() != NULL))
[4687]516          while (likely(drawPart != NULL))
517        {
518          glPushMatrix();
519          glMatrixMode(GL_MODELVIEW);
520          /* move */
521          glTranslatef(drawPart->position.x, drawPart->position.y, drawPart->position.z);
522          /* scale */
523          glScalef(drawPart->radius, drawPart->radius, drawPart->radius);
524          /* rotate */
525          drawPart->orientation.matrix (matrix);
526          glMultMatrixf((float*)matrix);
[4663]527
[5994]528          this->getModel()->draw();
[4663]529
[4687]530          glPopMatrix();
531          drawPart = drawPart->next;
532        }
[4726]533        else
[4732]534          PRINTF(2)("no model loaded onto ParticleSystem-%s\n", this->getName());
[4663]535      }
536      break;
537
[4176]538    case PARTICLE_DOT:
[4667]539      glDisable(GL_LIGHTING);
[4176]540      glBegin(GL_POINTS);
541      while (likely(drawPart != NULL))
[4663]542      {
543        glColor4fv(drawPart->color);
[4338]544
[4663]545        glLineWidth(drawPart->radius);
[4176]546
[4663]547        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
548        drawPart = drawPart->next;
549      }
[4176]550      glEnd();
551      break;
[4663]552  }
[4176]553  glPopAttrib();
[3932]554}
555
[3945]556/**
[4836]557 *  adds a new Particle to the System
558 * @param position the initial position, where the particle gets emitted.
559 * @param velocity the initial velocity of the particle.
560 * @param orientation the initial orientation of the Paritcle.
561 * @param momentum the initial momentum of the Particle (the speed of its rotation).
562 * @param data some more data given by the emitter
[3945]563*/
[4690]564void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
[3932]565{
[3934]566  if (this->count <= this->maxCount)
[3932]567    {
[3934]568      // if it is the first Particle
569      if (unlikely(particles == NULL))
[4597]570        {
571          if (likely(deadList != NULL))
572            {
573              this->particles = this->deadList;
574              deadList = deadList->next;
575            }
576          else
577            {
578              PRINTF(5)("Generating new Particle\n");
579              this->particles = new Particle;
580            }
581          this->particles->next = NULL;
582        }
[3934]583      // filling the List from the beginning
584      else
[4597]585        {
586          Particle* tmpPart;
587          if (likely(deadList != NULL))
588            {
589              tmpPart = this->deadList;
590              deadList = deadList->next;
591            }
592          else
593            {
594              PRINTF(5)("Generating new Particle\n");
595              tmpPart = new Particle;
596            }
597          tmpPart->next = this->particles;
598          this->particles = tmpPart;
599        }
[4338]600      particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
601      particles->lifeCycle = 0.0;
[3934]602      particles->position = position;
603      particles->velocity = velocity;
[3951]604
[4690]605      particles->orientation = orientation;
606      particles->momentum = momentum;
[4687]607
[4836]608      //  particle->rotation = ; //! @todo rotation is once again something to be done.
[4434]609      particles->massRand = 2*(float)rand()/RAND_MAX -1;
610      particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
611      particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
612      particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
[3934]613
614      ++this->count;
[3932]615    }
616  else
[4017]617    PRINTF(5)("maximum count of particles reached not adding any more\n");
[3932]618}
[3931]619
[3944]620/**
[4836]621 *  outputs some nice debug information
[3944]622*/
[4746]623void ParticleSystem::debug() const
[3944]624{
[4726]625  PRINT(0)("  ParticleSystem %s, type: ", this->getName());
626  {
627      if (this->particleType == PARTICLE_MODEL)
628        PRINT(0)("model");
629      else if (this->particleType == PARTICLE_SPRITE)
630        PRINT(0)("sprite");
631      else if (this->particleType == PARTICLE_DOT)
632        PRINT(0)("dot");
633      else if (this->particleType == PARTICLE_SPARK)
634        PRINT(0)("spark");
635
636      PRINT(0)("\n");
637  }
638
[3944]639  PRINT(0)("  ParticleCount: %d, maximumCount: %d :: filled %d%%\n", this->count, this->maxCount, 100*this->count/this->maxCount);
[4123]640  if (deadList)
641    {
642      PRINT(0)("  - ParticleDeadList is used: ");
643      int i = 1;
644      Particle* tmpPart = this->deadList;
645      while (tmpPart = tmpPart->next) ++i;
646      PRINT(0)("count: %d\n", i);
647    }
[3944]648}
Note: See TracBrowser for help on using the repository browser.