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

          Line data    Source code
       1           0 : /*
       2             : 
       3             : CollisionRegion.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 "CollisionRegion.h"
      26             : #import "OOMaths.h"
      27             : #import "Universe.h"
      28             : #import "Entity.h"
      29             : #import "ShipEntity.h"
      30             : #import "OOSunEntity.h"
      31             : #import "OOPlanetEntity.h"
      32             : #import "StationEntity.h"
      33             : #import "PlayerEntity.h"
      34             : #import "OODebugFlags.h"
      35             : 
      36             : 
      37           0 : static BOOL positionIsWithinRegion(HPVector position, CollisionRegion *region);
      38           0 : static BOOL sphereIsWithinRegion(HPVector position, GLfloat rad, CollisionRegion *region);
      39           0 : static BOOL positionIsWithinBorders(HPVector position, CollisionRegion *region);
      40             : 
      41             : 
      42             : @implementation CollisionRegion
      43             : 
      44             : // basic alloc/ dealloc routines
      45             : //
      46           0 : static int crid_counter = 1;
      47             : 
      48             : 
      49           0 : - (id) init     // Designated initializer.
      50             : {
      51             :         if ((self = [super init]))
      52             :         {
      53             :                 max_entities = COLLISION_MAX_ENTITIES;
      54             :                 entity_array = (Entity **)malloc(max_entities * sizeof(Entity *));
      55             :                 if (entity_array == NULL)
      56             :                 {
      57             :                         [self release];
      58             :                         return nil;
      59             :                 }
      60             :                 
      61             :                 crid = crid_counter++;
      62             :         }
      63             :         return self;
      64             : }
      65             : 
      66             : 
      67             : - (id) initAsUniverse
      68             : {
      69             :         if ((self = [self init]))
      70             :         {
      71             :                 isUniverse = YES;
      72             :         }
      73             :         return self;
      74             : }
      75             : 
      76             : 
      77             : - (id) initAtLocation:(HPVector)locn withRadius:(GLfloat)rad withinRegion:(CollisionRegion *)otherRegion
      78             : {
      79             :         if ((self = [self init]))
      80             :         {
      81             :                 location = locn;
      82             :                 radius = rad;
      83             :                 border_radius = COLLISION_REGION_BORDER_RADIUS;
      84             :                 parentRegion = otherRegion;
      85             :         }
      86             :         return self;
      87             : }
      88             : 
      89             : 
      90           0 : - (void) dealloc
      91             : {
      92             :         free(entity_array);
      93             :         DESTROY(subregions);
      94             :         
      95             :         [super dealloc];
      96             : }
      97             : 
      98             : 
      99           0 : - (NSString *) description
     100             : {
     101             :         return [NSString stringWithFormat:@"<%@ %p>{ID: %d, %lu subregions, %u ents}", [self class], self, crid, [subregions count], n_entities];
     102             : }
     103             : 
     104             : 
     105             : - (void) clearSubregions
     106             : {
     107             :         [subregions makeObjectsPerformSelector:@selector(clearSubregions)];
     108             :         [subregions removeAllObjects];
     109             : }
     110             : 
     111             : 
     112             : - (void) addSubregionAtPosition:(HPVector)pos withRadius:(GLfloat)rad
     113             : {
     114             :         // check if this can be fitted within any of the subregions
     115             :         //
     116             :         CollisionRegion *sub = nil;
     117             :         foreach (sub, subregions)
     118             :         {
     119             :                 if (sphereIsWithinRegion(pos, rad, sub))
     120             :                 {
     121             :                         // if it fits, put it in!
     122             :                         [sub addSubregionAtPosition:pos withRadius:rad];
     123             :                         return;
     124             :                 }
     125             :                 if (positionIsWithinRegion(pos, sub))
     126             :                 {
     127             :                         // crosses the border of this region already - leave it out
     128             :                         return;
     129             :                 }
     130             :         }
     131             :         // no subregion fit - move on...
     132             :         //
     133             :         sub = [[CollisionRegion alloc] initAtLocation:pos withRadius:rad withinRegion:self];
     134             :         if (subregions == nil)  subregions = [[NSMutableArray alloc] initWithCapacity:32];
     135             :         [subregions addObject:sub];
     136             :         [sub release];
     137             : }
     138             : 
     139             : 
     140             : // update routines to check if a position is within the radius or within its borders
     141             : //
     142           0 : static BOOL positionIsWithinRegion(HPVector position, CollisionRegion *region)
     143             : {
     144             :         if (region == nil)  return NO;
     145             :         if (region->isUniverse)  return YES;
     146             :         
     147             :         HPVector loc = region->location;
     148             :         GLfloat r1 = region->radius;
     149             :         
     150             :          if ((position.x < loc.x - r1)||(position.x > loc.x + r1)||
     151             :                  (position.y < loc.y - r1)||(position.y > loc.y + r1)||
     152             :                  (position.z < loc.z - r1)||(position.z > loc.z + r1))
     153             :          {
     154             :                  return NO;
     155             :          }
     156             :         
     157             :         return YES;
     158             : }
     159             : 
     160             : 
     161           0 : static BOOL sphereIsWithinRegion(HPVector position, GLfloat rad, CollisionRegion *region)
     162             : {
     163             :         if (region == nil)  return NO;
     164             :         if (region->isUniverse)  return YES;
     165             :         
     166             :         HPVector loc = region->location;
     167             :         GLfloat r1 = region->radius;
     168             :         
     169             :          if ((position.x - rad < loc.x - r1)||(position.x + rad > loc.x + r1)||
     170             :                  (position.y - rad < loc.y - r1)||(position.y + rad > loc.y + r1)||
     171             :                  (position.z - rad < loc.z - r1)||(position.z + rad > loc.z + r1))
     172             :          {
     173             :                  return NO;
     174             :          }
     175             :         
     176             :         return YES;
     177             : }
     178             : 
     179             : 
     180           0 : static BOOL positionIsWithinBorders(HPVector position, CollisionRegion *region)
     181             : {
     182             :         if (region == nil)  return NO;
     183             :         if (region->isUniverse)  return YES;
     184             :         
     185             :         HPVector loc = region->location;
     186             :         GLfloat r1 = region->radius + region->border_radius;
     187             :         
     188             :          if ((position.x < loc.x - r1)||(position.x > loc.x + r1)||
     189             :                  (position.y < loc.y - r1)||(position.y > loc.y + r1)||
     190             :                  (position.z < loc.z - r1)||(position.z > loc.z + r1))
     191             :          {
     192             :                  return NO;
     193             :          }
     194             :         
     195             :         return YES;
     196             : }
     197             : 
     198             : 
     199             : // collision checking
     200             : //
     201             : - (void) clearEntityList
     202             : {
     203             :         [subregions makeObjectsPerformSelector:@selector(clearEntityList)];
     204             :         n_entities = 0;
     205             :         isPlayerInRegion = NO;
     206             : }
     207             : 
     208             : 
     209             : - (void) addEntity:(Entity *)ent
     210             : {
     211             :         // expand if necessary
     212             :         //      
     213             :         if (n_entities == max_entities)
     214             :         {
     215             :                 max_entities = 1 + max_entities * 2;
     216             :                 Entity **new_store = (Entity **)realloc(entity_array, max_entities * sizeof(Entity *));
     217             :                 if (new_store == NULL)
     218             :                 {
     219             :                         [NSException raise:NSMallocException format:@"Not enough memory to grow collision region member list."];
     220             :                 }
     221             :                 
     222             :                 entity_array = new_store;
     223             :         }
     224             :         
     225             :         if ([ent isPlayer])  isPlayerInRegion = YES;
     226             :         entity_array[n_entities++] = ent;
     227             : }
     228             : 
     229             : 
     230             : - (BOOL) checkEntity:(Entity *)ent
     231             : {
     232             :         HPVector position = ent->position;
     233             :         
     234             :         // check subregions
     235             :         CollisionRegion *sub = nil;
     236             :         foreach (sub, subregions)
     237             :         {
     238             :                 if (positionIsWithinBorders(position, sub) && [sub checkEntity:ent])
     239             :                 {
     240             :                         return YES;
     241             :                 }
     242             :         }
     243             :         
     244             :         if (!positionIsWithinBorders(position, self))
     245             :         {
     246             :                 return NO;
     247             :         }
     248             :         
     249             :         [self addEntity:ent];
     250             :         [ent setCollisionRegion:self];
     251             :         return YES;
     252             : }
     253             : 
     254             : 
     255             : - (void) findCollisions
     256             : {
     257             :         // test for collisions in each subregion
     258             :         [subregions makeObjectsPerformSelector:@selector(findCollisions)];
     259             :         
     260             :         // reject trivial cases
     261             :         if (n_entities < 2)  return;
     262             :         
     263             :         //
     264             :         // According to Shark, when this was in Universe this was where Oolite spent most time!
     265             :         //
     266             :         Entity          *e1, *e2;
     267             :         HPVector                p1;
     268             :         double          dist2, r1, r2, r0, min_dist2;
     269             :         unsigned        i;
     270             :         Entity          *entities_to_test[n_entities];
     271             :         
     272             :         // only check unfiltered entities
     273             :         unsigned n_entities_to_test = 0;
     274             :         for (i = 0; i < n_entities; i++)
     275             :         {
     276             :                 e1 = entity_array[i];
     277             :                 if (e1->collisionTestFilter != 3)
     278             :                 {
     279             :                         entities_to_test[n_entities_to_test++] = e1;
     280             :                 }
     281             :         }
     282             : 
     283             : #ifndef NDEBUG
     284             :         if (gDebugFlags & DEBUG_COLLISIONS)
     285             :         {
     286             :                 OOLog(@"collisionRegion.debug", @"DEBUG in collision region %@ testing %d out of %d entities", self, n_entities_to_test, n_entities);
     287             :         }
     288             : #endif
     289             :         
     290             :         if (n_entities_to_test < 2)  return;
     291             : 
     292             :         //      clear collision variables
     293             :         //
     294             :         for (i = 0; i < n_entities_to_test; i++)
     295             :         {
     296             :                 e1 = entities_to_test[i];
     297             :                 if (e1->hasCollided)
     298             :                 {
     299             :                         [[e1 collisionArray] removeAllObjects];
     300             :                         e1->hasCollided = NO;
     301             :                 }
     302             :                 if (e1->isShip)
     303             :                 {
     304             :                         [(ShipEntity*)e1 setProximityAlert:nil];
     305             :                 }
     306             :                 e1->collider = nil;
     307             :         }
     308             :         
     309             :         checks_this_tick = 0;
     310             :         checks_within_range = 0;
     311             :         
     312             :         // test each entity in this region against the entities in its collision chain
     313             :         //
     314             :         for (i = 0; i < n_entities_to_test; i++)
     315             :         {
     316             :                 e1 = entities_to_test[i];
     317             :                 p1 = e1->position;
     318             :                 r1 = e1->collision_radius;
     319             :         
     320             :         
     321             : 
     322             :                 // check against the first in the collision chain
     323             :                 e2 = e1->collision_chain;
     324             :                 while (e2 != nil)
     325             :                 {
     326             :                         checks_this_tick++;
     327             :                         if (e1->isShip && e2->isShip && 
     328             :                                 [(ShipEntity *)e1 collisionExceptedFor:(ShipEntity *)e2]) 
     329             :                         {
     330             :                                 // nothing happens
     331             :                         } 
     332             :                         else
     333             :                         {
     334             : 
     335             :                                 r2 = e2->collision_radius;
     336             :                                 r0 = r1 + r2;
     337             :                                 dist2 = HPdistance2(e2->position, p1);
     338             :                                 min_dist2 = r0 * r0;
     339             :                                 if (dist2 < PROXIMITY_WARN_DISTANCE2 * min_dist2)
     340             :                                 {
     341             : #ifndef NDEBUG
     342             :                                         if (gDebugFlags & DEBUG_COLLISIONS)
     343             :                                         {
     344             :                                                 OOLog(@"collisionRegion.debug", @"DEBUG Testing collision between %@ (%@) and %@ (%@)",
     345             :                                                           e1, (e1->collisionTestFilter==3)?@"YES":@"NO", e2, (e2->collisionTestFilter==3)?@"YES":@"NO");
     346             :                                         }
     347             : #endif
     348             :                                         checks_within_range++;
     349             :                                 
     350             :                                         if (e1->isShip && e2->isShip)
     351             :                                         {
     352             :                                                 if ((dist2 < PROXIMITY_WARN_DISTANCE2 * r2 * r2) || (dist2 < PROXIMITY_WARN_DISTANCE2 * r1 * r1))
     353             :                                                 {
     354             :                                                         [(ShipEntity*)e1 setProximityAlert:(ShipEntity*)e2];
     355             :                                                         [(ShipEntity*)e2 setProximityAlert:(ShipEntity*)e1];
     356             :                                                 }
     357             : 
     358             :                                                 if (dist2 >= min_dist2)
     359             :                                                 {
     360             :                                                         if (e1->isStation)
     361             :                                                         {
     362             :                                                                 StationEntity* se1 = (StationEntity *)e1;
     363             :                                                                 [se1 shipIsInDockingCorridor:(ShipEntity *)e2];
     364             :                                                         }
     365             :                                                         else if (e2->isStation)
     366             :                                                         {
     367             :                                                                 StationEntity* se2 = (StationEntity *)e2;
     368             :                                                                 [se2 shipIsInDockingCorridor:(ShipEntity *)e1];
     369             :                                                         }
     370             :                                                 }
     371             : 
     372             :                                         }
     373             :                                         if (dist2 < min_dist2)
     374             :                                         {
     375             :                                                 BOOL collision = NO;
     376             :                                         
     377             :                                                 if (e1->isStation)
     378             :                                                 {
     379             :                                                         StationEntity* se1 = (StationEntity *)e1;
     380             :                                                         if ([se1 shipIsInDockingCorridor:(ShipEntity *)e2])
     381             :                                                         {
     382             :                                                                 collision = NO;
     383             :                                                         }
     384             :                                                         else
     385             :                                                         {
     386             :                                                                 collision = [e1 checkCloseCollisionWith:e2];
     387             :                                                         }
     388             :                                                 }
     389             :                                                 else if (e2->isStation)
     390             :                                                 {
     391             :                                                         StationEntity* se2 = (StationEntity *)e2;
     392             :                                                         if ([se2 shipIsInDockingCorridor:(ShipEntity *)e1])
     393             :                                                         {
     394             :                                                                 collision = NO;
     395             :                                                         }
     396             :                                                         else
     397             :                                                         {
     398             :                                                                 collision = [e2 checkCloseCollisionWith:e1];
     399             :                                                         }
     400             :                                                 }
     401             :                                                 else
     402             :                                                 {
     403             :                                                         collision = [e1 checkCloseCollisionWith:e2];
     404             :                                                 }
     405             :                                 
     406             :                                                 if (collision)
     407             :                                                 {
     408             :                                                         // now we have no need to check the e2-e1 collision
     409             :                                                         if (e1->collider)
     410             :                                                         {
     411             :                                                                 [[e1 collisionArray] addObject:e1->collider];
     412             :                                                         }
     413             :                                                         else
     414             :                                                         {
     415             :                                                                 [[e1 collisionArray] addObject:e2];
     416             :                                                         }
     417             :                                                         e1->hasCollided = YES;
     418             :                                                 
     419             :                                                         if (e2->collider)
     420             :                                                         {
     421             :                                                                 [[e2 collisionArray] addObject:e2->collider];
     422             :                                                         }
     423             :                                                         else
     424             :                                                         {
     425             :                                                                 [[e2 collisionArray] addObject:e1];
     426             :                                                         }
     427             :                                                         e2->hasCollided = YES;
     428             :                                                 }
     429             :                                         }
     430             :                                 }
     431             :                         }
     432             :                         // check the next in the collision chain
     433             :                         e2 = e2->collision_chain;
     434             :                 }
     435             :         }
     436             : 
     437             : #ifndef NDEBUG
     438             :         if (gDebugFlags & DEBUG_COLLISIONS)
     439             :         {
     440             :                 OOLog(@"collisionRegion.debug",@"Collision test checks %d, within range %d, for %d entities",checks_this_tick,checks_within_range,n_entities_to_test);
     441             :         }
     442             : #endif
     443             : }
     444             : 
     445             : 
     446             : // an outValue of 1 means it's just being occluded.
     447           0 : static BOOL entityByEntityOcclusionToValue(Entity *e1, Entity *e2, OOSunEntity *the_sun, float *outValue)
     448             : {
     449             :         if (EXPECT_NOT(e1 == e2))
     450             :         {
     451             :                 // you can't shade self
     452             :                 return NO;
     453             :         }
     454             :         return shadowAtPointOcclusionToValue(e1->position,e1->collision_radius,e2,the_sun,outValue);
     455             : }
     456             : 
     457             : // an outValue of 1 means it's just being occluded.
     458           0 : BOOL shadowAtPointOcclusionToValue(HPVector e1pos, GLfloat e1rad, Entity *e2, OOSunEntity *the_sun, float *outValue)
     459             : {
     460             :         *outValue = 1.5f;       // initial 'fully lit' value
     461             :         
     462             :         GLfloat cr_e2;
     463             :         if ([e2 isShip])
     464             :         {
     465             :                 cr_e2 = e2->collision_radius * 0.90f;
     466             :                 // 10% smaller shadow for ships
     467             :         }
     468             :         else
     469             :         {
     470             :                 cr_e2 = e2->collision_radius;
     471             :         }
     472             :         if (cr_e2 < e1rad)
     473             :         {
     474             :                 // smaller can't shade bigger
     475             :                 return NO;
     476             :         }
     477             :         
     478             :         // tested in construction of e2 list
     479             : //      if (e2->isSunlit == NO)
     480             : //              return NO;      // things already /in/ shade can't shade things more.
     481             :         //
     482             :         // check projected sizes of discs
     483             :         GLfloat d2_sun = HPdistance2(e1pos, the_sun->position);
     484             :         GLfloat d2_e2sun = HPdistance2(e2->position, the_sun->position);
     485             :         GLfloat d2_e2 = HPdistance2( e1pos, e2->position);
     486             : 
     487             :         if (d2_e2sun > d2_sun)
     488             :         {
     489             :                 // you are nearer the sun than the potential occluder, so it
     490             :                 // probably can't shade you
     491             :                 if (d2_e2 < cr_e2 * cr_e2 && [e2 isShip])
     492             :                 {
     493             :                         // exception: if within the collision radius of the other
     494             :                         // object, might still be shadowed by it.
     495             :                         GLfloat bbx = 0.0f, bby = 0.0f, bbz = 0.0f;
     496             :                         BoundingBox bb = [(ShipEntity*)e2 totalBoundingBox];
     497             :                         bounding_box_get_dimensions(bb,&bbx,&bby,&bbz);
     498             :                         float minbb = bbx;
     499             :                         if (bby < minbb) { minbb = bby; }
     500             :                         if (bbz < minbb) { minbb = bbz; }
     501             :                         minbb -= e1rad; // subtract object's size
     502             :                         /* closer to the object than the shortest axis. This check
     503             :                          * branch is basically for docking at a rock hermit facing
     504             :                          * away from the sun, but it checks the shortest bounding
     505             :                          * box size rather than the collision radius to avoid
     506             :                          * getting weird shadowing effects around large planar
     507             :                          * entities like the OXP Torus Station.
     508             :                          *
     509             :                          * Well... more weird shadowing effects than there already
     510             :                          * are, anyway.
     511             :                          *
     512             :                          * There are more accurate ways to check "sphere inside
     513             :                          * bounding box" but this seems accurate enough and is
     514             :                          * simpler.
     515             :                          *
     516             :                          * - CIM
     517             :                          */ 
     518             :                         if (d2_e2 < minbb * minbb)
     519             :                         {
     520             :                                 *outValue = 0.1;
     521             :                                 return YES;
     522             :                         }
     523             :                 }
     524             :                 return NO;
     525             :         }
     526             :         
     527             :         GLfloat cr_sun = the_sun->collision_radius;
     528             :         
     529             :         GLfloat cr2_sun_scaled = cr_sun * cr_sun * d2_e2 / d2_sun;
     530             :         if (cr_e2 * cr_e2 < cr2_sun_scaled)
     531             :         {
     532             :                 // if solar disc projected to the distance of e2 > collision radius it can't be shaded by e2
     533             :                 return NO;
     534             :         }
     535             :         
     536             :         // check angles subtended by sun and occluder
     537             :         // double theta_sun = asin( cr_sun / sqrt(d2_sun));     // 1/2 angle subtended by sun
     538             :         // double theta_e2 = asin( cr_e2 / sqrt(d2_e2));                // 1/2 angle subtended by e2
     539             :         // find the difference between the angles subtended by occluder and sun
     540             :         float d2_e = sqrt(d2_e2);
     541             :         float theta_diff;
     542             :         if (d2_e < cr_e2)
     543             :         {
     544             :                 // then we're "inside" the object. Calculate as if we were on
     545             :                 // the edge of it to avoid taking asin(x>1)
     546             :                 theta_diff = asin(1) - asin(cr_sun / sqrt(d2_sun));
     547             :         }
     548             :         else
     549             :         {
     550             :                 theta_diff = asin(cr_e2 / d2_e) - asin(cr_sun / sqrt(d2_sun));
     551             :         }
     552             :         
     553             :         HPVector p_sun = the_sun->position;
     554             :         HPVector p_e2 = e2->position;
     555             :         HPVector p_e1 = e1pos;
     556             :         Vector v_sun = HPVectorToVector(HPvector_subtract(p_sun, p_e1));
     557             :         v_sun = vector_normal_or_zbasis(v_sun);
     558             :         
     559             :         Vector v_e2 = HPVectorToVector(HPvector_subtract(p_e2, p_e1));
     560             :         v_e2 = vector_normal_or_xbasis(v_e2);
     561             :         
     562             :         float phi = acos(dot_product(v_sun, v_e2));             // angle between sun and e2 from e1's viewpoint
     563             :         *outValue = (phi / theta_diff); // 1 means just occluded, < 1 means in shadow
     564             :         
     565             :         if (phi > theta_diff)
     566             :         {
     567             :                 // sun is not occluded
     568             :                 return NO;
     569             :         }
     570             :         
     571             :         // all tests done e1 is in shade!
     572             :         return YES;
     573             : }
     574             : 
     575             : 
     576           0 : static inline BOOL testEntityOccludedByEntity(Entity *e1, Entity *e2, OOSunEntity *the_sun)
     577             : {
     578             :         float tmp;              // we're not interested in the amount of occlusion just now.
     579             :         return entityByEntityOcclusionToValue(e1, e2, the_sun, &tmp);
     580             : }
     581             : 
     582             : 
     583             : - (void) findShadowedEntities
     584             : {
     585             :         // reject trivial cases
     586             :         if (n_entities < 2)  return;
     587             :         
     588             :         //
     589             :         // Copy/pasting the collision code to detect occlusion!
     590             :         //
     591             :         unsigned i, j;
     592             :         
     593             :         if ([UNIVERSE reducedDetail])  return;  // don't do this in reduced detail mode
     594             :         
     595             :         OOSunEntity* the_sun = [UNIVERSE sun];
     596             :         
     597             :         if (the_sun == nil)
     598             :         {
     599             :                 return; // sun is required
     600             :         }
     601             :         
     602             :         unsigned        ent_count =     UNIVERSE->n_entities;
     603             :         Entity          **uni_entities = UNIVERSE->sortedEntities;   // grab the public sorted list
     604             :         Entity          *planets[ent_count];
     605             :         unsigned        n_planets = 0;
     606             :         Entity          *ships[ent_count];
     607             :         unsigned        n_ships = 0;
     608             :         
     609             :         for (i = 0; i < ent_count; i++)
     610             :         {
     611             :                 if (uni_entities[i]->isSunlit)
     612             :                 {
     613             :                         // get a list of planet entities because they can shade across regions
     614             :                         if ([uni_entities[i] isPlanet])
     615             :                         {
     616             :                                 //      don't bother retaining - nothing will happen to them!
     617             :                                 planets[n_planets++] = uni_entities[i];
     618             :                         }
     619             :                         
     620             :                         // and a list of shipentities large enough that they might cast a noticeable shadow
     621             :                         // if we can't see it, it can't be shadowing anything important
     622             :                         else if ([uni_entities[i] isShip] &&
     623             :                                          [uni_entities[i] isVisible] && 
     624             :                                          uni_entities[i]->collision_radius >= MINIMUM_SHADOWING_ENTITY_RADIUS)
     625             :                         {
     626             :                                 ships[n_ships++] = uni_entities[i];             //      don't bother retaining - nothing will happen to them!
     627             :                         }
     628             :                 }
     629             :         }
     630             :         
     631             :         // test for shadows in each subregion
     632             :         [subregions makeObjectsPerformSelector:@selector(findShadowedEntities)];
     633             :         
     634             :         // test each entity in this region against the others
     635             :         for (i = 0; i < n_entities; i++)
     636             :         {
     637             :                 Entity *e1 = entity_array[i];
     638             :                 if (![e1 isVisible])
     639             :                 {
     640             :                         continue; // don't check shading of objects we can't see
     641             :                 }
     642             :                 BOOL occluder_moved = NO;
     643             :                 if ([e1 status] == STATUS_COCKPIT_DISPLAY)
     644             :                 {
     645             :                         e1->isSunlit = YES;
     646             :                         e1->shadingEntityID = NO_TARGET;
     647             :                         continue;       // don't check shading in demo mode
     648             :                 }
     649             :                 Entity *occluder = nil;
     650             :                 if (e1->isSunlit == NO)
     651             :                 {
     652             :                         occluder = [UNIVERSE entityForUniversalID:e1->shadingEntityID];
     653             :                         if (occluder != nil)
     654             :                         {
     655             :                                 occluder_moved = occluder->hasMoved;
     656             :                         }
     657             :                 }
     658             :                 if (([e1 isShip] ||[e1 isPlanet]) && (e1->hasMoved || occluder_moved))
     659             :                 {
     660             :                         e1->isSunlit = YES;                          // sunlit by default
     661             :                         e1->shadingEntityID = NO_TARGET;
     662             :                         //
     663             :                         // check demo mode here..
     664             :                         if ([e1 isPlayer] && ([(PlayerEntity*)e1 showDemoShips]))
     665             :                         {
     666             :                                 continue;       // don't check shading in demo mode
     667             :                         }
     668             :                         
     669             :                         // test last occluder (most likely case)
     670             :                         if (occluder)
     671             :                         {
     672             :                                 if (testEntityOccludedByEntity(e1, occluder, the_sun))  
     673             :                                 {
     674             :                                         e1->isSunlit = NO;
     675             :                                         e1->shadingEntityID = [occluder universalID];
     676             :                                 }
     677             :                         }
     678             :                         if (!e1->isSunlit)
     679             :                         {
     680             :                                 // no point in continuing tests
     681             :                                 continue;
     682             :                         }
     683             :                         
     684             :                         // test planets
     685             :                         for (j = 0; j < n_planets; j++)
     686             :                         {
     687             :                                 float occlusionNumber;
     688             :                                 if (entityByEntityOcclusionToValue(e1, planets[j], the_sun, &occlusionNumber))
     689             :                                 {
     690             :                                         e1->isSunlit = NO;
     691             :                                         e1->shadingEntityID = [planets[j] universalID];
     692             :                                         break;
     693             :                                 }
     694             :                                 if ([e1 isPlayer])
     695             :                                 {
     696             :                                         [(PlayerEntity *)e1 setOcclusionLevel:occlusionNumber];
     697             :                                 }
     698             :                         }
     699             :                         if (!e1->isSunlit)
     700             :                         {
     701             :                                 // no point in continuing tests
     702             :                                 continue;
     703             :                         }
     704             :                         
     705             :                         // test local entities
     706             :                         for (j = 0; j < n_ships; j++)
     707             :                         {
     708             :                                 if (testEntityOccludedByEntity(e1, ships[j], the_sun))
     709             :                                 {
     710             :                                         e1->isSunlit = NO;
     711             :                                         e1->shadingEntityID = [ships[j] universalID];
     712             :                                         break;
     713             :                                 }
     714             :                         }
     715             :                 }
     716             :         }
     717             : }
     718             : 
     719             : 
     720             : - (NSString *) collisionDescription
     721             : {
     722             :         return [NSString stringWithFormat:@"p%u - c%u", checks_this_tick, checks_within_range];
     723             : }
     724             : 
     725             : 
     726             : - (NSString *) debugOut
     727             : {
     728             :         NSMutableString *result = [[NSMutableString alloc] initWithFormat:@"%d:", n_entities];
     729             :         CollisionRegion *sub = nil;
     730             :         foreach (sub, subregions)
     731             :         {
     732             :                 [result appendString:[sub debugOut]];
     733             :         }
     734             :         return [result autorelease];
     735             : }
     736             : 
     737             : @end

Generated by: LCOV version 1.14