/* -*- Objc -*- */ /* * $Id: ParticleSystem.m,v 1.1 2003/07/15 13:59:10 madruon Exp $ * * Copyright (C) 2003 Free Software Foundation, Inc. * * This file is part of GNU Hégémonie. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "ParticleSystem/ParticleSystem.h" /** * This class is the base of all the particle systems. * It doesn't be instanciate directly. * For all particle systems, you must fix all desired parameters and call * the "generate" method which initialises the system. Then, to move each * particle you must call the "move" method of the system with the passed * time from the last call. The "draw" method shows all particles. */ @implementation ParticleSystem /** * Initialisation of all commons variables. */ - (id) init { self = [super init]; if (self != nil) { _position = MakeCoord (0.0, 0.0, 0.0); _orientation = MakeCoord (0.0, 1.0, 0.0); _infiniteSystem = YES; _numberOfParticles = 500; _lifeMin = 0.0; _lifeMax = 0.0; _lifeStep = 0.0; _speedMin = 0.0; _speedMax = 0.0; _speedStep = 0.0; _sizeMin = 0.0; _sizeMax = 1.0; _sizeStep = 0.1; _colorRedMin = 0.0; _colorRedMax = 1.0; _colorRedStep = 0.1; _colorGreenMin = 0.0; _colorGreenMax = 1.0; _colorGreenStep = 0.1; _colorBlueMin = 0.0; _colorBlueMax = 1.0; _colorBlueStep = 0.1; _rotationAngle = 0; _rotationAxis = MakeCoord (0.0, 0.0, 0.0); for (_index = 0; _index < 16; _index++) { _modelViewMatrix[_index] = 0.0; } _right = MakeCoord (0.0, 0.0, 0.0); _up = MakeCoord (0.0, 0.0, 0.0); _tabParticles = nil; _texture = 0; _blendEnabled = GL_FALSE; _textureEnabled = GL_FALSE; _index = 0; } return self; } /** * Sets the value of "infiniteSystem". * If this value is "true" then when a particle dead, a new one is created. */ - (void) setInifiniteSystem: (BOOL)infiniteSystem { _infiniteSystem = infiniteSystem; } /** * Sets the number of particles in the system. */ - (void) setNumberOfParticles: (unsigned) numberOfParticles { _numberOfParticles = numberOfParticles; } /** * Sets the position of the system in the OpenGL repere. */ - (void) setPosition: (coord_t)position { _position = position; } /** * Sets the general orientation of the system in the OpenGL repere. */ - (void) setOrientation: (coord_t)orientation { if ((orientation.x == 0.0) && (orientation.y == 0.0) && (orientation.z == 0.0)) { NSAssert (NO, @"Invalid orientation of the particle system"); } coord_t origin = MakeCoord (0.0, 1.0, 0.0); _orientation = orientation; _rotationAngle = acos (origin.x * _orientation.x + origin.y * _orientation.y + origin.z * _orientation.z) * 180 / M_PI; _rotationAxis.x = origin.y * _orientation.z - origin.z * _orientation.y; _rotationAxis.y = origin.z * _orientation.x - origin.x * _orientation.z; _rotationAxis.z = origin.x * _orientation.y - origin.y * _orientation.x; } /** * Sets the minimal life and the maximal life of one particle. */ - (void) setLifeMin: (double)lifeMin lifeMax: (double)lifeMax lifeStep: (double)lifeStep { _lifeMin = lifeMin; _lifeMax = lifeMax; _lifeStep = lifeStep; } /** * Sets the minimal speed and the maximal speed of one particle. */ - (void) setSpeedMin: (double)speedMin speedMax: (double)speedMax speedStep: (double)speedStep { _speedMin = speedMin; _speedMax = speedMax; _speedStep = speedStep; } /** * Sets the minimal size and the maximal size of one particle. */ - (void) setSizeMin: (double)sizeMin sizeMax: (double)sizeMax sizeStep: (double)sizeStep { _sizeMin = sizeMin; _sizeMax = sizeMax; _sizeStep = sizeStep; } /** * Sets the minimal red color and the maximal red color of one particle. */ - (void) setColorRedMin: (double)colorRedMin colorRedMax: (double)colorRedMax colorRedStep: (double)colorRedStep { _colorRedMin = colorRedMin; _colorRedMax = colorRedMax; _colorRedStep = colorRedStep; } /** * Sets the minimal green color and the maximal green color of one particle. */ - (void) setColorGreenMin: (double)colorGreenMin colorGreenMax: (double)colorGreenMax colorGreenStep: (double)colorGreenStep { _colorGreenMin = colorGreenMin; _colorGreenMax = colorGreenMax; _colorGreenStep = colorGreenStep; } /** * Sets the minimal blue color and the maximal blue color of one particle. */ - (void) setColorBlueMin: (double)colorBlueMin colorBlueMax: (double)colorBlueMax colorBlueStep: (double)colorBlueStep { _colorBlueMin = colorBlueMin; _colorBlueMax = colorBlueMax; _colorBlueStep = colorBlueStep; } /** * Sets the shininess of the system. */ - (void) setShininess: (double)shininess { _shininess = shininess; } /** * Sets the particles texture. */ - (void) setTexture: (GLuint)textureName { _texture = textureName; } /** * Generates the system. It creates a new array of particles. */ - (void) generate { _tabParticles = nil; _tabParticles = [NSMutableArray arrayWithCapacity: _numberOfParticles]; NSParameterAssert (_tabParticles != nil); srand (time (NULL)); for(_index = 0; _index < _numberOfParticles; _index++) { [_tabParticles insertObject: [self createParticle] atIndex: _index]; } } /** * Moves all particles contained in the array. If one of them is dead and * the system is infinite then a new one is created. */ - (void) move: (double)elapsedTime { for(_index = 0; _index < _numberOfParticles; _index++) { if([[_tabParticles objectAtIndex: _index] isAlive] == YES) { // The particle is not dead : we move it [[_tabParticles objectAtIndex: _index] move: elapsedTime]; } else { // The particle is dead... if(_infiniteSystem == YES) { // The system is infinite : we create a new particle... [_tabParticles removeObjectAtIndex: _index]; [_tabParticles insertObject: [self createParticle] atIndex: _index]; } else { // ... else the particle is definitively dead } } } } /** * Draws all particles still alive. */ - (void) draw { // We save old parameters _blendEnabled = glIsEnabled (GL_BLEND); _textureEnabled = glIsEnabled (GL_TEXTURE_2D); // We active blending en texture glEnable (GL_BLEND); glEnable (GL_TEXTURE_2D); glDepthMask (GL_FALSE); glBlendFunc (GL_SRC_ALPHA, GL_ONE); glBindTexture (GL_TEXTURE_2D, _texture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glPushMatrix (); { glRotated (_rotationAngle, _rotationAxis.x, _rotationAxis.y, _rotationAxis.z); glTranslated (_position.x, _position.y, _position.z); // For billboarding glGetDoublev (GL_MODELVIEW_MATRIX, _modelViewMatrix); _right = MakeCoord (_modelViewMatrix[0], _modelViewMatrix[4], _modelViewMatrix[8]); _up = MakeCoord (_modelViewMatrix[1], _modelViewMatrix[5], _modelViewMatrix[9]); for (_index = 0; _index < _numberOfParticles; _index++) { if([[_tabParticles objectAtIndex:_index] isAlive] == YES) [[_tabParticles objectAtIndex:_index] drawRight: _right up:_up lifeMax:_lifeMax]; } } glPopMatrix (); // We restaure old parameters glDepthMask (GL_TRUE); if (_textureEnabled == GL_FALSE) glDisable (GL_TEXTURE_2D); if (_blendEnabled == GL_FALSE) glDisable (GL_BLEND); } /** * Generates a new particle life between "_lifeMin" and "_lifeMax" with a * precision of "_lifeStep". */ - (double) computeLife { return (_lifeMin + (int) ((_lifeMax - _lifeMin) / _lifeStep * rand() / (RAND_MAX + 1.0)) * _lifeStep); } /** * Generates a new particle speed between "_speedMin" and "_speedMax" with a * precision of "_speedStep". */ - (double) computeSpeed { return (_speedMin + (int) ((_speedMax - _speedMin) / _speedStep * rand() / (RAND_MAX + 1.0)) * _speedStep); } /** * Generates a new particle size between "_sizeMin" and "_sizeMax" with a * precision of "_sizeStep". */ - (double) computeSize { return (_sizeMin + (int) ((_sizeMax - _sizeMin) / _sizeStep * rand() / (RAND_MAX + 1.0)) * _sizeStep); } /** * Generates a new particle color between "_colorXXXMin" and "_colorXXXMax" * with a precision of "_colorXXXStep" (for the three componants). */ - (NSColor *) computeColor { double tmpColorRed, tmpColorGreen, tmpColorBlue; tmpColorRed = _colorRedMin + (int) ((_colorRedMax - _colorRedMin) / _colorRedStep * rand() / (RAND_MAX + 1.0)) * _colorRedStep; tmpColorGreen = _colorGreenMin + (int) ((_colorGreenMax - _colorGreenMin) / _colorGreenStep * rand() / (RAND_MAX + 1.0)) * _colorGreenStep; tmpColorBlue = _colorBlueMin + (int) ((_colorBlueMax - _colorBlueMin) / _colorBlueStep * rand() / (RAND_MAX + 1.0)) * _colorBlueStep; return ([NSColor colorWithCalibratedRed:tmpColorRed green:tmpColorGreen blue:tmpColorBlue alpha:1.0f]); } /** * Do nothing. Just to force herited classes to implement this method. */ - (Particle *) createParticle { [self subclassResponsibility: _cmd]; return nil; } /** * Removes the array. All particles are deleted. */ - (void) dealloc { /* FIXME - [_tabParticles removeAllObjects] */ RELEASE (_tabParticles); [super dealloc]; } @end