From 8548e7fa8a9fcdffea8d63a73a94d3e9cff96a35 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Mon, 9 Sep 2024 02:07:19 -0400 Subject: [PATCH] electrical arc upgrade, stage 3: add support for customizing segment depth and using persistent arc points --- code/debris/debris.cpp | 2 +- code/model/model.h | 6 ++-- code/model/modelinterp.cpp | 14 ++++---- code/model/modelread.cpp | 8 +++-- code/model/modelrender.cpp | 41 +++++++++++++-------- code/model/modelrender.h | 7 ++-- code/scripting/api/objs/ship.cpp | 61 ++++++++++++++++++++++++++++---- code/ship/ship.cpp | 2 +- code/ship/ship.h | 3 ++ code/ship/shipfx.cpp | 8 +++++ 10 files changed, 114 insertions(+), 38 deletions(-) diff --git a/code/debris/debris.cpp b/code/debris/debris.cpp index 38aa6c55793..6ab475f5464 100644 --- a/code/debris/debris.cpp +++ b/code/debris/debris.cpp @@ -1178,7 +1178,7 @@ void debris_render(object * obj, model_draw_list *scene) if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f ) { for (auto &arc: db->electrical_arcs) { if ( arc.timestamp.isValid() ) { - model_instance_add_arc( pm, pmi, db->submodel_num, &arc.endpoint_1, &arc.endpoint_2, MARC_TYPE_DAMAGED ); + model_instance_add_arc( pm, pmi, db->submodel_num, &arc.endpoint_1, &arc.endpoint_2, nullptr, MARC_TYPE_DAMAGED ); } } } diff --git a/code/model/model.h b/code/model/model.h index 8d72ab83e61..fba09e5b88a 100644 --- a/code/model/model.h +++ b/code/model/model.h @@ -94,10 +94,12 @@ struct electrical_arc vec3d endpoint_1; vec3d endpoint_2; ubyte type; // see MARC_TYPE_* defines + ubyte segment_depth; // number of times to divide the arc into segments }; struct model_electrical_arc : electrical_arc { + const SCP_vector *persistent_arc_points; }; // Data specific to a particular instance of a submodel. @@ -1204,8 +1206,8 @@ extern void model_set_up_techroom_instance(ship_info *sip, int model_instance_nu void model_replicate_submodel_instance(polymodel *pm, polymodel_instance *pmi, int submodel_num, flagset& flags); // Adds an electrical arcing effect to a submodel -void model_instance_clear_arcs(polymodel *pm, polymodel_instance *pmi); -void model_instance_add_arc(polymodel *pm, polymodel_instance *pmi, int sub_model_num, vec3d *v1, vec3d *v2, int arc_type, color *primary_color_1 = nullptr, color *primary_color_2 = nullptr, color *secondary_color = nullptr, float width = 0.0f); +void model_instance_clear_arcs(const polymodel *pm, polymodel_instance *pmi); +void model_instance_add_arc(const polymodel *pm, polymodel_instance *pmi, int sub_model_num, const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, ubyte arc_type, const color *primary_color_1 = nullptr, const color *primary_color_2 = nullptr, const color *secondary_color = nullptr, float width = 0.0f, ubyte segment_depth = 4); // Gets two random points on the surface of a submodel extern vec3d submodel_get_random_point(int model_num, int submodel_num, int seed = -1); diff --git a/code/model/modelinterp.cpp b/code/model/modelinterp.cpp index 7a40fbfeff7..183ad84807a 100644 --- a/code/model/modelinterp.cpp +++ b/code/model/modelinterp.cpp @@ -785,15 +785,13 @@ void model_draw_bay_paths_htl(int model_num) gr_set_cull(cull); } -SCP_vector Arc_segment_points; - -void interp_render_arc_segment(const vec3d *v1, const vec3d *v2, int depth ) +void interp_generate_arc_segment(SCP_vector &arc_segment_points, const vec3d *v1, const vec3d *v2, ubyte depth_limit, ubyte depth) { float d = vm_vec_dist_quick( v1, v2 ); - const float scaler = 0.30f; + constexpr float scaler = 0.30f; - if ( (d < scaler) || (depth > 4) ) { - Arc_segment_points.push_back(*v2); + if ( (d < scaler) || (depth > depth_limit) ) { + arc_segment_points.push_back(*v2); } else { // divide in half vec3d tmp; @@ -804,8 +802,8 @@ void interp_render_arc_segment(const vec3d *v1, const vec3d *v2, int depth ) tmp.xyz.z += (frand() - 0.5f) * d * scaler; // add additional point - interp_render_arc_segment( v1, &tmp, depth+1 ); - interp_render_arc_segment( &tmp, v2, depth+1 ); + interp_generate_arc_segment( arc_segment_points, v1, &tmp, depth_limit, depth+1 ); + interp_generate_arc_segment( arc_segment_points, &tmp, v2, depth_limit, depth+1 ); } } diff --git a/code/model/modelread.cpp b/code/model/modelread.cpp index 3bb1fe61bd3..ac4e8c2f5e5 100644 --- a/code/model/modelread.cpp +++ b/code/model/modelread.cpp @@ -5154,7 +5154,7 @@ void model_do_intrinsic_motions(object *objp) } } -void model_instance_clear_arcs(polymodel *pm, polymodel_instance *pmi) +void model_instance_clear_arcs(const polymodel *pm, polymodel_instance *pmi) { Assert(pm->id == pmi->model_num); @@ -5164,7 +5164,7 @@ void model_instance_clear_arcs(polymodel *pm, polymodel_instance *pmi) } // Adds an electrical arcing effect to a submodel -void model_instance_add_arc(polymodel *pm, polymodel_instance *pmi, int sub_model_num, vec3d *v1, vec3d *v2, int arc_type, color *primary_color_1, color *primary_color_2, color *secondary_color, float width ) +void model_instance_add_arc(const polymodel *pm, polymodel_instance *pmi, int sub_model_num, const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, ubyte arc_type, const color *primary_color_1, const color *primary_color_2, const color *secondary_color, float width, ubyte segment_depth) { Assert(pm->id == pmi->model_num); @@ -5182,9 +5182,11 @@ void model_instance_add_arc(polymodel *pm, polymodel_instance *pmi, int sub_mode smi->electrical_arcs.emplace_back(); auto &new_arc = smi->electrical_arcs.back(); - new_arc.type = static_cast(arc_type); + new_arc.type = arc_type; new_arc.endpoint_1 = *v1; new_arc.endpoint_2 = *v2; + new_arc.persistent_arc_points = persistent_arc_points; + new_arc.segment_depth = segment_depth; if (arc_type == MARC_TYPE_SHIP || arc_type == MARC_TYPE_SCRIPTED) { new_arc.primary_color_1 = *primary_color_1; diff --git a/code/model/modelrender.cpp b/code/model/modelrender.cpp index 601eb1cb0ac..598d640b1d0 100644 --- a/code/model/modelrender.cpp +++ b/code/model/modelrender.cpp @@ -40,12 +40,10 @@ extern int Model_polys; extern int tiling; extern float model_radius; -extern SCP_vector Arc_segment_points; - extern bool Scene_framebuffer_in_frame; color Wireframe_color; -extern void interp_render_arc_segment(const vec3d *v1, const vec3d *v2, int depth); +extern void interp_generate_arc_segment(SCP_vector &arc_segment_points, const vec3d *v1, const vec3d *v2, ubyte depth_limit, ubyte depth); int model_render_determine_elapsed_time(int objnum, uint64_t flags); @@ -444,7 +442,7 @@ void model_draw_list::add_submodel_to_batch(int model_num) TransformBufferHandler.set_model_transform(transform, model_num); } -void model_draw_list::add_arc(const vec3d *v1, const vec3d *v2, const color *primary, const color *secondary, float arc_width) +void model_draw_list::add_arc(const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, const color *primary, const color *secondary, float arc_width, ubyte segment_depth) { arc_effect new_arc; @@ -454,6 +452,8 @@ void model_draw_list::add_arc(const vec3d *v1, const vec3d *v2, const color *pri new_arc.primary = *primary; new_arc.secondary = *secondary; new_arc.width = arc_width; + new_arc.segment_depth = segment_depth; + new_arc.persistent_arc_points = persistent_arc_points; Arcs.push_back(new_arc); } @@ -623,7 +623,7 @@ void model_draw_list::render_arc(const arc_effect &arc) { g3_start_instance_matrix(&arc.transform); - model_render_arc(&arc.v1, &arc.v2, &arc.primary, &arc.secondary, arc.width); + model_render_arc(&arc.v1, &arc.v2, arc.persistent_arc_points, &arc.primary, &arc.secondary, arc.width, arc.segment_depth); g3_done_instance(true); } @@ -887,7 +887,7 @@ void model_render_add_lightning(model_draw_list *scene, const model_render_param // render the actual arc segment if (width > 0.0f) - scene->add_arc(&arc.endpoint_1, &arc.endpoint_2, &primary, &secondary, width); + scene->add_arc(&arc.endpoint_1, &arc.endpoint_2, arc.persistent_arc_points, &primary, &secondary, width, arc.segment_depth); } } @@ -2507,23 +2507,36 @@ void model_render_insignias(const insignia_draw_data *insignia_data) } } -void model_render_arc(const vec3d *v1, const vec3d *v2, const color *primary, const color *secondary, float arc_width) +SCP_vector Arc_segment_points; + +void model_render_arc(const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, const color *primary, const color *secondary, float arc_width, ubyte depth_limit) { - Arc_segment_points.clear(); + int size; + const vec3d *pvecs; + + if (persistent_arc_points) { + size = static_cast(persistent_arc_points->size()); + pvecs = persistent_arc_points->data(); + } else { + Arc_segment_points.clear(); + + // need to add the first point + Arc_segment_points.push_back(*v1); - // need to add the first point - Arc_segment_points.push_back(*v1); + // this should fill in all of the middle, and the last, points + interp_generate_arc_segment(Arc_segment_points, v1, v2, depth_limit, 0); - // this should fill in all of the middle, and the last, points - interp_render_arc_segment(v1, v2, 0); + size = static_cast(Arc_segment_points.size()); + pvecs = Arc_segment_points.data(); + } // use primary color for fist pass Assert( primary ); - g3_render_rod(primary, static_cast(Arc_segment_points.size()), Arc_segment_points.data(), arc_width); + g3_render_rod(primary, size, pvecs, arc_width); if (secondary) { - g3_render_rod(secondary, static_cast(Arc_segment_points.size()), Arc_segment_points.data(), arc_width * 0.33f); + g3_render_rod(secondary, size, pvecs, arc_width * 0.33f); } } diff --git a/code/model/modelrender.h b/code/model/modelrender.h index 4d9d55842a2..cbd99397c61 100644 --- a/code/model/modelrender.h +++ b/code/model/modelrender.h @@ -164,6 +164,9 @@ struct arc_effect color primary; color secondary; float width; + ubyte segment_depth; + + const SCP_vector *persistent_arc_points; }; struct insignia_draw_data @@ -281,7 +284,7 @@ class model_draw_list void pop_transform(); void set_scale(const vec3d *scale = NULL); - void add_arc(const vec3d *v1, const vec3d *v2, const color *primary, const color *secondary, float arc_width); + void add_arc(const vec3d *v1, const vec3d *v2, const SCP_vector *persistent_arc_points, const color *primary, const color *secondary, float arc_width, ubyte segment_depth); void render_arcs(); void add_insignia(const model_render_params *params, const polymodel *pm, int detail_level, int bitmap_num); @@ -308,7 +311,7 @@ void submodel_render_immediate(const model_render_params* render_info, const pol void submodel_render_queue(const model_render_params* render_info, model_draw_list* scene, const polymodel* pm, const polymodel_instance* pmi, int submodel_num, const matrix* orient, const vec3d* pos); void model_render_buffers(model_draw_list* scene, model_material* rendering_material, const model_render_params* interp, const vertex_buffer* buffer, const polymodel* pm, int mn, int detail_level, uint tmap_flags); bool model_render_check_detail_box(const vec3d* view_pos, const polymodel* pm, int submodel_num, uint64_t flags); -void model_render_arc(const vec3d* v1, const vec3d* v2, const color* primary, const color* secondary, float arc_width); +void model_render_arc(const vec3d* v1, const vec3d* v2, const SCP_vector *persistent_arc_points, const color* primary, const color* secondary, float arc_width, ubyte depth_limit); void model_render_insignias(const insignia_draw_data* insignia); void model_render_set_wireframe_color(const color* clr); bool render_tech_model(tech_render_type model_type, int x1, int y1, int x2, int y2, float zoom, bool lighting, int class_idx, const matrix* orient, const SCP_string& pof_filename = "", float closeup_zoom = 0, const vec3d* closeup_pos = &vmd_zero_vector); diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 218ef99eb78..075d8375678 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -39,6 +39,7 @@ extern void ship_reset_disabled_physics(object *objp, int ship_class); extern bool sexp_check_flag_arrays(const char *flag_name, Object::Object_Flags &object_flag, Ship::Ship_Flags &ship_flags, Mission::Parse_Object_Flags &parse_obj_flag, AI::AI_Flags &ai_flag); extern void sexp_alter_ship_flag_helper(object_ship_wing_point_team &oswpt, bool future_ships, Object::Object_Flags object_flag, Ship::Ship_Flags ship_flag, Mission::Parse_Object_Flags parse_obj_flag, AI::AI_Flags ai_flag, bool set_flag); +extern void interp_generate_arc_segment(SCP_vector &arc_segment_points, const vec3d *v1, const vec3d *v2, ubyte depth_limit, ubyte depth); namespace scripting { namespace api { @@ -2675,8 +2676,9 @@ ADE_FUNC(jettison, l_Ship, "number jettison_speed, [ship... dockee_ships /* All return jettison_helper(L, docker_objh, jettison_speed, 2); } -ADE_FUNC(AddElectricArc, l_Ship, "vector firstPoint, vector secondPoint, number duration, number width", - "Creates an electric arc on the ship between two points in the ship's reference frame, for the specified duration in seconds, and the specified width in meters.", +ADE_FUNC(AddElectricArc, l_Ship, "vector firstPoint, vector secondPoint, number duration, number width, [number segment_depth, boolean persistent_points]", + "Creates an electric arc on the ship between two points in the ship's reference frame, for the specified duration in seconds, and the specified width in meters. Optionally, " + "specify the segment depth (the number of times the spark is divided) and whether to generate a set of arc points that will persist from frame to frame.", "number", "The arc index if successful, 0 otherwise") { @@ -2685,8 +2687,10 @@ ADE_FUNC(AddElectricArc, l_Ship, "vector firstPoint, vector secondPoint, number vec3d* v2; float duration = 0.0f; float width = 0.0f; + int segment_depth = 4; + bool persistent_points = false; - if (!ade_get_args(L, "oooff", l_Ship.GetPtr(&objh), l_Vector.GetPtr(&v1), l_Vector.GetPtr(&v2), &duration, &width)) + if (!ade_get_args(L, "oooff|ib", l_Ship.GetPtr(&objh), l_Vector.GetPtr(&v1), l_Vector.GetPtr(&v2), &duration, &width, &segment_depth, &persistent_points)) return ade_set_error(L, "i", 0); if (!objh->isValid()) @@ -2711,6 +2715,19 @@ ADE_FUNC(AddElectricArc, l_Ship, "vector firstPoint, vector secondPoint, number arc->type = MARC_TYPE_SCRIPTED; arc->width = width; + arc->segment_depth = static_cast(segment_depth); + + // we might want to generate the arc points ahead of time, rather than every frame + if (persistent_points) + { + arc->persistent_arc_points.reset(new SCP_vector()); + + // need to add the first point + arc->persistent_arc_points->push_back(*v1); + + // this should fill in all of the middle, and the last, points + interp_generate_arc_segment(*arc->persistent_arc_points, v1, v2, static_cast(segment_depth), 1); // start at depth 1 for the benefit of Lua + } return ade_set_args(L, "i", static_cast(arc - shipp->electrical_arcs.data()) + 1); // FS2 -> Lua } @@ -2743,8 +2760,9 @@ ADE_FUNC(DeleteElectricArc, l_Ship, "number index", return ADE_RETURN_NIL; } -ADE_FUNC(ModifyElectricArc, l_Ship, "number index, vector firstPoint, vector secondPoint, [number width]", - "Sets the endpoints (in the ship's reference frame) and width of the specified electric arc on the ship, .", +ADE_FUNC(ModifyElectricArc, l_Ship, "number index, vector firstPoint, vector secondPoint, [number width, number segment_depth, boolean persistent_points]", + "Sets the endpoints (in the ship's reference frame), width, and segment depth of the specified electric arc on the ship, plus whether the arc has persistent points. " + "If this arc already had a collection of persistent points and it still does after this function is called, the points will be regenerated.", nullptr, nullptr) { @@ -2753,8 +2771,10 @@ ADE_FUNC(ModifyElectricArc, l_Ship, "number index, vector firstPoint, vector sec vec3d* v1; vec3d* v2; float width = 0.0f; + int segment_depth = 4; + bool persistent_points = false; - int args = ade_get_args(L, "oioo|f", l_Ship.GetPtr(&objh), &index, l_Vector.GetPtr(&v1), l_Vector.GetPtr(&v2), &width); + int args = ade_get_args(L, "oioo|fib", l_Ship.GetPtr(&objh), &index, l_Vector.GetPtr(&v1), l_Vector.GetPtr(&v2), &width, &segment_depth, &persistent_points); if (args < 4) return ADE_RETURN_NIL; @@ -2770,8 +2790,35 @@ ADE_FUNC(ModifyElectricArc, l_Ship, "number index, vector firstPoint, vector sec arc.endpoint_1 = *v1; arc.endpoint_2 = *v2; - if (args == 5) + if (args >= 5) arc.width = width; + if (args >= 6) + arc.segment_depth = static_cast(segment_depth); + if (args >= 7) + { + if (persistent_points) + { + if (!arc.persistent_arc_points) + arc.persistent_arc_points.reset(new SCP_vector()); + } + else + { + if (arc.persistent_arc_points) + arc.persistent_arc_points.reset(); + } + } + + // persistent points need to be regenerated when the arc is moved; they also need to be generated if we are adding them for the first time + if (arc.persistent_arc_points) + { + arc.persistent_arc_points->clear(); + + // need to add the first point + arc.persistent_arc_points->push_back(*v1); + + // this should fill in all of the middle, and the last, points + interp_generate_arc_segment(*arc.persistent_arc_points, v1, v2, static_cast(segment_depth), 1); // start at depth 1 for the benefit of Lua + } } return ADE_RETURN_NIL; diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 698e8e81587..b4f968cf719 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -20958,7 +20958,7 @@ void ship_render(object* obj, model_draw_list* scene) if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f && !Rendering_to_shadow_map ) { for (auto &arc: shipp->electrical_arcs) { if (arc.timestamp.isValid()) { - model_instance_add_arc(pm, pmi, -1, &arc.endpoint_1, &arc.endpoint_2, arc.type, &arc.primary_color_1, &arc.primary_color_2, &arc.secondary_color, arc.width); + model_instance_add_arc(pm, pmi, -1, &arc.endpoint_1, &arc.endpoint_2, arc.persistent_arc_points.get(), arc.type, &arc.primary_color_1, &arc.primary_color_2, &arc.secondary_color, arc.width, arc.segment_depth); } } } diff --git a/code/ship/ship.h b/code/ship/ship.h index 5ce7a343df0..d1a4c02384f 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -539,6 +539,9 @@ struct reload_pct struct ship_electrical_arc : electrical_arc { TIMESTAMP timestamp; // When this times out, the spark goes away. Invalid is not used + + // if this vector exists, these points will be used instead of the ones generated on each frame by interp_generate_arc_segment() + std::unique_ptr> persistent_arc_points; }; // NOTE: Can't be treated as a struct anymore, since it has STL data structures in its object tree! diff --git a/code/ship/shipfx.cpp b/code/ship/shipfx.cpp index ec7e020466f..4528d733624 100644 --- a/code/ship/shipfx.cpp +++ b/code/ship/shipfx.cpp @@ -2205,6 +2205,10 @@ void shipfx_do_lightning_arcs_frame( ship *shipp ) } } + arc->segment_depth = 4; // previously hard-coded in interp_generate_arc_segment() + + arc->persistent_arc_points.reset(); // by default, no persistent points + shipp->passive_arc_next_times[passive_arc_info_idx] = _timestamp(fl2i(arc_info->frequency * MILLISECONDS_PER_SECOND)); } } @@ -2354,6 +2358,10 @@ void shipfx_do_lightning_arcs_frame( ship *shipp ) arc->type = MARC_TYPE_DAMAGED; } + arc->segment_depth = 4; // previously hard-coded in interp_generate_arc_segment() + + arc->persistent_arc_points.reset(); // by default, no persistent points + num_damage_arcs++; } }