Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions code/ai/aicode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2792,7 +2792,7 @@ void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int cou
auto pm = model_get(pmi->model_num);

// Goober5000 - check for moving submodels
if ((mp->parent_submodel >= 0) && (pm->submodel[mp->parent_submodel].rotation_type >= 0))
if ((mp->parent_submodel >= 0) && ((pm->submodel[mp->parent_submodel].rotation_type >= 0) || (pm->submodel[mp->parent_submodel].translation_type >= 0)))
{
moving_submodel = true;

Expand Down Expand Up @@ -9444,7 +9444,7 @@ int find_parent_moving_submodel(polymodel *pm, int dock_index)
submodel = pm->paths[path_num].parent_submodel;

// submodel must exist and must move
if ((submodel >= 0) && (submodel < pm->n_models) && (pm->submodel[submodel].rotation_type >= 0))
if ((submodel >= 0) && (submodel < pm->n_models) && ((pm->submodel[submodel].rotation_type >= 0) || (pm->submodel[submodel].translation_type >= 0)))
{
return submodel;
}
Expand Down
6 changes: 3 additions & 3 deletions code/lab/dialogs/render_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ void set_show_damage_lightning_flag(Checkbox* caller) {
getLabManager()->Renderer->setRenderFlag(LabRenderFlag::ShowDamageLightning, !value);
}

void set_rotate_subsys_flag(Checkbox* caller) {
void set_move_subsys_flag(Checkbox* caller) {
auto value = caller->GetChecked();

getLabManager()->Renderer->setRenderFlag(LabRenderFlag::RotateSubsystems, !value);
getLabManager()->Renderer->setRenderFlag(LabRenderFlag::MoveSubsystems, !value);
}

void set_post_proc_flag(Checkbox* caller) {
Expand Down Expand Up @@ -232,7 +232,7 @@ void RenderOptions::open(Button* /*caller*/) {
cbp = (Checkbox*)dialogWindow->AddChild(new Checkbox("Show Damage lightning", 2, y, set_show_damage_lightning_flag));
y += cbp->GetHeight() + 2;

cbp = (Checkbox*)dialogWindow->AddChild(new Checkbox("Rotate Subsystems", 2, y, set_rotate_subsys_flag));
cbp = (Checkbox*)dialogWindow->AddChild(new Checkbox("Rotate/Translate Subsystems", 2, y, set_move_subsys_flag));
y += cbp->GetHeight() + 2;

cbp = (Checkbox*)dialogWindow->AddChild(new Checkbox("Hide Post Processing", 2, y, set_post_proc_flag));
Expand Down
2 changes: 1 addition & 1 deletion code/lab/renderer/lab_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void LabRenderer::renderModel(float frametime) {
PostProcessing_override = renderFlags[LabRenderFlag::HidePostProcessing];

if (obj->type == OBJ_SHIP) {
Ships[obj->instance].flags.set(Ship::Ship_Flags::Subsystem_movement_locked, !renderFlags[LabRenderFlag::RotateSubsystems]);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Subsystem_movement_locked, !renderFlags[LabRenderFlag::MoveSubsystems]);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_light,
Expand Down
2 changes: 1 addition & 1 deletion code/lab/renderer/lab_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FLAG_LIST(LabRenderFlag) {
ModelRotationEnabled,
ShowInsignia,
ShowDamageLightning,
RotateSubsystems,
MoveSubsystems,
HidePostProcessing,
NoDiffuseMap,
NoGlowMap,
Expand Down
40 changes: 39 additions & 1 deletion code/model/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,24 @@ struct submodel_instance
float turn_accel = 0.0f;
TIMESTAMP stepped_rotation_started;

float cur_offset = 0.0f;
float prev_offset = 0.0f;

float current_shift_rate = 0.0f;
float desired_shift_rate = 0.0f;
float shift_accel = 0.0f;
TIMESTAMP stepped_translation_started;

bool blown_off = false; // If set, this subobject is blown off
bool collision_checked = false;

// These fields are the true standard reference for submodel rotation. They should seldom be read directly
// and should almost never be written directly. In most cases, coders should prefer cur_angle and prev_angle.
matrix canonical_orient = vmd_identity_matrix;
matrix canonical_prev_orient = vmd_identity_matrix;
// similarly for translation
vec3d canonical_offset = vmd_zero_vector;
vec3d canonical_prev_offset = vmd_zero_vector;

// --- these fields used to be in bsp_info ---

Expand All @@ -108,6 +119,7 @@ struct submodel_instance

//SMI-Specific movement axis. Only valid in MOVEMENT_TYPE_TRIGGERED.
vec3d rotation_axis;
vec3d translation_axis;

submodel_instance()
{
Expand Down Expand Up @@ -139,6 +151,17 @@ typedef struct stepped_rotation {
bool backwards; // if rate is negative
} stepped_rotation_t;

typedef struct stepped_translation {
bool reverse_after_step; // for back-and-forth motion
float step_distance; // linear displacement of one step
float fraction; // fraction of time in step spent in accel
float t_transit; // time spent moving from one step to next
float t_pause; // time at rest between steps
float max_shift_rate; // max shift rate going between steps
float max_shift_accel; // max accel going between steps
bool backwards; // if rate is negative
} stepped_translation_t;

struct queued_animation;

// definition for model subsystems.
Expand Down Expand Up @@ -185,6 +208,7 @@ class model_subsystem { /* contains rotation rate info */
// movement specific info
int weapon_rotation_pbank; // weapon-controlled rotation - Goober5000
std::shared_ptr<stepped_rotation_t> stepped_rotation; // turn rotation struct
std::shared_ptr<stepped_translation_t> stepped_translation; // shift translation struct

// AWACS specific information
float awacs_intensity; // awacs intensity of this subsystem
Expand Down Expand Up @@ -350,9 +374,16 @@ class bsp_info
vec3d rotation_axis = vmd_zero_vector; // which axis this subobject rotates on.
int rotation_axis_id = MOVEMENT_AXIS_NONE; // for optimization

int translation_type = MOVEMENT_TYPE_NONE;
vec3d translation_axis = vmd_zero_vector;
int translation_axis_id = MOVEMENT_AXIS_NONE;

float default_turn_rate = 0.0f;
float default_turn_accel = 0.0f;

float default_shift_rate = 0.0f;
float default_shift_accel = 0.0f;

flagset<Model::Submodel_flags> flags;

int subsys_num = -1; // subsystem number; index to ship_info->subsystems; the counterpart of model_subsystem->subobj_num
Expand Down Expand Up @@ -1001,14 +1032,21 @@ extern int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi

// Rotates the angle of a submodel. Use this so the right unlocked axis
// gets stuffed.
extern void submodel_canonicalize(bsp_info *sm, submodel_instance *smi, bool clamp);
extern void submodel_canonicalize_rotation(bsp_info *sm, submodel_instance *smi, bool clamp);
extern void submodel_rotate(model_subsystem *psub, submodel_instance *smi);
extern void submodel_rotate(bsp_info *sm, submodel_instance *smi);

// Rotates the angle of a submodel. Use this so the right unlocked axis
// gets stuffed. Does this for stepped rotations
void submodel_stepped_rotate(model_subsystem *psub, submodel_instance *smi);

// Similar to above
extern void submodel_canonicalize_translation(bsp_info *sm, submodel_instance *smi);
extern void submodel_translate(model_subsystem *psub, submodel_instance *smi);
extern void submodel_translate(bsp_info *sm, submodel_instance *smi);

void submodel_stepped_translate(model_subsystem *psub, submodel_instance *smi);

// ------- submodel transformations -------

// Goober5000
Expand Down
3 changes: 3 additions & 0 deletions code/model/model_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Model {
Do_not_scale_detail_distances, // if set should not scale boxes or spheres based on 'model detail' settings
Gun_rotation, // for animated weapon models
Instant_rotate_accel, // rotating submodels instantly reach their desired velocity
Instant_translate_accel, // ditto for translating submodels
No_collisions, // for $no_collisions property - kazan
Nocollide_this_only, //SUSHI: Like no_collisions, but not recursive. For the "replacement" collision model scheme.
Collide_invisible, //SUSHI: If set, this submodel should allow collisions for invisible textures. For the "replacement" collision model scheme.
Expand All @@ -25,7 +26,9 @@ namespace Model {

FLAG_LIST(Subsystem_Flags) {
Rotates, // This means the object rotates automatically
Translates, // This means the object translates automatically
Stepped_rotate, // This means that the rotation occurs in steps
Stepped_translate, // Ditto for translation
Ai_rotate, // This means that the rotation is controlled by ai
Crewpoint, // If set, this is a crew point.
Awacs, // If set, this subsystem has AWACS capability
Expand Down
46 changes: 39 additions & 7 deletions code/model/modelanimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ namespace animation {
if (!submodel)
return;

// rotation -----

submodel->canonical_prev_orient = submodel->canonical_orient;
submodel->canonical_orient = data.orientation;

Expand All @@ -333,8 +335,19 @@ namespace animation {
vm_matrix_to_rot_axis_and_angle(&delta, &deltaAngle, &submodel->rotation_axis);
submodel->current_turn_rate = deltaAngle / flFrametime;

//TODO: Once translation is a thing
//m_subsys->submodel_instance_1->offset = data.position;
// translation -----

submodel->canonical_prev_offset = submodel->canonical_offset;
submodel->canonical_offset = data.position;

vec3d delta_vec;
vm_vec_sub(&delta_vec, &submodel->canonical_offset, &submodel->canonical_prev_offset);

// figure out the sign of the displacement based on whether it is along or against the axis
int sign = (vm_vec_dot(&delta_vec, &submodel->translation_axis) < 0.0f) ? -1 : 1;

float deltaMag = sign * vm_vec_mag(&delta_vec);
submodel->current_shift_rate = deltaMag / flFrametime;
}

void ModelAnimationSubmodel::resetPhysicsData(polymodel_instance* pmi) {
Expand All @@ -361,15 +374,19 @@ namespace animation {
if (submodel.second->rotation_type == MOVEMENT_TYPE_NONE) {
submodel.second->rotation_type = MOVEMENT_TYPE_TRIGGERED;
}
if (submodel.second->translation_type == MOVEMENT_TYPE_NONE) {
submodel.second->translation_type = MOVEMENT_TYPE_TRIGGERED;
}
}

data.orientation = submodel.first->canonical_orient;
//TODO: Once translation is a thing
//data.position = m_subsys->submodel_instance_1->offset;
data.position = submodel.first->canonical_offset;

//In this case, we just initial-type initialized the submodel. Properly set its last frame data as well
if(isInitialType)
if (isInitialType) {
submodel.first->canonical_prev_orient = submodel.first->canonical_orient;
submodel.first->canonical_prev_offset = submodel.first->canonical_offset;
}

return true;
}
Expand Down Expand Up @@ -481,15 +498,30 @@ namespace animation {
if (!submodel)
return;

// rotation -----

submodel->canonical_prev_orient = submodel->canonical_orient;
submodel->canonical_orient = data.orientation;

submodel->rotation_axis = sm->rotation_axis;

float angle = 0.0f;
vm_closest_angle_to_matrix(&submodel->canonical_orient, &sm->rotation_axis, &angle);
submodel->rotation_axis = sm->rotation_axis;

submodel->cur_angle = angle;
submodel->turret_idle_angle = angle;

// translation -----

submodel->canonical_prev_offset = submodel->canonical_offset;
submodel->canonical_offset = data.position;

submodel->translation_axis = sm->translation_axis;

// figure out the sign of the displacement based on whether it is along or against the axis
int sign = (vm_vec_dot(&submodel->canonical_offset, &submodel->translation_axis) < 0.0f) ? -1 : 1;

submodel->cur_offset = sign * vm_vec_mag(&submodel->canonical_offset);
}


Expand Down Expand Up @@ -1504,7 +1536,7 @@ namespace animation {
{"$Set Angle:", ModelAnimationSegmentSetAngle::parser},
{"$Rotation:", ModelAnimationSegmentRotation::parser},
{"$Axis Rotation:", ModelAnimationSegmentAxisRotation::parser},
// {"$Translation:", ModelAnimationSegmentTranslation::parser},
{"$Translation:", ModelAnimationSegmentTranslation::parser},
{"$Sound During:", ModelAnimationSegmentSoundDuring::parser},
{"$Inverse Kinematics:", ModelAnimationSegmentIK::parser}
};
Expand Down
24 changes: 12 additions & 12 deletions code/model/modelcollide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1060,25 +1060,25 @@ void mc_check_subobj( int mn )
// Check all of this subobject's children
i = sm->first_child;
while ( i >= 0 ) {
matrix instance_orient;
bool blown_off;
bool collision_checked;
bsp_info * csm = &Mc_pm->submodel[i];
auto csm = &Mc_pm->submodel[i];
matrix instance_orient = vmd_identity_matrix;
vec3d instance_offset = csm->offset;
bool blown_off = false;
bool collision_checked = false;

if ( Mc_pmi ) {
instance_orient = Mc_pmi->submodel[i].canonical_orient;
blown_off = Mc_pmi->submodel[i].blown_off;
collision_checked = Mc_pmi->submodel[i].collision_checked;
} else {
instance_orient = vmd_identity_matrix;
blown_off = false;
collision_checked = false;
auto csmi = &Mc_pmi->submodel[i];
instance_orient = csmi->canonical_orient;
vm_vec_add2(&instance_offset, &csmi->canonical_offset);

blown_off = csmi->blown_off;
collision_checked = csmi->collision_checked;
}

// Don't check it or its children if it is destroyed
// or if it's set to no collision
if ( !blown_off && !collision_checked && !csm->flags[Model::Submodel_flags::No_collisions] ) {
vm_vec_unrotate(&Mc_base, &csm->offset, &saved_orient);
vm_vec_unrotate(&Mc_base, &instance_offset, &saved_orient);
vm_vec_add2(&Mc_base, &saved_base);

vm_matrix_x_matrix(&Mc_orient, &saved_orient, &instance_orient);
Expand Down
Loading