Hard Light Productions Forums

Modding, Mission Design, and Coding => FS2 Open Coding - The Source Code Project (SCP) => Topic started by: zookeeper on April 15, 2013, 09:51:38 am

Title: [Committed] Auto Spread Shields
Post by: zookeeper on April 15, 2013, 09:51:38 am
This patch allows shields which extend a given distance away from the ship's hull, without requiring a shield mesh. This is good for capships, ships with odd shapes with which shield meshes tend to not work right, or ships with large moving parts which would require a huge shield mesh to cover. Or simply if you don't feel like making shield meshes. However, the shield impact effects which normally get projected onto the shield mesh itself won't occur.

Since it'd look bad if, for example, you were close to a capship and your fighter lasers would hit its shields as soon as you pressed the trigger, weapons are always guaranteed to travel at least a distance equal to the thickness of the shield before they're allowed to impact it; unless of course the weapon hits the hull, in which case it is treated as a shield hit, just like when surface shields are used.

To enable the shields for a particular ship type, you need to give the ship type the "auto spread shields" flag and then specify the shield thickness underneath the $Shields attribute:

Code: [Select]
...
$SBank Capacity: ( )
$Shields: 500
  +Auto Spread: 50 ;makes the shield 50m thick
  +Spread From LOD: 2 ;OPTIONAL: projects the shield based on LOD2, helps performance a lot
$Shield Color:                100, 100, 100
...

Later on I might make it a ship-specific rather than shiptype-specific attribute and expose it to Lua, but for now it's defined per shiptype only. I could also add an option to make hits closer to the hull deal more damage than hits to the outer boundary of the shield if someone has a particular need for that, or for disabling the above-mentioned exception to the minimum weapon travel distances. You can make other suggestions, but I won't promise anything.

And here's the patch itself (also as an attachment):

Code: [Select]
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 9629)
+++ ship/ship.h (working copy)
@@ -918,8 +918,9 @@
 #define SIF2_NO_ETS (1 << 13) // The E - No ETS on this ship class
 #define SIF2_NO_LIGHTING (1 << 14) // Valathil - No lighting for this ship
 #define SIF2_DYN_PRIMARY_LINKING (1 << 15) // RSAXVC - Dynamically generate weapon linking options
+#define SIF2_AUTO_SPREAD_SHIELDS (1 << 16) // zookeeper - auto spread shields
 // !!! IF YOU ADD A FLAG HERE BUMP MAX_SHIP_FLAGS !!!
-#define MAX_SHIP_FLAGS 16 // Number of distinct flags for flags field in ship_info struct
+#define MAX_SHIP_FLAGS 17 // Number of distinct flags for flags field in ship_info struct
 #define SIF_DEFAULT_VALUE 0
 #define SIF2_DEFAULT_VALUE 0
 
@@ -1290,6 +1291,8 @@
 
  float max_hull_strength; // Max hull strength of this class of ship.
  float max_shield_strength;
+ float auto_shield_spread;
+ int auto_shield_spread_from_lod;
 
  float hull_repair_rate; //How much of the hull is repaired every second
  float subsys_repair_rate; //How fast
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 9629)
+++ ship/ship.cpp (working copy)
@@ -305,6 +305,7 @@
  { "no pain flash", SIF2_NO_PAIN_FLASH, 1 },
  { "no ets", SIF2_NO_ETS, 1 },
  { "no lighting", SIF2_NO_LIGHTING, 1 },
+ { "auto spread shields", SIF2_AUTO_SPREAD_SHIELDS, 1 },
 
  // to keep things clean, obsolete options go last
  { "ballistic primaries", -1, 255 }
@@ -806,6 +807,8 @@
  }
 
  sip->max_shield_strength = 0.0f;
+ sip->auto_shield_spread = 0.0f;
+ sip->auto_shield_spread_from_lod = -1;
  sip->shield_color[0] = 255;
  sip->shield_color[1] = 255;
  sip->shield_color[2] = 255;
@@ -2428,9 +2431,23 @@
  stuff_bool_list(sip->draw_secondary_models, sip->num_secondary_banks);
  }
 
- if(optional_string("$Shields:"))
+ if(optional_string("$Shields:")) {
  stuff_float(&sip->max_shield_strength);
 
+ if(optional_string("+Auto Spread:")) {
+ stuff_float(&sip->auto_shield_spread);
+ }
+ if(optional_string("+Spread From LOD:")) {
+ int temp;
+ stuff_int(&temp);
+
+ if (temp > sip->num_detail_levels)
+ Warning(LOCATION, "+Spread From LOD for %s was %i whereas ship only has %i detail levels, ignoring...", sip->name, temp, sip->num_detail_levels);
+ else
+ sip->auto_shield_spread_from_lod = temp;
+ }
+ }
+
  // optional shield color
  if(optional_string("$Shield Color:")){
  stuff_ubyte(&sip->shield_color[0]);
@@ -8782,6 +8799,12 @@
  } else
  shipp->shield_integrity = NULL;
 
+ // Bump the object radius to ensure that collision detection works right
+ // even when spread shields extend outside the models natural radius
+ if (sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) {
+ Objects[objnum].radius += sip->auto_shield_spread;
+ }
+
  // allocate memory for keeping glow point bank status (enabled/disabled)
  {
  bool val = true; // default value, enabled
Index: object/collideshipweapon.cpp
===================================================================
--- object/collideshipweapon.cpp (revision 9629)
+++ object/collideshipweapon.cpp (working copy)
@@ -193,41 +193,136 @@
  // will absorb it when it hits the hull instead.  This has no fancy graphical effect, though.
  // Someone should make one.
 
- // set flags
- mc_shield.flags = MC_CHECK_SHIELD;
- mc_hull.flags = MC_CHECK_MODEL;
-
  // check both kinds of collisions
- int shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
- int hull_collision = model_collide(&mc_hull);
+ int shield_collision = 0;
+ int hull_collision = 0;
 
  // check shields for impact
- if (!(ship_objp->flags & OF_NO_SHIELDS))
- {
- // pick out the shield quadrant
- if (shield_collision)
- quadrant_num = get_quadrant(&mc_shield.hit_point);
- else if (hull_collision && (sip->flags2 & SIF2_SURFACE_SHIELDS)) {
- vec3d local_pos, local_pos_rot;
- vm_vec_sub(&local_pos, &mc_hull.hit_point_world, &ship_objp->pos);
- vm_vec_rotate(&local_pos_rot, &local_pos, &ship_objp->orient);
- quadrant_num = get_quadrant(&local_pos_rot);
+ if (!(ship_objp->flags & OF_NO_SHIELDS)) {
+ if (sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) {
+ // The weapon is not allowed to impact the shield before it reaches this point
+ vec3d shield_ignored_until = weapon_objp->last_pos;
+
+ float weapon_flown_for = vm_vec_dist(&wp->start_pos, &weapon_objp->last_pos);
+
+ // If weapon hasn't yet flown a distance greater than the maximum ignore
+ // range, then some part of the currently checked range needs to be
+ // ignored
+ if (weapon_flown_for < sip->auto_shield_spread) {
+ vm_vec_sub(&shield_ignored_until, &weapon_end_pos, &wp->start_pos);
+ vm_vec_normalize(&shield_ignored_until);
+ vm_vec_scale(&shield_ignored_until, sip->auto_shield_spread);
+ vm_vec_add2(&shield_ignored_until, &wp->start_pos);
+ }
+
+ float this_range = vm_vec_dist(&weapon_objp->last_pos, &weapon_end_pos);
+
+ // The range during which the weapon is not allowed to collide with the
+ // shield, except if it actually hits the hull
+ float ignored_range;
+
+ // If the weapon has not yet surpassed the ignore range, calculate the
+ // remaining ignore range
+ if (vm_vec_dist(&wp->start_pos, &shield_ignored_until) > weapon_flown_for)
+ ignored_range = vm_vec_dist(&weapon_objp->last_pos, &shield_ignored_until);
+ else
+ ignored_range = 0.0f;
+
+ // The range during which the weapon may impact the shield
+ float active_range = this_range - ignored_range;
+
+ // During the ignored range, we only check for a ray collision with
+ // the model
+ if (ignored_range > 0.0f) {
+ mc_shield.flags = MC_CHECK_MODEL;
+ mc_shield.p1 = &shield_ignored_until;
+
+ shield_collision = model_collide(&mc_shield);
+
+ mc_shield.p1 = &weapon_end_pos;
+ mc_shield.hit_dist = mc_shield.hit_dist * (ignored_range / this_range);
+ }
+
+ // If no collision with the model found in the ignore range, only
+ // then do we check for sphereline collisions with the model during the
+ // non-ignored range
+ if (!shield_collision && weapon_flown_for + this_range > sip->auto_shield_spread) {
+ mc_shield.p0 = &shield_ignored_until;
+
+ mc_shield.p1 = &weapon_end_pos;
+
+ mc_shield.radius = sip->auto_shield_spread;
+
+ mc_shield.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
+
+ if (sip->auto_shield_spread_from_lod > -1) {
+ polymodel *pm = model_get(sip->model_num);
+ mc_shield.submodel_num = pm->detail[sip->auto_shield_spread_from_lod];
+ }
+
+ shield_collision = model_collide(&mc_shield);
+
+ mc_shield.submodel_num = -1;
+
+ // Because we manipulated p0 and p1 above, hit_dist will be
+ // relative to the values we used, not the values the rest of
+ // the code expects; this fixes that
+ mc_shield.p0 = &weapon_objp->last_pos;
+ mc_shield.p1 = &weapon_end_pos;
+ mc_shield.hit_dist = (ignored_range + (active_range * mc_shield.hit_dist)) / this_range;
+ }
+
+ if (shield_collision) {
+ // If we used a sphereline check, then the collision point will lie
+ // somewhere on the ship's hull; this re-positions it to lie on the
+ // correct point along the weapon's path
+ if (mc_shield.flags & MC_CHECK_SPHERELINE) {
+ vec3d tempv;
+ vm_vec_sub(&tempv, mc_shield.p1, mc_shield.p0);
+ vm_vec_scale(&tempv, mc_shield.hit_dist);
+ vm_vec_add2(&tempv, mc_shield.p0);
+ mc_shield.hit_point_world = tempv;
+ }
+
+ // Re-calculate hit_point because it's likely pointing to the wrong
+ // place
+ vec3d tempv;
+ vm_vec_sub(&tempv, &mc_shield.hit_point_world, &ship_objp->pos);
+ vm_vec_rotate(&mc_shield.hit_point, &tempv, &ship_objp->orient);
+ }
+ } else if (sip->flags2 & SIF2_SURFACE_SHIELDS) {
+ mc_shield.flags = MC_CHECK_MODEL;
+ shield_collision = model_collide(&mc_shield);
+ } else {
+ // Normal collision check against a shield mesh
+ mc_shield.flags = MC_CHECK_SHIELD;
+ shield_collision = (pm->shield.ntris > 0) ? model_collide(&mc_shield) : 0;
  }
+ }
 
+ // If we found a shield collision but were only checking for a simple model
+ // collision, we can re-use the same collision info for the hull as well
+ if (shield_collision && mc_shield.flags == MC_CHECK_MODEL) {
+ memcpy(&mc_hull, &mc_shield, sizeof(mc_info));
+ hull_collision = shield_collision;
+ } else {
+ mc_hull.flags = MC_CHECK_MODEL;
+ hull_collision = model_collide(&mc_hull);
+ }
+
+ if (shield_collision) {
+ // pick out the shield quadrant
+ quadrant_num = get_quadrant(&mc_shield.hit_point);
+
  // make sure that the shield is active in that quadrant
- if ((quadrant_num >= 0) && ((shipp->flags & SF_DYING) || !ship_is_shield_up(ship_objp, quadrant_num)))
+ if (shipp->flags & SF_DYING || !ship_is_shield_up(ship_objp, quadrant_num))
  quadrant_num = -1;
 
  // see if we hit the shield
- if (quadrant_num >= 0)
- {
+ if (quadrant_num >= 0) {
  // do the hit effect
- if (shield_collision)
- add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
- else {
- /* TODO */
-            }
-           
+ add_shield_point(OBJ_INDEX(ship_objp), mc_shield.shield_hit_tri, &mc_shield.hit_point);
+
  // if this weapon pierces the shield, then do the hit effect, but act like a shield collision never occurred;
  // otherwise, we have a valid hit on this shield
  if (wip->wi_flags2 & WIF2_PIERCE_SHIELDS)
@@ -339,6 +434,8 @@
  Assert( ship->type == OBJ_SHIP );
  Assert( weapon->type == OBJ_WEAPON );
 
+ ship_info *sip = &Ship_info[Ships[ship->instance].ship_info_index];
+
  // Don't check collisions for player if past first warpout stage.
  if ( Player->control_mode > PCM_WARPOUT_STAGE1) {
  if ( ship == Player_obj )
@@ -352,11 +449,13 @@
  // If it does hit, don't check the pair until about 200 ms before collision. 
  // If it does not hit and is within error tolerance, cull the pair.
 
- if ( (Ship_info[Ships[ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) {
+ if ( (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) ) {
  // Check when within ~1.1 radii. 
  // This allows good transition between sphere checking (leaving the laser about 200 ms from radius) and checking
  // within the sphere with little time between.  There may be some time for "small" big ships
- if ( vm_vec_dist_squared(&ship->pos, &weapon->pos) < (1.2f*ship->radius*ship->radius) ) {
+ // Note: culling ships with auto spread shields seems to waste more performance than it saves,
+ // so we're not doing that here
+ if ( !(sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) && vm_vec_dist_squared(&ship->pos, &weapon->pos) < (1.2f*ship->radius*ship->radius) ) {
  return check_inside_radius_for_big_ships( ship, weapon, pair );
  }
  }

P.S. Feel free to suggest a better name.

[attachment deleted by ninja]
Title: Re: Auto Spread Shields
Post by: General Battuta on April 15, 2013, 10:54:16 am
holy **** aaa
Title: Re: Auto Spread Shields
Post by: Nohiki on April 15, 2013, 02:41:31 pm
At first I thought this just automatically pressed "Q" all the time, but now that I see it... It's infinitely better than surface shields :)
Title: Re: Auto Spread Shields
Post by: BlasterNT on April 15, 2013, 02:47:32 pm
Ooh.  I don't suppose there's a way to do some auto-topology magic so that shield impacts could be projected?

(What does $Shield Color: in your example do if no effects happen?)
Title: Re: Auto Spread Shields
Post by: MatthTheGeek on April 15, 2013, 03:12:00 pm
Nothing more than it does on shieldless ships would be my guess. It was probably only put there to show between what and what and the new table arguments are supposed to be.
Title: Re: Auto Spread Shields
Post by: zookeeper on April 15, 2013, 03:13:58 pm
Ooh.  I don't suppose there's a way to do some auto-topology magic so that shield impacts could be projected?

Actually I don't think it would be terribly hard to do by just playing the impact animation on a flat quad, positioned on the point of impact and oriented along the hull. Simulating fancier geometry than that would be much harder.

(What does $Shield Color: in your example do if no effects happen?)

Nothing, I guess. It was just there in the table I copypasted the example from.
Title: Re: Auto Spread Shields
Post by: BlasterNT on April 15, 2013, 03:17:37 pm
That would actually be awesome, and avoid issues with animation distortion. 

Come to think of it, don't the WCS builds have some sort of particle system for shield impacts?
Title: Re: Auto Spread Shields
Post by: The E on April 16, 2013, 01:22:53 am
Even if they do, since there's no code available for WCS, it's useless to us.
Title: Re: Auto Spread Shields
Post by: Spoon on April 16, 2013, 06:28:26 am
Even if they do, since there's no code available for WCS, it's useless to us.
I totally remember them saying that the code would be released eventually.
WCS team is too bitter.

Regardless, the shield impacts WCS has looks a fair bit better than the ancient poo poo effect we have in freespace still.
Title: Re: Auto Spread Shields
Post by: MatthTheGeek on April 16, 2013, 07:02:42 am
Yet ancient poo poo effect is better than no effect at all, at least in a FS setting. This patch, intended for FotG, works fine enough for them I suppose, since they don't need any shield impact effect, but it would be cool if someone could set up something to enable a broader use.
Title: Re: Auto Spread Shields
Post by: karajorma on April 16, 2013, 09:18:39 pm
I totally remember them saying that the code would be released eventually.
WCS team is too bitter.

Actually, whatever is preventing them from releasing the code is whatever is preventing them from releasing the Mac OS X version. I have no idea what that is but they've basically said they're willing to release the code. It's a pity because it only hurts them really. If the code was out, someone would have ported the game already.
Title: Re: Auto Spread Shields
Post by: jr2 on May 10, 2013, 04:23:51 pm
Hmm, can this be modded to allow you to fly under a capship's shields and make attack runs?  Sort of like (IIRC) you could do with the Death Star?
Title: Re: Auto Spread Shields
Post by: zookeeper on May 10, 2013, 05:05:57 pm
Hmm, can this be modded to allow you to fly under a capship's shields and make attack runs?  Sort of like (IIRC) you could do with the Death Star?

I don't see why not, but there's at least two different possibilities how that could work:

1) Any shot which impacts on the hull bypasses shields.

2) Any shot which has travelled less than the shield thickness when it hits the hull, bypasses shields.

I guess the latter would make more sense.
Title: Re: Auto Spread Shields
Post by: redsniper on May 13, 2013, 08:37:32 am
What if you were below the shields, but you fired at a shallow angle, so the shot ends up traveling farther than the shield thickness?
Title: Re: Auto Spread Shields
Post by: zookeeper on May 13, 2013, 09:03:04 am
What if you were below the shields, but you fired at a shallow angle, so the shot ends up traveling farther than the shield thickness?

Then it'd impact the shield as soon as the distance travelled exceeds that threshold.
Title: Re: Auto Spread Shields
Post by: Dragon on May 13, 2013, 10:46:58 am
Well, that also means that if you're just below the shield, the shot would hit the shield anyway unless fired straight "down" at the ship's hull. I think that the angle should be taken into account as well.
Title: Re: Auto Spread Shields
Post by: Trivial Psychic on May 13, 2013, 09:27:28 pm
Might it be possible to add shield collision into the game engine?  This would prevent an enemy from flying within the shield perimeter.
Title: Re: Auto Spread Shields
Post by: Dragon on May 14, 2013, 06:27:38 am
That would be a nice feature, but sometimes (like with Star Wars ray shielding) you want strikecraft to pass. So it's not a solution to this particular problem.
Title: Re: Auto Spread Shields
Post by: jr2 on May 17, 2013, 12:34:54 pm
Set it as a flag in the mod.ini or whatnot?  OFC then certain people could cheat, but they can cheat anyways if they want.  So you could have shield collisions in certain universes, and not in others.  I'm not sure, though, Star Wars has ray shielding, but don't they have shields for mass, too?  Otherwise all you'd need to take them out would be a Mass Driver, unless they have some pretty nice armor, as well...
Title: Re: Auto Spread Shields
Post by: zookeeper on September 17, 2013, 07:34:48 am
Committed!

In added in this additional optional feature:
2) Any shot which has travelled less than the shield thickness when it hits the hull, bypasses shields.

Taking angles into account would be too complicated so it works the simple way described a few posts ago.

So, an updated usage example:

Code: [Select]
$Shields:                     1000
  +Auto Spread:                 50
  +Allow Bypass:                YES ;defaults to NO
  +Spread From LOD:             2

You'll also need to add the "auto spread shields" ship type flag.
Title: Re: [Committed] Auto Spread Shields
Post by: Goober5000 on September 17, 2013, 09:43:05 am
I missed this the first time around.  This is pretty cool. :)


Ooh.  I don't suppose there's a way to do some auto-topology magic so that shield impacts could be projected?

Actually I don't think it would be terribly hard to do by just playing the impact animation on a flat quad, positioned on the point of impact and oriented along the hull. Simulating fancier geometry than that would be much harder.

That sounds like the best approach.  I took a look at the shield hit display code back when surface-shields were added, and it is utterly inscrutable.
Title: Re: Auto Spread Shields
Post by: Fury on September 17, 2013, 12:44:12 pm
At first I thought this just automatically pressed "Q" all the time, but now that I see it... It's infinitely better than surface shields :)
That's what I thought too. And even now I don't immediately see what's the advantage over old surface shields. Perhaps I'm just missing something really obvious here, wouldn't be the first time.

I missed this the first time around.  This is pretty cool. :)


Ooh.  I don't suppose there's a way to do some auto-topology magic so that shield impacts could be projected?

Actually I don't think it would be terribly hard to do by just playing the impact animation on a flat quad, positioned on the point of impact and oriented along the hull. Simulating fancier geometry than that would be much harder.

That sounds like the best approach.  I took a look at the shield hit display code back when surface-shields were added, and it is utterly inscrutable.
Each weapon already has $Impact explosion and $Impact radius defined in weapons.tbl or tbm, why not just use those for shield hit but replace $Impact explosion with $Shield impact from ships.tbl or tbm? That way shield impact could vary from ship to ship but impact radius is same as it would be when weapon hits hull.
Title: Re: [Committed] Auto Spread Shields
Post by: The E on September 17, 2013, 12:48:02 pm
That's what I thought too. And even now I don't immediately see what's the advantage over old surface shields. Perhaps I'm just missing something really obvious here, wouldn't be the first time.

It allows you to have traditional "shield bubble" behaviour without having to define a shield model. Given the issues we've had with bad shield models (holes etc), and the difficulties inherent in testing them, this is a definitive improvement.
Title: Re: [Committed] Auto Spread Shields
Post by: Fury on September 17, 2013, 12:53:59 pm
Alrighty, I can see that. Now it just needs this impact effect problem figured out and it's golden.
Title: Re: Auto Spread Shields
Post by: Goober5000 on September 17, 2013, 06:31:53 pm
Each weapon already has $Impact explosion and $Impact radius defined in weapons.tbl or tbm, why not just use those for shield hit but replace $Impact explosion with $Shield impact from ships.tbl or tbm? That way shield impact could vary from ship to ship but impact radius is same as it would be when weapon hits hull.

It's not the graphics that are the problem, it's the displaying of them.  The shield code draws shield effect animations on the shield mesh using an array of triangles.  The particular shield effects to flash on or off are indicated by flagging a collection of triangles in the array corresponding to the location of the shield hit.  The array is heavily tied into the POF model code, and doesn't lend itself well to either inspection or expansion.  It would probably take less coding effort to code up a new routine than to modify the existing one.
Title: Re: [Committed] Auto Spread Shields
Post by: Bryan See on February 16, 2014, 03:39:00 am
There's a discussion on whether the shielding code can be improved. It is located here (http://www.hard-light.net/forums/index.php?topic=86849)
Title: Re: [Committed] Auto Spread Shields
Post by: karajorma on February 16, 2014, 09:59:36 am
Don't bump old threads to discuss new ones!