00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <cmath>
00023
00024 #include "animationparticle.h"
00025 #include "imageparticle.h"
00026 #include "log.h"
00027 #include "particle.h"
00028 #include "particleemitter.h"
00029
00030 #include "resources/image.h"
00031 #include "resources/imageset.h"
00032 #include "resources/resourcemanager.h"
00033
00034 #define SIN45 0.707106781f
00035 #define DEG_RAD_FACTOR 0.017453293f
00036
00037 ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map, int rotation):
00038 mOutputPauseLeft(0),
00039 mParticleImage(0)
00040 {
00041 mMap = map;
00042 mParticleTarget = target;
00043
00044
00045 mParticlePosX.set(0.0f);
00046 mParticlePosY.set(0.0f);
00047 mParticlePosZ.set(0.0f);
00048 mParticleAngleHorizontal.set(0.0f);
00049 mParticleAngleVertical.set(0.0f);
00050 mParticlePower.set(0.0f);
00051 mParticleGravity.set(0.0f);
00052 mParticleRandomness.set(0);
00053 mParticleBounce.set(0.0f);
00054 mParticleFollow = false;
00055 mParticleAcceleration.set(0.0f);
00056 mParticleDieDistance.set(-1.0f);
00057 mParticleMomentum.set(1.0f);
00058 mParticleLifetime.set(-1);
00059 mParticleFadeOut.set(0);
00060 mParticleFadeIn.set(0);
00061 mOutput.set(1);
00062 mOutputPause.set(0);
00063 mParticleAlpha.set(1.0f);
00064
00065 for_each_xml_child_node(propertyNode, emitterNode)
00066 {
00067 if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
00068 {
00069 std::string name = XML::getProperty(propertyNode, "name", "");
00070
00071 if (name == "position-x")
00072 {
00073 mParticlePosX = readParticleEmitterProp(propertyNode, 0.0f);
00074 }
00075 else if (name == "position-y")
00076 {
00077
00078 mParticlePosY = readParticleEmitterProp(propertyNode, 0.0f);
00079 mParticlePosY.minVal *= SIN45;
00080 mParticlePosY.maxVal *= SIN45;
00081 mParticlePosY.changeAmplitude *= SIN45;
00082 }
00083 else if (name == "position-z")
00084 {
00085 mParticlePosZ = readParticleEmitterProp(propertyNode, 0.0f);
00086 mParticlePosZ.minVal *= SIN45;
00087 mParticlePosZ.maxVal *= SIN45;
00088 mParticlePosZ.changeAmplitude *= SIN45;
00089 }
00090 else if (name == "image")
00091 {
00092 std::string image = XML::getProperty(propertyNode, "value", "");
00093
00094 if (!image.empty() && !mParticleImage)
00095 {
00096 ResourceManager *resman = ResourceManager::getInstance();
00097 mParticleImage = resman->getImage(image);
00098 }
00099 }
00100 else if (name == "horizontal-angle")
00101 {
00102 mParticleAngleHorizontal = readParticleEmitterProp(propertyNode, 0.0f);
00103 mParticleAngleHorizontal.minVal += rotation;
00104 mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR;
00105 mParticleAngleHorizontal.maxVal += rotation;
00106 mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR;
00107 mParticleAngleHorizontal.changeAmplitude *= DEG_RAD_FACTOR;
00108 }
00109 else if (name == "vertical-angle")
00110 {
00111 mParticleAngleVertical = readParticleEmitterProp(propertyNode, 0.0f);
00112 mParticleAngleVertical.minVal *= DEG_RAD_FACTOR;
00113 mParticleAngleVertical.maxVal *= DEG_RAD_FACTOR;
00114 mParticleAngleVertical.changeAmplitude *= DEG_RAD_FACTOR;
00115 }
00116 else if (name == "power")
00117 {
00118 mParticlePower = readParticleEmitterProp(propertyNode, 0.0f);
00119 }
00120 else if (name == "gravity")
00121 {
00122 mParticleGravity = readParticleEmitterProp(propertyNode, 0.0f);
00123 }
00124 else if (name == "randomnes" || name == "randomness")
00125 {
00126 mParticleRandomness = readParticleEmitterProp(propertyNode, 0);
00127 }
00128 else if (name == "bounce")
00129 {
00130 mParticleBounce = readParticleEmitterProp(propertyNode, 0.0f);
00131 }
00132 else if (name == "lifetime")
00133 {
00134 mParticleLifetime = readParticleEmitterProp(propertyNode, 0);
00135 mParticleLifetime.minVal += 1;
00136 }
00137 else if (name == "output")
00138 {
00139 mOutput = readParticleEmitterProp(propertyNode, 0);
00140 mOutput.maxVal +=1;
00141 }
00142 else if (name == "output-pause")
00143 {
00144 mOutputPause = readParticleEmitterProp(propertyNode, 0);
00145 mOutputPauseLeft = mOutputPause.value(0);
00146 }
00147 else if (name == "acceleration")
00148 {
00149 mParticleAcceleration = readParticleEmitterProp(propertyNode, 0.0f);
00150 }
00151 else if (name == "die-distance")
00152 {
00153 mParticleDieDistance = readParticleEmitterProp(propertyNode, 0.0f);
00154 }
00155 else if (name == "momentum")
00156 {
00157 mParticleMomentum = readParticleEmitterProp(propertyNode, 1.0f);
00158 }
00159 else if (name == "fade-out")
00160 {
00161 mParticleFadeOut = readParticleEmitterProp(propertyNode, 0);
00162 }
00163 else if (name == "fade-in")
00164 {
00165 mParticleFadeIn = readParticleEmitterProp(propertyNode, 0);
00166 }
00167 else if (name == "alpha")
00168 {
00169 mParticleAlpha = readParticleEmitterProp(propertyNode, 1.0f);
00170 }
00171 else if (name == "follow-parent")
00172 {
00173 mParticleFollow = true;
00174 }
00175 else
00176 {
00177 logger->log("Particle Engine: Warning, unknown emitter property \"%s\"",
00178 name.c_str()
00179 );
00180 }
00181 }
00182 else if (xmlStrEqual(propertyNode->name, BAD_CAST "emitter"))
00183 {
00184 ParticleEmitter newEmitter(propertyNode, mParticleTarget, map);
00185 mParticleChildEmitters.push_back(newEmitter);
00186 }
00187 else if (xmlStrEqual(propertyNode->name, BAD_CAST "animation"))
00188 {
00189 ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
00190 XML::getProperty(propertyNode, "imageset", ""),
00191 XML::getProperty(propertyNode, "width", 0),
00192 XML::getProperty(propertyNode, "height", 0)
00193 );
00194
00195
00196 for_each_xml_child_node(frameNode, propertyNode)
00197 {
00198 int delay = XML::getProperty(frameNode, "delay", 0);
00199 int offsetX = XML::getProperty(frameNode, "offsetX", 0);
00200 int offsetY = XML::getProperty(frameNode, "offsetY", 0);
00201 offsetY -= imageset->getHeight() - 32;
00202 offsetX -= imageset->getWidth() / 2 - 16;
00203
00204 if (xmlStrEqual(frameNode->name, BAD_CAST "frame"))
00205 {
00206 int index = XML::getProperty(frameNode, "index", -1);
00207
00208 if (index < 0)
00209 {
00210 logger->log("No valid value for 'index'");
00211 continue;
00212 }
00213
00214 Image *img = imageset->get(index);
00215
00216 if (!img)
00217 {
00218 logger->log("No image at index %d", index);
00219 continue;
00220 }
00221
00222 mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
00223 }
00224 else if (xmlStrEqual(frameNode->name, BAD_CAST "sequence"))
00225 {
00226 int start = XML::getProperty(frameNode, "start", -1);
00227 int end = XML::getProperty(frameNode, "end", -1);
00228
00229 if (start < 0 || end < 0)
00230 {
00231 logger->log("No valid value for 'start' or 'end'");
00232 continue;
00233 }
00234
00235 while (end >= start)
00236 {
00237 Image *img = imageset->get(start);
00238
00239 if (!img)
00240 {
00241 logger->log("No image at index %d", start);
00242 continue;
00243 }
00244
00245 mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
00246 start++;
00247 }
00248 }
00249 else if (xmlStrEqual(frameNode->name, BAD_CAST "end"))
00250 {
00251 mParticleAnimation.addTerminator();
00252 }
00253 }
00254 }
00255 }
00256 }
00257
00258 ParticleEmitter::ParticleEmitter(const ParticleEmitter &o)
00259 {
00260 *this = o;
00261 }
00262
00263 ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter &o)
00264 {
00265 mParticlePosX = o.mParticlePosX;
00266 mParticlePosY = o.mParticlePosY;
00267 mParticlePosZ = o.mParticlePosZ;
00268 mParticleAngleHorizontal = o.mParticleAngleHorizontal;
00269 mParticleAngleVertical = o.mParticleAngleVertical;
00270 mParticlePower = o.mParticlePower;
00271 mParticleGravity = o.mParticleGravity;
00272 mParticleRandomness = o.mParticleRandomness;
00273 mParticleBounce = o.mParticleBounce;
00274 mParticleFollow = o.mParticleFollow;
00275 mParticleTarget = o.mParticleTarget;
00276 mParticleAcceleration = o.mParticleAcceleration;
00277 mParticleDieDistance = o.mParticleDieDistance;
00278 mParticleMomentum = o.mParticleMomentum;
00279 mParticleLifetime = o.mParticleLifetime;
00280 mParticleFadeOut = o.mParticleFadeOut;
00281 mParticleFadeIn = o.mParticleFadeIn;
00282 mParticleAlpha = o.mParticleAlpha;
00283 mMap = o.mMap;
00284 mOutput = o.mOutput;
00285 mOutputPause = o.mOutputPause;
00286 mParticleImage = o.mParticleImage;
00287 mParticleAnimation = o.mParticleAnimation;
00288 mParticleChildEmitters = o.mParticleChildEmitters;
00289
00290 mOutputPauseLeft = 0;
00291
00292 if (mParticleImage) mParticleImage->incRef();
00293
00294 return *this;
00295 }
00296
00297
00298 ParticleEmitter::~ParticleEmitter()
00299 {
00300 if (mParticleImage) mParticleImage->decRef();
00301 }
00302
00303
00304 template <typename T> ParticleEmitterProp<T>
00305 ParticleEmitter::readParticleEmitterProp(xmlNodePtr propertyNode, T def)
00306 {
00307 ParticleEmitterProp<T> retval;
00308
00309 def = (T) XML::getFloatProperty(propertyNode, "value", (double) def);
00310 retval.set((T) XML::getFloatProperty(propertyNode, "min", (double) def),
00311 (T) XML::getFloatProperty(propertyNode, "max", (double) def));
00312
00313 std::string change = XML::getProperty(propertyNode, "change-func", "none");
00314 T amplitude = (T) XML::getFloatProperty(propertyNode, "change-amplitude", 0.0);
00315 int period = XML::getProperty(propertyNode, "change-period", 0);
00316 int phase = XML::getProperty(propertyNode, "change-phase", 0);
00317 if (change == "saw" || change == "sawtooth") {
00318 retval.setFunction(FUNC_SAW, amplitude, period, phase);
00319 } else if (change == "sine" || change == "sinewave") {
00320 retval.setFunction(FUNC_SINE, amplitude, period, phase);
00321 } else if (change == "triangle") {
00322 retval.setFunction(FUNC_TRIANGLE, amplitude, period, phase);
00323 } else if (change == "square"){
00324 retval.setFunction(FUNC_SQUARE, amplitude, period, phase);
00325 }
00326
00327 return retval;
00328 }
00329
00330
00331 std::list<Particle *> ParticleEmitter::createParticles(int tick)
00332 {
00333 std::list<Particle *> newParticles;
00334
00335 if (mOutputPauseLeft > 0)
00336 {
00337 mOutputPauseLeft--;
00338 return newParticles;
00339 }
00340 mOutputPauseLeft = mOutputPause.value(tick);
00341
00342 for (int i = mOutput.value(tick); i > 0; i--)
00343 {
00344
00345 if (Particle::particleCount > Particle::maxCount) break;
00346
00347 Particle *newParticle;
00348 if (mParticleImage)
00349 {
00350 newParticle = new ImageParticle(mMap, mParticleImage);
00351 }
00352 else if (mParticleAnimation.getLength() > 0)
00353 {
00354 Animation *newAnimation = new Animation(mParticleAnimation);
00355 newParticle = new AnimationParticle(mMap, newAnimation);
00356 }
00357 else
00358 {
00359 newParticle = new Particle(mMap);
00360 }
00361
00362 Vector position(mParticlePosX.value(tick),
00363 mParticlePosY.value(tick),
00364 mParticlePosZ.value(tick));
00365 newParticle->moveTo(position);
00366
00367 float angleH = mParticleAngleHorizontal.value(tick);
00368 float angleV = mParticleAngleVertical.value(tick);
00369 float power = mParticlePower.value(tick);
00370 newParticle->setVelocity(
00371 cos(angleH) * cos(angleV) * power,
00372 sin(angleH) * cos(angleV) * power,
00373 sin(angleV) * power);
00374
00375 newParticle->setRandomness(mParticleRandomness.value(tick));
00376 newParticle->setGravity(mParticleGravity.value(tick));
00377 newParticle->setBounce(mParticleBounce.value(tick));
00378 newParticle->setFollow(mParticleFollow);
00379
00380 newParticle->setDestination(mParticleTarget,
00381 mParticleAcceleration.value(tick),
00382 mParticleMomentum.value(tick)
00383 );
00384 newParticle->setDieDistance(mParticleDieDistance.value(tick));
00385
00386 newParticle->setLifetime(mParticleLifetime.value(tick));
00387 newParticle->setFadeOut(mParticleFadeOut.value(tick));
00388 newParticle->setFadeIn(mParticleFadeIn.value(tick));
00389 newParticle->setAlpha(mParticleAlpha.value(tick));
00390
00391 for (std::list<ParticleEmitter>::iterator i = mParticleChildEmitters.begin();
00392 i != mParticleChildEmitters.end();
00393 i++)
00394 {
00395 newParticle->addEmitter(new ParticleEmitter(*i));
00396 }
00397
00398 newParticles.push_back(newParticle);
00399 }
00400
00401 return newParticles;
00402 }