And here's what I hope to be the final version; engine wash works correctly and there's no crash in the ship lab anymore.
I also added a few comments to model.h which would have saved quite a bit of my time had they been there before.
It'd be great if someone could optimize find_submodel_instance_point_normal so that it'd give you a single matrix instead of two vec3d's which you'd then use to transform the thrusterpoint location and normal; I think that way it wouldn't need to get called once per every thrusterpoint, but only once per bank. I might be wrong though.
Index: gamesequence/gamesequence.cpp
===================================================================
--- gamesequence/gamesequence.cpp (revision 7926)
+++ gamesequence/gamesequence.cpp (working copy)
@@ -433,3 +433,16 @@
return -1;
}
+
+// If the given state exists in the stack then return the index, -1 if not
+int gameseq_get_state_idx(int state)
+{
+ for(int i = 0; i <= gs_current_stack; i++)
+ {
+ if(gs[i].current_state == state) {
+ return i;
+ }
+ }
+
+ return -1;
+}
Index: gamesequence/gamesequence.h
===================================================================
--- gamesequence/gamesequence.h (revision 7926)
+++ gamesequence/gamesequence.h (working copy)
@@ -193,4 +193,7 @@
int gameseq_get_event_idx(char *s);
int gameseq_get_state_idx(char *s);
+//zookeeper
+int gameseq_get_state_idx(int state);
+
#endif /* __GAMESEQUENCE_H__ */
Index: model/model.h
===================================================================
--- model/model.h (revision 7926)
+++ model/model.h (working copy)
@@ -90,10 +90,11 @@
bool blown_off;
} submodel_instance;
+// Data specific to a particular instance of a model.
typedef struct polymodel_instance {
- int model_num;
- int root_submodel_num;
- submodel_instance *submodel;
+ int model_num; // global model num index, same as polymodel->id
+ int root_submodel_num; // unused?
+ submodel_instance *submodel; // array of submodel instances; mirrors the polymodel->submodel array
//float gun_submodel_rotation;
} polymodel_instance;
@@ -166,8 +167,8 @@
char subobj_name[MAX_NAME_LEN]; // Temporary (hopefully) parameter used to match stuff in ships.tbl
char alt_sub_name[NAME_LENGTH]; //Karajorma - Name that overrides name of original
char alt_dmg_sub_name[NAME_LENGTH]; // Name for the damage popup subsystems, allows for translation
- int subobj_num; // subobject number (from bspgen) -- used to match subobjects of subsystems to these entries
- int model_num; // Which model this is attached to (i.e. the polymodel[] index)
+ int subobj_num; // subobject number (from bspgen) -- used to match subobjects of subsystems to these entries; index to polymodel->submodel
+ int model_num; // Which model this is attached to (i.e. the polymodel[] index); same as polymodel->id
int type; // type. see SUBSYSTEM_* types above. A generic type thing
vec3d pnt; // center point of this subsystem
float radius; // the extent of the subsystem
@@ -324,6 +325,7 @@
bool collide_invisible; //SUSHI: If set, this submodel should allow collisions for invisible textures. For the "replacement" collision model scheme.
bool force_turret_normal; //Wanderer: Sets the turret uvec to override any input of for turret normal.
char lod_name[MAX_NAME_LEN]; //FUBAR: Name to be used for LOD naming comparison to preserve compatibility with older tables. Only used on LOD0
+ bool attach_thrusters; //zookeeper: If set and this submodel or any of its parents rotates, also rotates associated thrusters.
float dumb_turn_rate;
@@ -359,6 +361,7 @@
bsp_data = NULL;
rad = 0.f;
lod_name[ 0 ] = '\0';
+ attach_thrusters = false;
/* Compound types */
memset( live_debris, 0, sizeof( live_debris ) );
@@ -435,7 +438,8 @@
// Engine wash info
struct engine_wash_info *wash_info_pointer; // index into Engine_wash_info
- int obj_num; // what subsystem number this thruster is on
+ int obj_num; // what subsystem number this bank is on; index to ship_info->subsystems
+ int submodel_num; // what submodel number this bank is on; index to polymodel->submodel/polymodel_instance->submodel
} thruster_bank;
typedef struct glow_point_bank { // glow bank structure -Bobboau
@@ -870,6 +874,8 @@
void world_find_model_instance_point(vec3d *out, vec3d *world_pt, polymodel *pm, polymodel_instance *pmi, int submodel_num, matrix *orient, vec3d *pos);
+extern void find_submodel_instance_point_normal(vec3d *outpnt, vec3d *outnorm, object *ship_obj, int submodel_num, vec3d *submodel_pnt, vec3d *submodel_norm);
+
// Given a polygon model index, find a list of rotating submodels to be used for collision
void model_get_rotating_submodel_list(SCP_vector<int> *submodel_vector, object *objp);
Index: model/modelinterp.cpp
===================================================================
--- model/modelinterp.cpp (revision 7926)
+++ model/modelinterp.cpp (working copy)
@@ -32,6 +32,7 @@
#include "parse/parselo.h"
#include "graphics/gropengllight.h"
#include "ship/shipfx.h"
+#include "gamesequence/gamesequence.h"
#include <limits.h>
@@ -2198,22 +2199,46 @@
gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
for (i = 0; i < pm->n_thrusters; i++ ) {
+ vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
+ bool submodel_rotation = false;
+
bank = &pm->thrusters[i];
// don't draw this thruster if the engine is destroyed or just not on
if ( !model_should_render_engine_glow(objnum, bank->obj_num) )
continue;
+ // If bank is attached to a submodel, prepare to account for rotations
+ //
+ // TODO: This won't work in the ship lab, because the lab code doesn't
+ // set the the necessary submodel instance info needed here. The second
+ // condition is thus a hack to disable the feature while in the lab, and
+ // can be removed if the lab is re-structured accordingly. -zookeeper
+ if ( bank->submodel_num > -1 && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
+ model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_num);
+
+ submodel_rotation = true;
+ }
+
for (j = 0; j < bank->num_points; j++) {
Assert( bank->points != NULL );
float d, D;
vec3d tempv;
glow_point *gpt = &bank->points[j];
+ vec3d loc_offset = gpt->pnt;
+ vec3d loc_norm = gpt->norm;
vec3d world_pnt;
vec3d world_norm;
- vm_vec_unrotate(&world_pnt, &gpt->pnt, orient);
+ if ( submodel_rotation ) {
+ vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
+
+ tempv = loc_offset;
+ find_submodel_instance_point_normal(&loc_offset, &loc_norm, &Objects[objnum], bank->submodel_num, &tempv, &loc_norm);
+ }
+
+ vm_vec_unrotate(&world_pnt, &loc_offset, orient);
vm_vec_add2(&world_pnt, pos);
if (shipp) {
@@ -2238,7 +2263,7 @@
vm_vec_sub(&tempv, &View_position, &world_pnt);
vm_vec_normalize(&tempv);
- vm_vec_unrotate(&world_norm, &gpt->norm, orient);
+ vm_vec_unrotate(&world_norm, &loc_norm, orient);
D = d = vm_vec_dot(&tempv, &world_norm);
// ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
Index: model/modelread.cpp
===================================================================
--- model/modelread.cpp (revision 7926)
+++ model/modelread.cpp (working copy)
@@ -1310,6 +1310,11 @@
if ( (p = strstr(props, "$lod0_name")) != NULL)
get_user_prop_value(p+10, pm->submodel[n].lod_name);
+ if (strstr(props, "$attach_thrusters") != NULL )
+ pm->submodel[n].attach_thrusters = true;
+ else
+ pm->submodel[n].attach_thrusters = false;
+
if ( (p = strstr(props, "$detail_box:")) != NULL ) {
p += 12;
while (*p == ' ') p++;
@@ -1786,7 +1791,8 @@
if (bank->num_points > 0)
bank->points = (glow_point *) vm_malloc(sizeof(glow_point) * bank->num_points);
- bank ->obj_num = -1;
+ bank->obj_num = -1;
+ bank->submodel_num = -1;
if (pm->version < 2117) {
bank->wash_info_pointer = NULL;
@@ -1810,6 +1816,8 @@
bank->wash_info_pointer = NULL;
for (int k=0; k<n_subsystems; k++) {
if ( !subsystem_stricmp(subsystems[k].subobj_name, engine_subsys_name) ) {
+ bank->submodel_num = subsystems[k].subobj_num;
+
bank->wash_info_pointer = subsystems[k].engine_wash_pointer;
if (bank->wash_info_pointer != NULL) {
table_error = 0;
@@ -3878,6 +3886,72 @@
vm_vec_rotate(out, &tempv2, &m);
}
+/**
+ * Finds the current location and rotation of a submodel point, taking into account the
+ * rotations of the submodel and any parent submodels it might have.
+ *
+ * @param *outpnt Output point
+ * @param *outnorm Output normal
+ * @param *ship_obj Ship object
+ * @param submodel_num The number of the submodel we're interested in
+ * @param *submodel_pnt The point which's current position we want, in the submodel's frame of reference
+ * @param *submodel_norm The normal which's current direction we want, in the ship's frame of reference
+ */
+void find_submodel_instance_point_normal(vec3d *outpnt, vec3d *outnorm, object *ship_obj, int submodel_num, vec3d *submodel_pnt, vec3d *submodel_norm)
+{
+ Assert(ship_obj->type == OBJ_SHIP);
+
+ outnorm = submodel_norm;
+ vm_vec_zero(outpnt);
+ matrix submodel_instance_matrix, rotation_matrix, inv_orientation;
+
+ polymodel_instance *pmi = model_get_instance(Ships[ship_obj->instance].model_instance_num);
+ polymodel *pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].model_num);
+
+ int mn = submodel_num;
+ while ( (mn >= 0) && (pm->submodel[mn].parent >= 0) ) {
+ vec3d offset = pm->submodel[mn].offset;
+
+ if ( mn == submodel_num) {
+ vec3d submodel_pnt_offset = *submodel_pnt;
+
+ rotation_matrix = pm->submodel[submodel_num].orientation;
+ vm_rotate_matrix_by_angles(&rotation_matrix, &pmi->submodel[submodel_num].angs);
+
+ vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[submodel_num].orientation);
+
+ vm_matrix_x_matrix(&submodel_instance_matrix, &rotation_matrix, &inv_orientation);
+
+ vec3d tvec = submodel_pnt_offset;
+ vm_vec_unrotate(&submodel_pnt_offset, &tvec, &submodel_instance_matrix);
+
+ vec3d tnorm = *outnorm;
+ vm_vec_unrotate(outnorm, &tnorm, &submodel_instance_matrix);
+
+ vm_vec_add2(&offset, &submodel_pnt_offset);
+ }
+
+ int parent_model_num = pm->submodel[mn].parent;
+
+ rotation_matrix = pm->submodel[parent_model_num].orientation;
+ vm_rotate_matrix_by_angles(&rotation_matrix, &pmi->submodel[parent_model_num].angs);
+
+ vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[parent_model_num].orientation);
+
+ vm_matrix_x_matrix(&submodel_instance_matrix, &rotation_matrix, &inv_orientation);
+
+ vec3d tvec = offset;
+ vm_vec_unrotate(&offset, &tvec, &submodel_instance_matrix);
+
+ vec3d tnorm = *outnorm;
+ vm_vec_unrotate(outnorm, &tnorm, &submodel_instance_matrix);
+
+ vm_vec_add2(outpnt, &offset);
+
+ mn = pm->submodel[mn].parent;
+ }
+}
+
// Verify rotating submodel has corresponding ship subsystem -- info in which to store rotation angle
int rotating_submodel_has_ship_subsys(int submodel, ship *shipp)
{
Index: ship/shipfx.cpp
===================================================================
--- ship/shipfx.cpp (revision 7926)
+++ ship/shipfx.cpp (working copy)
@@ -38,6 +38,7 @@
#include "parse/scripting.h"
#include "asteroid/asteroid.h"
#include "bmpman/bmpman.h"
+#include "model/model.h"
@@ -2697,7 +2698,6 @@
objp = &Objects[shipp->objnum];
ship_obj *so;
- vec3d world_thruster_pos, world_thruster_norm, apex, thruster_to_ship, apex_to_ship, temp;
float dist_sqr, inset_depth, dot_to_ship, max_ship_intensity;
polymodel *pm;
@@ -2766,6 +2766,8 @@
for (idx = 0; idx < pm->n_thrusters; idx++) {
thruster_bank *bank = &pm->thrusters[idx];
+ vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
+ bool submodel_rotation = false;
// make sure this engine is functional before we try to process a wash from it
if ( !model_should_render_engine_glow(OBJ_INDEX(wash_objp), bank->obj_num) ) {
@@ -2787,13 +2789,38 @@
half_angle = ewp->angle;
radius_mult = ewp->radius_mult;
+
+ // If bank is attached to a submodel, prepare to account for rotations
+ //
+ // TODO: This won't work in the ship lab, because the lab code doesn't
+ // set the the necessary submodel instance info needed here. The second
+ // condition is thus a hack to disable the feature while in the lab, and
+ // can be removed if the lab is re-structured accordingly. -zookeeper
+ if ( bank->submodel_num > -1 && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
+ model_find_submodel_offset(&submodel_static_offset, wash_sip->model_num, bank->submodel_num);
+
+ submodel_rotation = true;
+ }
+
for (j=0; j<bank->num_points; j++) {
+ vec3d world_thruster_pos, world_thruster_norm, apex, thruster_to_ship, apex_to_ship, temp;
+ vec3d loc_pos = bank->points[j].pnt;
+ vec3d loc_norm = bank->points[j].norm;
+
+ if ( submodel_rotation ) {
+ vm_vec_sub(&loc_pos, &bank->points[j].pnt, &submodel_static_offset);
+
+ // Gets the final offset and normal in the ship's frame of reference
+ temp = loc_pos;
+ find_submodel_instance_point_normal(&loc_pos, &loc_norm, wash_objp, bank->submodel_num, &temp, &loc_norm);
+ }
+
// get world pos of thruster
- vm_vec_unrotate(&world_thruster_pos, &bank->points[j].pnt, &wash_objp->orient);
+ vm_vec_unrotate(&world_thruster_pos, &loc_pos, &wash_objp->orient);
vm_vec_add2(&world_thruster_pos, &wash_objp->pos);
// get world norm of thruster;
- vm_vec_unrotate(&world_thruster_norm, &bank->points[j].norm, &wash_objp->orient);
+ vm_vec_unrotate(&world_thruster_norm, &loc_norm, &wash_objp->orient);
// get vector from thruster to ship
vm_vec_sub(&thruster_to_ship, &objp->pos, &world_thruster_pos);
@@ -4287,4 +4314,4 @@
vm_vec_scale_add(&objp->pos, &pos_final, &objp->orient.vec.fvec, scale);
}
return 1;
-}
+}
\ No newline at end of file