@c -*-texinfo-*-
@cindex Converting old particle systems

This section documents how to convert the various particle systems
to the new particle system in Crystal Space.

@menu
* Api100 PartConv Explo:: Explosion
* Api100 PartConv Rain:: Rain
* Api100 PartConv Snow:: Snow
* Api100 PartConv Fire:: Fire
* Api100 PartConv Fountain:: Fountain
@end menu

@node Api100 PartConv Explo, Api100 PartConv Rain, Api100 Particle system changes, Api100 Particle system changes
@subsubsection Explosion

To convert the old explosion particle system to new particles you
can use a sphere emitter with a very small radius and with particle
placement in center. Set a very small duration and a high emission rate
so that all particles will appear to be created at once. Using
a linear color effector you can obtain the fading of the particles (use
either alpha mixmode or add depending no the kind of fading you want).
Here is an example of how the old explosion can be emulated using
the new particle system (in code): Note that this is not an attempt to
make a realistic or even nice looking explosion. It is just an attempt
to emulate the ugly old explosion particle system from the past:

@example
  // Create the factory.
  csRef<iMeshFactoryWrapper> mfw = engine->CreateMeshFactory (
	"crystalspace.mesh.object.particles", "explosion");
  if (!mfw) return;

  // Create the mesh and setup material, mixmode, and color.
  csRef<iMeshWrapper> exp = engine->CreateMeshWrapper (mfw, "boing",
	sector, center));
  exp->SetZBufMode(CS_ZBUF_TEST);
  exp->SetRenderPriority (engine->GetAlphaRenderPriority ());
  exp->GetMeshObject()->SetMaterialWrapper (mat);
  exp->GetMeshObject()->SetMixMode (CS_FX_ALPHA);
  exp->GetMeshObject()->SetColor (csColor (1, 1, 0));

  // Find/load the built-in emitter factory. From this factory we
  // can access some of the standard built-in emitters.
  csRef<iParticleBuiltinEmitterFactory> emit_factory = 
      csLoadPluginCheck<iParticleBuiltinEmitterFactory> (
        object_reg, "crystalspace.mesh.object.particles.emitter", false);
  // Find/load the built-in effector factory. From this factory we
  // can access some of the standard built-in effectors.
  csRef<iParticleBuiltinEffectorFactory> eff_factory = 
      csLoadPluginCheck<iParticleBuiltinEffectorFactory> (
        object_reg, "crystalspace.mesh.object.particles.effector", false);

  // Create a sphere emitter where we will have a quick burst of
  // all the particles from the center.
  csRef<iParticleBuiltinEmitterSphere> sphereemit = emit_factory->
    CreateSphere ();
  sphereemit->SetRadius (0.1);
  sphereemit->SetParticlePlacement (CS_PARTICLE_BUILTIN_CENTER);
  sphereemit->SetPosition (csVector3 (0, 0, 0));
  sphereemit->SetInitialVelocity (csVector3 (1, 0, 0), csVector3 (3, 3, 3));
  sphereemit->SetUniformVelocity (false);
  sphereemit->SetDuration (0.1f);
  sphereemit->SetEmissionRate (1000.0f);
  sphereemit->SetInitialTTL (1.0f, 1.0f);

  // Create a lincolor effector to fade out the particles.
  csRef<iParticleBuiltinEffectorLinColor> lincol = eff_factory->
    CreateLinColor ();
  lincol->AddColor (csColor4 (1,1,1,1), 1.0f);
  lincol->AddColor (csColor4 (1,1,1,0), 0.0f);

  // Setup the real particle system and add the emitter and effector
  // we created above.
  csRef<iParticleSystem> partstate =
  	scfQueryInterface<iParticleSystem> (exp->GetMeshObject ());
  partstate->SetParticleSize (csVector2 (0.15f, 0.15f));
  partstate->SetRotationMode (CS_PARTICLE_ROTATE_VERTICES);
  partstate->SetIntegrationMode (CS_PARTICLE_INTEGRATE_BOTH);
  partstate->AddEmitter (sphereemit);
  partstate->AddEffector (lincol);

  // Make sure that the particle mesh and factories are removed
  // when all particles are gone (approx 1 second).
  Sys->Engine->DelayedRemoveObject (1100, exp);
  Sys->Engine->DelayedRemoveObject (1101, mfw);
@end example

@node Api100 PartConv Rain, Api100 PartConv Snow, Api100 PartConv Explo, Api100 Particle system changes
@subsubsection Rain

To convert the old rain particle system to new particles you
can use a box emitter which is oriented at the top where the rain drops
should appear and which is very thin. Here you see an example of a rain
particle system in the world file:

@example
  <meshfact name="rainFact">
    <plugin>crystalspace.mesh.loader.factory.particles</plugin>
    <params>
      <particlesize x="0.02" y="0.37" />
      <minbb>
        <min x="-10" y="0" z="-5" />
        <max x="5" y="6" z="10" />
      </minbb>
      <sortmode>none</sortmode>
      <renderorientation>common</renderorientation>
      <commondirection x="0" y="1" z="0" />
      <emitter type="box">
        <emissionrate>250</emissionrate>
        <mass min="5" max="7.5" />
        <box>
          <min x="-10" y="6" z="-5" />
          <max x="5" y="6" z="10" />
        </box>
        <uniformvelocity />
        <initialvelocity x="0" y="-2.84" z="0" />
        <initialttl min="2.5" max="2.5" />
        <placement>volume</placement>
      </emitter>
      <effector type="lincolor">
        <color red="0" green="0" blue="1" time="2.5" />
      </effector>
    </params>
  </meshfact>
  @dots{}
  <meshobj name="rainfall">
    <priority>alpha</priority>
    <plugin>crystalspace.mesh.loader.particles</plugin>
    <ztest />
    <params>
      <factory>rainFact</factory>
      <mixmode> <add /> </mixmode>
      <material>raindrop</material>
    </params>
    <move>
      <v x="-10" y="0" z="10" />
    </move>
  </meshobj>
@end example

Here is how this could work in code:

@example
  csRef<iMeshFactoryWrapper> mfw = engine->CreateMeshFactory (
      "crystalspace.mesh.object.particles", "rain");
  if (!mfw) return;

  csRef<iMeshWrapper> exp = engine->CreateMeshWrapper (mfw, "custom rain",
	sector, csVector3 (0, 0, 0));

  exp->SetZBufMode(CS_ZBUF_TEST);
  exp->GetMeshObject()->SetMixMode (CS_FX_ADD);
  exp->GetMeshObject()->SetMaterialWrapper (mat);

  csRef<iParticleBuiltinEmitterFactory> emit_factory = 
      csLoadPluginCheck<iParticleBuiltinEmitterFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.emitter", false);
  csRef<iParticleBuiltinEffectorFactory> eff_factory = 
      csLoadPluginCheck<iParticleBuiltinEffectorFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.effector", false);

  csRef<iParticleBuiltinEmitterBox> boxemit = emit_factory->CreateBox ();
  boxemit->SetBox (bbox);
  boxemit->SetParticlePlacement (CS_PARTICLE_BUILTIN_VOLUME);
  boxemit->SetEmissionRate (num / 2.5f);
  boxemit->SetInitialMass (5.0f, 7.5f);
  boxemit->SetUniformVelocity (true);
  boxemit->SetInitialTTL (2.5f, 2.5f);
  boxemit->SetInitialVelocity (csVector3 (0, -2.84f, 0), csVector3 (0));

  csRef<iParticleBuiltinEffectorLinColor> lincol = eff_factory->
    CreateLinColor ();
  lincol->AddColor (csColor4 (.25,.25,.25,1), 2.5f);

  csRef<iParticleSystem> partstate =
  	scfQueryInterface<iParticleSystem> (exp->GetMeshObject ());
  partstate->SetMinBoundingBox (bbox);
  partstate->SetParticleSize (csVector2 (0.3f/50.0f, 0.3f));
  partstate->SetParticleRenderOrientation (CS_PARTICLE_ORIENT_COMMON);
  partstate->SetCommonDirection (csVector3 (0, 1, 0));
  partstate->AddEmitter (boxemit);
  partstate->AddEffector (lincol);
@end example

@node Api100 PartConv Snow, Api100 PartConv Fire, Api100 PartConv Rain, Api100 Particle system changes
@subsubsection Snow

To convert the old snow particle system to new particles you
can use a box emitter which is oriented at the top where the snow drops
should appear and which is very thin. You can use a force effector
with random acceleration on the @sc{xz} plane to get the effect that
snowflakes drift. Here you see an example of a snow particle system in the
world file:

@example
  <meshfact name="snowFact">
    <plugin>crystalspace.mesh.loader.factory.particles</plugin>
    <params>
      <particlesize x="0.17" y="0.17" />
      <minbb>
        <min x="-5" y="-0.3" z="-5" />
        <max x="10" y="6" z="10" />
      </minbb>
      <sortmode>none</sortmode>
      <emitter type="box">
        <emissionrate>250</emissionrate>
        <mass min="5" max="7.5" />
        <box>
          <min x="-5" y="6" z="-5" />
          <max x="10" y="6" z="10" />
        </box>
        <uniformvelocity />
        <initialvelocity x="0" y="-1.42" z="0" />
        <initialttl min="5" max="5" />
        <placement>volume</placement>
      </emitter>
      <effector type="lincolor">
        <color red="0" green="0" blue="1" time="5" />
      </effector>
      <effector type="force">
	<randomacceleration x="2.5" y="0" z="2.5" />
      </effector>
    </params>
  </meshfact>
  @dots{}
  <meshobj name="snowfall">
    <priority>alpha</priority>
    <plugin>crystalspace.mesh.loader.particles</plugin>
    <ztest />
    <params>
      <factory>snowFact</factory>
      <mixmode>
        <add />
      </mixmode>
      <material>snow</material>
    </params>
    <move>
      <v x="10" y="0" z="10" />
    </move>
  </meshobj>
@end example

Here is how this could work in code:

@example
  csRef<iMeshFactoryWrapper> mfw = engine->CreateMeshFactory (
      "crystalspace.mesh.object.particles", "snow");
  if (!mfw) return;

  csRef<iMeshWrapper> exp = engine->CreateMeshWrapper (mfw, "custom snow",
	sector, csVector3 (0, 0, 0));

  exp->SetZBufMode(CS_ZBUF_TEST);
  exp->GetMeshObject()->SetMixMode (CS_FX_ADD);
  exp->GetMeshObject()->SetMaterialWrapper (mat);

  csRef<iParticleBuiltinEmitterFactory> emit_factory = 
      csLoadPluginCheck<iParticleBuiltinEmitterFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.emitter", false);
  csRef<iParticleBuiltinEffectorFactory> eff_factory = 
      csLoadPluginCheck<iParticleBuiltinEffectorFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.effector", false);

  csRef<iParticleBuiltinEmitterBox> boxemit = emit_factory->CreateBox ();
  boxemit->SetBox (bbox);
  boxemit->SetParticlePlacement (CS_PARTICLE_BUILTIN_VOLUME);
  boxemit->SetEmissionRate (num / 5f);
  boxemit->SetInitialMass (5.0f, 7.5f);
  boxemit->SetUniformVelocity (true);
  boxemit->SetInitialTTL (5f, 5f);
  boxemit->SetInitialVelocity (csVector3 (0, -1.42f, 0), csVector3 (0));

  csRef<iParticleBuiltinEffectorLinColor> lincol = eff_factory->
    CreateLinColor ();
  lincol->AddColor (csColor4 (.25,.25,.25,1), 2.5f);

  csRef<iParticleBuiltinEffectorForce> force = eff_factory->
    CreateForce ();
  force->SetRandomAcceleration (csVector3 (1.5f, 0.0f, 1.5f));

  csRef<iParticleSystem> partstate =
  	scfQueryInterface<iParticleSystem> (exp->GetMeshObject ());
  partstate->SetMinBoundingBox (bbox);
  partstate->SetParticleSize (csVector2 (0.07f, 0.07f));
  partstate->AddEmitter (boxemit);
  partstate->AddEffector (lincol);
  partstate->AddEffector (force);
@end example

@node Api100 PartConv Fire, Api100 PartConv Fountain, Api100 PartConv Snow, Api100 Particle system changes
@subsubsection Fire

To convert the old fire particle system to new particles you
can use an emitter which is oriented at the bottom of the flame.
To get behaviour equivalent to the old system use a box-shaped emitter, either
with the metrics specified by  @samp{originbox} or a very small box 
around @samp{origin}.
You can use a force effector with random acceleration to have the particles
shift a bit. Here you see an example of a fire particle system in the
world file:

@example
  <meshfact name="fireFact">
    <plugin>crystalspace.mesh.loader.factory.particles</plugin>
    <params>
      <particlesize x="0.04" y="0.08" />
      <sortmode>none</sortmode>
      <emitter type="sphere">
      	<radius>.1</radius>
        <emissionrate>250</emissionrate>
        <mass min="5" max="7.5" />
        <uniformvelocity />
        <initialvelocity x="0" y="1" z="0" />
        <initialttl min="1" max="1" />
        <placement>center</placement>
      </emitter>
      <effector type="lincolor">
        <color red="0.00" green="0.00" blue="0.00" time="1" />
        <color red="1.00" green="0.35" blue="0.00" time="0.96" />
        <color red="1.00" green="0.22" blue="0.00" time="0.9" />
        <color red="1.00" green="0.12" blue="0.00" time="0.8" />
        <color red="0.80" green="0.02" blue="0.00" time="0.7" />
        <color red="0.60" green="0.00" blue="0.00" time="0.75" />
        <color red="0.40" green="0.00" blue="0.00" time="0.5625" />
        <color red="0.20" green="0.05" blue="0.05" time="0.375" />
        <color red="0.10" green="0.10" blue="0.10" time="0.1875" />
        <color red="0.00" green="0.00" blue="0.00" time="0" />
      </effector>
      <effector type="force">
	<randomacceleration x="2.5" y="2.5" z="2.5" />
      </effector>
    </params>
  </meshfact>
  @dots{}
  <meshobj name="fire">
    <priority>alpha</priority>
    <plugin>crystalspace.mesh.loader.particles</plugin>
    <ztest />
    <params>
      <factory>fireFact</factory>
      <mixmode>
        <add />
      </mixmode>
      <material>firematerial</material>
    </params>
    <move>
      <v x="10" y="0" z="10" />
    </move>
  </meshobj>
@end example

Here is how this could work in code:

@example
  csRef<iMeshFactoryWrapper> mfw = engine->CreateMeshFactory (
      "crystalspace.mesh.object.particles", "fire");
  if (!mfw) return;

  csRef<iMeshWrapper> exp = engine->CreateMeshWrapper (mfw, "custom fire",
	sector, origin);

  exp->SetZBufMode(CS_ZBUF_TEST);
  exp->GetMeshObject()->SetMixMode (CS_FX_ADD);
  exp->GetMeshObject()->SetMaterialWrapper (mat);

  csRef<iParticleBuiltinEmitterFactory> emit_factory = 
      csLoadPluginCheck<iParticleBuiltinEmitterFactory> (
        object_reg, "crystalspace.mesh.object.particles.emitter", false);
  csRef<iParticleBuiltinEffectorFactory> eff_factory = 
      csLoadPluginCheck<iParticleBuiltinEffectorFactory> (
        object_reg, "crystalspace.mesh.object.particles.effector", false);

  csRef<iParticleBuiltinEmitterSphere> sphemit = emit_factory->CreateSphere ();
  // Time to live depends on height of sector.
  float velocity = 0.5f;
  float seconds_to_live = 2.0f;
  sphemit->SetRadius (.2f);
  sphemit->SetParticlePlacement (CS_PARTICLE_BUILTIN_VOLUME);
  sphemit->SetEmissionRate (float (num) / seconds_to_live);
  sphemit->SetInitialMass (5.0f, 7.5f);
  sphemit->SetUniformVelocity (true);
  sphemit->SetInitialTTL (seconds_to_live, seconds_to_live);
  sphemit->SetInitialVelocity (csVector3 (0, velocity, 0),
      csVector3 (0));

  csRef<iParticleBuiltinEffectorLinColor> lincol = eff_factory->
    CreateLinColor ();
  lincol->AddColor (csColor4 (0.00, 0.00, 0.00, 1), 2);
  lincol->AddColor (csColor4 (1.00, 0.35, 0.00, 0), 1.5);
  lincol->AddColor (csColor4 (1.00, 0.22, 0.00, .1), 1.3125);
  lincol->AddColor (csColor4 (1.00, 0.12, 0.00, .3), 1.125);
  lincol->AddColor (csColor4 (0.80, 0.02, 0.00, .8), 0.9375);
  lincol->AddColor (csColor4 (0.60, 0.00, 0.00, .9), 0.75);
  lincol->AddColor (csColor4 (0.40, 0.00, 0.00, .97), 0.5625);
  lincol->AddColor (csColor4 (0.20, 0.00, 0.00, 1), 0.375);
  lincol->AddColor (csColor4 (0.00, 0.00, 0.00, 1), 0.1875);
  lincol->AddColor (csColor4 (0.00, 0.00, 0.00, 1), 0);

  csRef<iParticleBuiltinEffectorForce> force = eff_factory->
    CreateForce ();
  force->SetRandomAcceleration (csVector3 (1.5f, 1.5f, 1.5f));

  csRef<iParticleSystem> partstate =
  	scfQueryInterface<iParticleSystem> (exp->GetMeshObject ());
  partstate->SetParticleSize (csVector2 (0.04f, 0.08f));
  partstate->AddEmitter (sphemit);
  partstate->AddEffector (lincol);
  partstate->AddEffector (force);
@end example

@node Api100 PartConv Fountain, , Api100 PartConv Fire, Api100 Particle system changes
@subsubsection Fountain

To convert the old fountain particle system to new particles you
can use a cone emitter. Use a force emitter with downwards acceleration
so that the fountain particles fall down after a while.
Here you see an example of a fountain particle system in the world file:

@example
  <meshfact name="fountainFact">
    <plugin>crystalspace.mesh.loader.factory.particles</plugin>
    <params>
      <particlesize x="0.1" y="0.1" />
      <emitter type="cone">
        <extent x="0" y="2" z="1" />
        <coneangle>0.2</coneangle>
        <placement>center</placement>
        <mass min="7.5" max="10" />
        <initialttl min="10" max="10.5" />
        <emissionrate>70</emissionrate>
        <initialvelocity x="1.8" y="0" z="0" />
      </emitter>
      <effector type="force">
        <acceleration x="0" y="-0.3" z="0" />
      </effector>
      <effector type="lincolor">
        <color red="0" green="0" blue="1" time="10"/>
        <color red="0" green="0.5" blue="1" time="5"/>
        <color red="1" green="1" blue="1" time="0"/>
      </effector>
    </params>
  </meshfact>
  @dots{}
  <meshobj name="fountain">
    <priority>alpha</priority>
    <plugin>crystalspace.mesh.loader.particles</plugin>
    <ztest />
    <params>
      <factory>fountainFact</factory>
      <mixmode>
        <add />
      </mixmode>
      <material>fountainmaterial</material>
    </params>
    <move>
      <v x="10" y="0" z="10" />
    </move>
  </meshobj>
@end example

Here is how this could work in code:

@example
  csRef<iMeshFactoryWrapper> mfw = engine->CreateMeshFactory (
      "crystalspace.mesh.object.particles", "fountain");
  if (!mfw) return;

  csRef<iMeshWrapper> exp = engine->CreateMeshWrapper (mfw, "custom fountain",
	sector, origin);

  exp->SetZBufMode(CS_ZBUF_TEST);
  exp->GetMeshObject()->SetMixMode (CS_FX_ADD);
  exp->GetMeshObject()->SetMaterialWrapper (mat);

  csRef<iParticleBuiltinEmitterFactory> emit_factory = 
      csLoadPluginCheck<iParticleBuiltinEmitterFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.emitter", false);
  csRef<iParticleBuiltinEffectorFactory> eff_factory = 
      csLoadPluginCheck<iParticleBuiltinEffectorFactory> (
        Sys->object_reg, "crystalspace.mesh.object.particles.effector", false);

  csRef<iParticleBuiltinEmitterCone> conemit = emit_factory->CreateCone ();
  float velocity = 3.0f;
  float seconds_to_live = 1.5f;
  conemit->SetExtent (csVector3 (0, 0.5f, 0));
  conemit->SetConeAngle (0.3f);
  conemit->SetParticlePlacement (CS_PARTICLE_BUILTIN_VOLUME);
  conemit->SetEmissionRate (float (num) / seconds_to_live);
  conemit->SetInitialMass (8.0f, 10.0f);
  conemit->SetInitialTTL (seconds_to_live, seconds_to_live);
  conemit->SetInitialVelocity (csVector3 (0, velocity, 0), csVector3 (0));

  csRef<iParticleBuiltinEffectorLinColor> lincol = eff_factory->
    CreateLinColor ();
  lincol->AddColor (csColor4 (0.25f, 0.35f, 0.55f, 1), seconds_to_live);

  csRef<iParticleBuiltinEffectorForce> force = eff_factory->
    CreateForce ();
  force->SetAcceleration (csVector3 (0.0f, -3.0f, 0.0f));

  csRef<iParticleSystem> partstate =
  	scfQueryInterface<iParticleSystem> (exp->GetMeshObject ());
  partstate->SetParticleSize (csVector2 (0.1f, 0.1f));
  partstate->AddEmitter (conemit);
  partstate->AddEffector (lincol);
  partstate->AddEffector (force);
@end example

