LCOV - code coverage report
Current view: top level - Core/Entities - OOParticleSystem.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 25 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOParticleSystem.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #import "OOParticleSystem.h"
      26             : 
      27             : #import "Universe.h"
      28             : #import "OOTexture.h"
      29             : #import "PlayerEntity.h"
      30             : #import "OOLightParticleEntity.h"
      31             : #import "OOMacroOpenGL.h"
      32             : #import "MyOpenGLView.h"
      33             : 
      34             : 
      35             : //      Testing toy: cause particle systems to stop after half a second.
      36           0 : #define FREEZE_PARTICLES        0
      37             : 
      38             : 
      39             : @implementation OOParticleSystem
      40             : 
      41           0 : - (id) init
      42             : {
      43             :         [self release];
      44             :         return nil;
      45             : }
      46             : 
      47             : 
      48             : /*      Initialize shared aspects of the fragburst entities.
      49             :         Also stashes generated particle speeds in _particleSize[] array.
      50             : */
      51             : - (id) initWithPosition:(HPVector)pos
      52             :                            velocity:(Vector)vel
      53             :                                   count:(unsigned)count
      54             :                            minSpeed:(float)minSpeed
      55             :                            maxSpeed:(float)maxSpeed
      56             :                            duration:(OOTimeDelta)duration
      57             :                           baseColor:(GLfloat[4])baseColor
      58             : {
      59             :         NSParameterAssert(count <= kFragmentBurstMaxParticles);
      60             :         
      61             :         if ((self = [super init]))
      62             :         {
      63             :                 _count = count;
      64             :                 [self setPosition:pos];
      65             :                 
      66             :                 velocity = vel;
      67             :                 _duration = duration;
      68             :                 _maxSpeed = maxSpeed;
      69             :                 
      70             :                 for (unsigned i = 0; i < count; i++)
      71             :                 {
      72             :                         GLfloat speed = minSpeed + 0.5f * (randf()+randf()) * (maxSpeed - minSpeed);    // speed tends toward middle of range
      73             :                         _particleVelocity[i] = vector_multiply_scalar(OORandomUnitVector(), speed);
      74             :                         
      75             :                         Vector color = make_vector(baseColor[0] * 0.1f * (9.5f + randf()), baseColor[1] * 0.1f * (9.5f + randf()), baseColor[2] * 0.1f * (9.5f + randf()));
      76             :                         color = vector_normal(color);
      77             :                         _particleColor[i][0] = color.x;
      78             :                         _particleColor[i][1] = color.y;
      79             :                         _particleColor[i][2] = color.z;
      80             :                         _particleColor[i][3] = baseColor[3];
      81             :                         
      82             :                         _particleSize[i] = speed;
      83             :                 }
      84             :                 
      85             :                 [self setStatus:STATUS_EFFECT];
      86             :                 scanClass = CLASS_NO_DRAW;
      87             :         }
      88             :         
      89             :         return self;
      90             : }
      91             : 
      92             : 
      93           0 : - (NSString *) descriptionComponents
      94             : {
      95             :         return [NSString stringWithFormat:@"ttl: %.3fs", _duration - _timePassed];
      96             : }
      97             : 
      98             : 
      99           0 : - (BOOL) canCollide
     100             : {
     101             :         return NO;
     102             : }
     103             : 
     104             : 
     105           0 : - (BOOL) checkCloseCollisionWith:(Entity *)other
     106             : {
     107             :         if (other == [self owner])  return NO;
     108             :         return ![other isEffect];
     109             : }
     110             : 
     111             : 
     112           0 : - (void) update:(OOTimeDelta) delta_t
     113             : {
     114             :         [super update:delta_t];
     115             :         
     116             :         _timePassed += delta_t;
     117             :         collision_radius += delta_t * _maxSpeed;
     118             :         
     119             :         unsigned        i, count = _count;
     120             :         Vector          *particlePosition = _particlePosition;
     121             :         Vector          *particleVelocity = _particleVelocity;
     122             :         
     123             :         for (i = 0; i < count; i++)
     124             :         {
     125             :                 particlePosition[i] = vector_add(particlePosition[i], vector_multiply_scalar(particleVelocity[i], delta_t));
     126             :         }
     127             :         
     128             :         // disappear eventually.
     129             :         if (_timePassed > _duration)  [UNIVERSE removeEntity:self];
     130             : }
     131             : 
     132             : 
     133           0 : #define DrawQuadForView(x, y, z, sz) \
     134             : do { \
     135             :         glTexCoord2f(0.0, 1.0); glVertex3f(x-sz, y-sz, z); \
     136             :         glTexCoord2f(1.0, 1.0); glVertex3f(x+sz, y-sz, z); \
     137             :         glTexCoord2f(1.0, 0.0); glVertex3f(x+sz, y+sz, z); \
     138             :         glTexCoord2f(0.0, 0.0); glVertex3f(x-sz, y+sz, z); \
     139             : } while (0)
     140             : 
     141             : 
     142           0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
     143             : {
     144             :         if (!translucent || [UNIVERSE breakPatternHide])  return;
     145             : 
     146             :         OO_ENTER_OPENGL();
     147             :         OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
     148             :         
     149             :         OOGL(glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT));
     150             :         
     151             :         OOGL(glEnable(GL_TEXTURE_2D));
     152             :         OOGL(glEnable(GL_BLEND));
     153             :         OOGL(glBlendFunc(GL_SRC_ALPHA, GL_ONE));
     154             :         [[self texture] apply];
     155             :         
     156             :         HPVector                viewPosition = [PLAYER viewpointPosition];
     157             :         HPVector                selfPosition = [self position];
     158             :         
     159             :         unsigned        i, count = _count;
     160             :         Vector          *particlePosition = _particlePosition;
     161             :         GLfloat         (*particleColor)[4] = _particleColor;
     162             :         GLfloat         *particleSize = _particleSize;
     163             :         
     164             :         if ([UNIVERSE reducedDetail])
     165             :         {
     166             :                 // Quick rendering - particle cloud is effectively a 2D billboard.
     167             :                 OOGLPushModelView();
     168             :                 OOGLMultModelView(OOMatrixForBillboard(selfPosition, viewPosition));
     169             :                 
     170             :                 OOGLBEGIN(GL_QUADS);
     171             :                 for (i = 0; i < count; i++)
     172             :                 {
     173             :                         glColor4fv(particleColor[i]);
     174             :                         DrawQuadForView(particlePosition[i].x, particlePosition[i].y, particlePosition[i].z, particleSize[i]);
     175             :                 }
     176             :                 OOGLEND();
     177             :                 
     178             :                 OOGLPopModelView();
     179             :         }
     180             :         else
     181             :         {
     182             :                 float distanceThreshold = collision_radius * 2.0f;      // Distance between player and middle of effect where we start to transition to "non-fast rendering."
     183             :                 float thresholdSq = distanceThreshold * distanceThreshold;
     184             :                 float distanceSq = cam_zero_distance;
     185             :                 
     186             :                 if (distanceSq > thresholdSq)
     187             :                 {
     188             :                         /*      Semi-quick rendering - particle positions are volumetric, but
     189             :                                 orientation is shared. This can cause noticeable distortion
     190             :                                 if the player is close to the centre of the cloud.
     191             :                         */
     192             :                         OOMatrix bbMatrix = OOMatrixForBillboard(selfPosition, viewPosition);
     193             :                         
     194             :                         for (i = 0; i < count; i++)
     195             :                         {
     196             :                                 OOGLPushModelView();
     197             :                                 OOGLTranslateModelView(particlePosition[i]);
     198             :                                 OOGLMultModelView(bbMatrix);
     199             :                                 
     200             :                                 glColor4fv(particleColor[i]);
     201             :                                 OOGLBEGIN(GL_QUADS);
     202             :                                         DrawQuadForView(0, 0, 0, particleSize[i]);
     203             :                                 OOGLEND();
     204             :                                 
     205             :                                 OOGLPopModelView();
     206             :                         }
     207             :                 }
     208             :                 else
     209             :                 {
     210             :                         /*      Non-fast rendering - each particle is billboarded individually.
     211             :                                 The "individuality" factor interpolates between this behavior
     212             :                                 and "semi-quick" to avoid jumping at the boundary.
     213             :                         */
     214             :                         float individuality = 3.0f * (1.0f - distanceSq / thresholdSq);
     215             :                         individuality = OOClamp_0_1_f(individuality);
     216             :                         
     217             :                         for (i = 0; i < count; i++)
     218             :                         {
     219             :                                 OOGLPushModelView();
     220             :                                 OOGLTranslateModelView(particlePosition[i]);
     221             :                                 OOGLMultModelView(OOMatrixForBillboard(HPvector_add(selfPosition, vectorToHPVector(vector_multiply_scalar(particlePosition[i], individuality))), viewPosition));
     222             :                                 
     223             :                                 glColor4fv(particleColor[i]);
     224             :                                 OOGLBEGIN(GL_QUADS);
     225             :                                 DrawQuadForView(0, 0, 0, particleSize[i]);
     226             :                                 OOGLEND();
     227             :                                 
     228             :                                 OOGLPopModelView();
     229             :                         }
     230             :                 }
     231             : 
     232             :         }
     233             :         
     234             :         OOGL(glPopAttrib());
     235             :         
     236             :         OOVerifyOpenGLState();
     237             :         OOCheckOpenGLErrors(@"OOParticleSystem after drawing %@", self);
     238             : }
     239             : 
     240             : 
     241           0 : - (BOOL) isEffect
     242             : {
     243             :         return YES;
     244             : }
     245             : 
     246             : - (OOTexture *) texture
     247             : {
     248             :         return [OOLightParticleEntity defaultParticleTexture];
     249             : }
     250             : 
     251             : #ifndef NDEBUG
     252           0 : - (NSSet *) allTextures
     253             : {
     254             :         return [NSSet setWithObject:[OOLightParticleEntity defaultParticleTexture]];
     255             : }
     256             : #endif
     257             : 
     258             : @end
     259             : 
     260             : 
     261             : @implementation OOSmallFragmentBurstEntity: OOParticleSystem
     262             : 
     263             : - (id) initFragmentBurstFrom:(HPVector)fragPosition velocity:(Vector)fragVelocity size:(GLfloat)size
     264             : {
     265             :         enum
     266             :         {
     267           0 :                 kMinSpeed = 100, kMaxSpeed = 400
     268             :         };
     269             :         
     270           0 :         unsigned count = 0.4f * size;
     271             :         count = MIN(count | 12, (unsigned)kFragmentBurstMaxParticles);
     272             :         
     273             :         // Select base colour
     274             :         // yellow/orange (0.12) through yellow (0.1667) to yellow/slightly green (0.20)
     275           0 :         OOColor *hsvColor = [OOColor colorWithHue:0.12f + 0.08f * randf() saturation:1.0f brightness:1.0f alpha:1.0f];
     276           0 :         GLfloat baseColor[4];
     277           0 :         [hsvColor getRed:&baseColor[0] green:&baseColor[1] blue:&baseColor[2] alpha:&baseColor[3]];
     278             :         
     279           0 :         if ((self = [super initWithPosition:fragPosition velocity:fragVelocity count:count minSpeed:kMinSpeed maxSpeed:kMaxSpeed duration:1.5 baseColor:baseColor]))
     280             :         {
     281             :                 for (unsigned i = 0; i < count; i++)
     282             :                 {
     283             :                         // Note: initWithPosition:... stashes speeds in _particleSize[].
     284             :                         _particleSize[i] = 32.0f * kMinSpeed / _particleSize[i];
     285             :                 }
     286             :         }
     287             :         
     288           0 :         return self;
     289             : }
     290             : 
     291             : 
     292             : + (id) fragmentBurstFromEntity:(Entity *)entity
     293             : {
     294             :         return [[[self alloc] initFragmentBurstFrom:[entity position] velocity:[entity velocity] size:[entity collisionRadius]] autorelease];
     295             : }
     296             : 
     297             : 
     298           0 : - (void) update:(OOTimeDelta) delta_t
     299             : {
     300             : #if FREEZE_PARTICLES
     301             :         if (_timePassed + delta_t > 0.5) delta_t = 0.5 - _timePassed;
     302             : #endif
     303             :         
     304             :         [super update:delta_t];
     305             :         
     306             :         unsigned        i, count = _count;
     307             :         GLfloat         (*particleColor)[4] = _particleColor;
     308             :         GLfloat         timePassed = _timePassed;
     309             :         
     310             :         for (i = 0; i < count; i++)
     311             :         {
     312             :                 GLfloat du = 0.5f + (1.0f/32.0f) * (32 - i);
     313             :                 particleColor[i][3] = OOClamp_0_1_f(1.0f - timePassed / du);
     314             :         }
     315             : }
     316             : 
     317             : @end
     318             : 
     319             : 
     320             : @implementation OOBigFragmentBurstEntity: OOParticleSystem
     321             : 
     322             : - (id) initFragmentBurstFrom:(HPVector)fragPosition velocity:(Vector)fragVelocity size:(GLfloat)size
     323             : {
     324             :         float minSpeed = 1.0f + size * 0.5f;
     325           0 :         float maxSpeed = minSpeed * 4.0f;
     326             :         
     327           0 :         unsigned count = 0.2f * size;
     328             :         count = MIN(count | 3, (unsigned)kBigFragmentBurstMaxParticles);
     329             :         
     330           0 :         GLfloat baseColor[4] = { 1.0f, 1.0f, 0.5f, 1.0f };      
     331             :         
     332             :         size *= 2.0f;    // Account for margins in particle texture.
     333           0 :         if ((self = [super initWithPosition:fragPosition velocity:fragVelocity count:count minSpeed:minSpeed maxSpeed:maxSpeed duration:1.0 baseColor:baseColor]))
     334             :         {
     335             :                 _baseSize = size;
     336             :                 
     337             :                 for (unsigned i = 0; i < count; i++)
     338             :                 {
     339             :                         _particleSize[i] = size;
     340             :                 }
     341             :         }
     342             :         
     343           0 :         return self;
     344             : }
     345             : 
     346             : 
     347             : + (id) fragmentBurstFromEntity:(Entity *)entity
     348             : {
     349             :         return [[[self alloc] initFragmentBurstFrom:[entity position] velocity:vector_multiply_scalar([entity velocity], 0.85) size:[entity collisionRadius]] autorelease];
     350             : }
     351             : 
     352             : 
     353           0 : - (void) update:(double)delta_t
     354             : {
     355             : #if FREEZE_PARTICLES
     356             :         if (_timePassed + delta_t > 0.5) delta_t = 0.5 - _timePassed;
     357             : #endif
     358             :         
     359             :         [super update:delta_t];
     360             :         
     361             :         unsigned        i, count = _count;
     362             :         GLfloat         (*particleColor)[4] = _particleColor;
     363             :         GLfloat         *particleSize = _particleSize;
     364             :         GLfloat         timePassed = _timePassed;
     365             :         GLfloat         duration = _duration;
     366             :         
     367             :         GLfloat size = (1.0f + timePassed) * _baseSize;
     368             :         GLfloat di = 1.0f / (count - 1);
     369             :         
     370             :         for (i = 0; i < count; i++)
     371             :         {
     372             :                 GLfloat du = duration * (0.5f + di * i);
     373             :                 particleColor[i][3] = OOClamp_0_1_f(1.0f - timePassed / du);
     374             :                 
     375             :                 particleSize[i] = size;
     376             :         }
     377             : }
     378             : 
     379             : @end

Generated by: LCOV version 1.14