From 74dd4671a63f42ebfb6e82088d5fd0d63f024651 Mon Sep 17 00:00:00 2001 From: justinfilip Date: Thu, 22 Oct 2026 04:34:45 -0500 Subject: [PATCH 1/3] Draw the plasma pistol's charge with the picture, and keep the meter's values. --- port/linux/game/view_fov.c | 6 +- source/effects/decals.c | 21 +- source/interface/first_person_weapons.c | 24 +- source/objects/objects.c | 343 ++++++++++++------ source/objects/objects.h | 5 + source/objects/widgets/glow.c | 32 +- source/rasterizer/rasterizer_lights.c | 82 ++++- .../rasterizer/xbox/rasterizer_xbox_models.c | 29 +- .../rasterizer_xbox_transparent_geometry.c | 3 + .../rasterizer/xbox/rasterizer_xbox_widgets.c | 11 + source/render/render.c | 2 + source/render/render_objects.c | 16 +- 12 files changed, 441 insertions(+), 133 deletions(-) diff --git a/port/linux/game/view_fov.c b/port/linux/game/view_fov.c index 2c0241de..a2e530f2 100644 --- a/port/linux/game/view_fov.c +++ b/port/linux/game/view_fov.c @@ -231,7 +231,11 @@ void viewmodel_projection_begin(void) render_camera_build_frustum(&global_window_parameters.camera, &rasterizer_bounds, &global_window_parameters.frustum, TRUE); projection_applied = TRUE; - rasterizer_set_frustum_z(0.0f, 0.0f); + /* the gun sits closer than the world's near plane. A glow on it was + cut down to a sliver until this plane matches the weapon's. */ + rasterizer_set_frustum_z( + rasterizer_globals.first_person_weapon_near_clip_distance, + rasterizer_globals.first_person_weapon_far_clip_distance); } void viewmodel_projection_end(void) diff --git a/source/effects/decals.c b/source/effects/decals.c index 18641671..87b8e4b5 100644 --- a/source/effects/decals.c +++ b/source/effects/decals.c @@ -823,10 +823,23 @@ pixel32 real_a_rgb_color_to_pixel32( real scale = 255.0f; pixel32 result; - match_assert( - "..\\bitmaps\\bitmaps_inlines.h", - 243, - alpha>=0.0f && alpha<=1.0f); + /* port: a meter was reading function values after their frame was gone, + and the halt stopped the match. Keep the color in range. */ + if (!(alpha >= 0.0f && alpha <= 1.0f)) + { + static boolean reported = FALSE; + + if (!reported) + { + reported = TRUE; + error( + _error_silent, + "### alpha %f from %p", + alpha, + __builtin_return_address(0)); + } + alpha = PIN(alpha, 0.0f, 1.0f); + } match_vassert( "..\\bitmaps\\bitmaps_inlines.h", 244, diff --git a/source/interface/first_person_weapons.c b/source/interface/first_person_weapons.c index 0b735111..2270d3df 100644 --- a/source/interface/first_person_weapons.c +++ b/source/interface/first_person_weapons.c @@ -117,6 +117,7 @@ symbols in this file: #include "models/models.h" #include "networking/network_connection.h" #include "objects/objects.h" +#include "objects/widgets/widgets.h" #include "rasterizer/rasterizer_model_types.h" #include "render/render.h" #include "saved games/game_state.h" @@ -509,7 +510,13 @@ void first_person_weapon_draw( struct animation_graph *animation_graph= animation_graph_definition_get( weapon_definition->weapon.interface_definition.first_person_animations.index); struct render_lighting *lighting= object_get_cached_render_lighting(unit_index, FLT_MAX); + real weapon_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + real unit_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + /* port: the tick's functions, moved into this frame, so the + charge clicks with the picture instead of once a tick */ + object_function_values_for_render(first_person_weapon->weapon_index, weapon_functions); + object_function_values_for_render(unit_index, unit_functions); model_effect.modifier_shader= NULL; if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit) || unit->unit.active_camouflage>0.0f) @@ -540,7 +547,7 @@ void first_person_weapon_draw( model_node_matrices, NULL, weapon->object.outgoing_change_colors, - weapon->object.outgoing_function_values, + weapon_functions, lighting, &render.camera.position, 0.0f, @@ -550,6 +557,17 @@ void first_person_weapon_draw( FLAG(_render_model_first_person_bit)); } + /* port: the charge is a widget on the weapon. The body is + not drawn in first person, so neither was the charge, while + another player still saw it. */ + { + struct render_animation animation; + + animation.colors= weapon->object.outgoing_change_colors; + animation.values= weapon_functions; + widgets_render(first_person_weapon->weapon_index, lighting, &animation); + } + /* port: the shield's flare on the arms as on the body, the unit's own modifier shader (display.viewmodel_shield, view_fov.c), in the color chosen for it as on the body @@ -569,7 +587,7 @@ void first_person_weapon_draw( model_effect.modifier_animation.colors= shield_color_colors(unit_index, unit->object.outgoing_change_colors); model_effect.modifier_animation.values= overshield_effect_values(unit_index, - unit->object.outgoing_function_values, TRUE); + unit_functions, TRUE); } } } @@ -589,7 +607,7 @@ void first_person_weapon_draw( model_node_matrices, NULL, unit->object.outgoing_change_colors, - unit->object.outgoing_function_values, + unit_functions, lighting, &render.camera.position, 0.0f, diff --git a/source/objects/objects.c b/source/objects/objects.c index 40726f9f..526c76c0 100644 --- a/source/objects/objects.c +++ b/source/objects/objects.c @@ -96,6 +96,19 @@ index, which reads no function: 0 */ #define OBJECT_FRAME_INDEX_GET(object_index) ((object_index) + game_time_get()) +/* port: set while a frame is drawn, so a function read for the picture is +the tick's value moved into the frame */ +static int object_function_render_depth = 0; + +static real object_function_addition( + struct object_datum *object, + struct object_function_definition *function, + real huh, + real const *outgoing, + boolean *active); +static real object_function_wrap( + real value); + /* ---------- structures */ struct object_globals @@ -1549,6 +1562,72 @@ void object_permute_region( return; } +void object_functions_set_for_render( + boolean for_render) +{ + if (for_render) + object_function_render_depth++; + else if (object_function_render_depth > 0) + object_function_render_depth--; +} + +void object_function_values_for_render( + long object_index, + real values[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]) +{ + struct object_datum *object = object_get(object_index); + struct object_definition *definition = object_definition_get(object->definition_index); + real fraction = game_time_get_tick_fraction(); + short function_count = (short)MIN( + definition->object.functions.count, + NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS); + short function_index; + real huh; + + csmemcpy( + values, + object->object.outgoing_function_values, + sizeof(object->object.outgoing_function_values)); + /* a fraction of 0 is the tick itself, and 1 is the paused hold */ + if (!(fraction > 0.0f) || !(fraction < 1.0f)) + return; + + huh = (57 * DATUM_INDEX_TO_ABSOLUTE_INDEX(object_index) + game_time_get()) * 0.033333335f; + for (function_index = 0; function_index < function_count; function_index++) + { + struct object_function_definition *function = TAG_BLOCK_GET_ELEMENT( + &definition->object.functions, + function_index, + struct object_function_definition); + boolean active; + real output; + + /* port: an additive function steps by a whole tick. Move that step + across the frames. Anything else is the function at this moment. */ + if (TEST_FLAG(function->flags, _object_function_clip_to_bounds_and_normalize)) + { + output = object_function_addition( + object, + function, + huh, + object->object.outgoing_function_values, + &active); + values[function_index] = object_function_wrap( + object->object.outgoing_function_values[function_index] + fraction * output); + } + else + { + output = object_function_addition( + object, + function, + huh + fraction * 0.033333335f, + values, + &active); + values[function_index] = output; + } + } +} + boolean object_get_function_value( long object_index, short function_index, @@ -1572,7 +1651,17 @@ boolean object_get_function_value( it reads as off */ if (VALID_INDEX(function_index, NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS)) { - *value_reference = object->object.outgoing_function_values[function_index]; + if (object_function_render_depth > 0) + { + real shown[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + + object_function_values_for_render(object_index, shown); + *value_reference = shown[function_index]; + } + else + { + *value_reference = object->object.outgoing_function_values[function_index]; + } result = TEST_FLAG(object->object.functions_active_flags, function_index); } @@ -5172,6 +5261,148 @@ static void object_delete_recursive( return; } +static real object_function_input_value( + struct object_datum *object, + real const *outgoing, + short index) +{ + if ((unsigned long)(index - 1) >= + (unsigned long)(NUMBER_OF_INCOMING_OBJECT_FUNCTIONS + NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS)) + return 0.0f; + if (index >= NUMBER_OF_INCOMING_OBJECT_FUNCTIONS + 1) + return outgoing[index - NUMBER_OF_INCOMING_OBJECT_FUNCTIONS - 1]; + return object->object.incoming_function_values[index - 1]; +} + +static real object_function_wrap( + real value) +{ + value -= (real)floor((double)value); + if (!(value >= 0.0f)) + value += 1.0f; + return value; +} + +/* what one tick stores, before an additive function folds it into the value +it already held. `outgoing` is the functions already computed. */ +static real object_function_addition( + struct object_datum *object, + struct object_function_definition *function, + real huh, + real const *outgoing, + boolean *active) +{ + real value; + real output; + boolean function_is_active = TRUE; + real period = function->runtime_one_over_period; + + if (function->scale_period_by_function_index) + { + real function_value = object_function_input_value( + object, + outgoing, + function->scale_period_by_function_index); + + if (function_value > 0.f) + period = period / function_value; + } + + value = periodic_function_evaluate(function->function_type, huh * period); + + if (function->scale_function_by_function_index) + { + value *= object_function_input_value( + object, + outgoing, + function->scale_function_by_function_index); + } + + if (TEST_FLAG(function->flags, _object_function_invert_function_bit)) + value = 1.f - value; + + if (function->wobble_magnitude != 0.f) + { + real val = periodic_function_evaluate( + function->wobble_function_type, + huh * function->wobble_period); + + value += (2.f * function->wobble_magnitude * (val - 0.5f)); + } + + if (function->square_wave_threshold != 0.f) + value = value > function->square_wave_threshold ? 1.f : 0.f; + + if (function->step_count > 1) + { + real floor_value = floor(function->step_count * value); + + value = floor_value * function->runtime_reciprocal_step_count; + } + + if (function->runtime_reciprocal_sawtooth_count > 0.f) + value = fmod(value, function->runtime_reciprocal_sawtooth_count); + + if (function->add_function_index) + { + value += object_function_input_value(object, outgoing, function->add_function_index); + value = MIN(value, 1.f); + } + + if (function->scale_result_by_function_index) + { + value *= object_function_input_value( + object, + outgoing, + function->scale_result_by_function_index); + } + + output = transition_function_evaluate(function->map_result_to_transition_function, value); + + if (function->scale_by > 0.f) + output *= function->scale_by; + + if (function->bounds_mode == _object_function_scale_to_fit_bounds) + { + output *= (function->upper_bound - function->lower_bound); + output += function->lower_bound; + + if (function->lower_bound + _real_epsilon >= output) + { + function_is_active = TEST_FLAG( + function->flags, + _object_function_does_not_deactivate_below_lower_bound_bit); + } + } + else + { + if (function->lower_bound + _real_epsilon >= output) + { + output = function->lower_bound; + function_is_active = TEST_FLAG( + function->flags, + _object_function_does_not_deactivate_below_lower_bound_bit); + } + + if (output > function->upper_bound) + output = function->upper_bound; + + if (function->bounds_mode == _object_function_clip_to_bounds_and_normalize) + { + output = (output - function->lower_bound) * function->runtime_reciprocal_bounds_range; + } + } + + if (function->turn_off_with_function_index != NONE && + !TEST_FLAG(object->object.functions_active_flags, function->turn_off_with_function_index)) + { + function_is_active = FALSE; + } + + *active = function_is_active; + return output; +} + static void object_compute_function_values( long object_index) { @@ -5191,112 +5422,20 @@ static void object_compute_function_values( for (function_index = 0; function_indexobject.functions, function_index, struct object_function_definition); - boolean function_is_active = TRUE; - real period = function->runtime_one_over_period; - - if (function->scale_period_by_function_index) - { - real function_value = OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_period_by_function_index); - if (function_value>0.f) - { - period = period/function_value; - } - } - - value = periodic_function_evaluate(function->function_type, huh*period); - - if (function->scale_function_by_function_index) - { - value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_function_by_function_index); - } - - if (TEST_FLAG(function->flags, _object_function_invert_function_bit)) - { - value = 1.f-value; - } - - if (function->wobble_magnitude!=0.f) - { - real val = periodic_function_evaluate(function->wobble_function_type, huh*function->wobble_period); - value+= (2.f*function->wobble_magnitude * (val-0.5f)); - } - - if (function->square_wave_threshold!=0.f) - { - value = value>function->square_wave_threshold ? 1.f : 0.f ; - } - - if (function->step_count>1) - { - real floor_value = floor(function->step_count*value); - value = floor_value*function->runtime_reciprocal_step_count; - } - - if (function->runtime_reciprocal_sawtooth_count>0.f) - { - value = fmod(value, function->runtime_reciprocal_sawtooth_count); - } - - if (function->add_function_index) - { - value+= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->add_function_index); - value = MIN(value, 1.f); - } - - if (function->scale_result_by_function_index) - { - value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_result_by_function_index); - } - - output = transition_function_evaluate(function->map_result_to_transition_function, value); - - if (function->scale_by>0.f) - { - output*= function->scale_by; - } - - if (function->bounds_mode==_object_function_scale_to_fit_bounds) - { - output*= (function->upper_bound-function->lower_bound); - output+= function->lower_bound; - - if (function->lower_bound+_real_epsilon>=output) - { - function_is_active = TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit); - } - } - else - { - if (function->lower_bound+_real_epsilon>=output) - { - output = function->lower_bound; - function_is_active = TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit); - } - - if (output>function->upper_bound) - { - output = function->upper_bound; - } - - if (function->bounds_mode==_object_function_clip_to_bounds_and_normalize) - { - output = (output-function->lower_bound)*function->runtime_reciprocal_bounds_range; - } - } - - if (function->turn_off_with_function_index!=NONE && - !TEST_FLAG(object->object.functions_active_flags, function->turn_off_with_function_index)) - { - function_is_active = FALSE; - } + boolean function_is_active; + real output = object_function_addition( + object, + function, + huh, + object->object.outgoing_function_values, + &function_is_active); + /* bit 1 is the additive function (the bounds-mode name shares its + value). It folds this tick onto the value already held. */ if (TEST_FLAG(function->flags, _object_function_clip_to_bounds_and_normalize)) { output = fmod(output+object->object.outgoing_function_values[function_index], 1.f); diff --git a/source/objects/objects.h b/source/objects/objects.h index 96dbee3f..e8afcc75 100644 --- a/source/objects/objects.h +++ b/source/objects/objects.h @@ -430,6 +430,11 @@ void object_start_interpolation(long object_index, short frame_count); void object_offset_interpolation(long object_index, real_vector3d *offset); void object_permute_region(long object_index, char *permutation_name, short desired_region_index, boolean active_flag); boolean object_get_function_value(long object_index, short function_index, real *value_reference); +/* port: the picture is drawn between ticks. While this is set, a function +read for drawing is the tick's value moved forward by the fraction of the +tick that has passed. */ +void object_functions_set_for_render(boolean for_render); +void object_function_values_for_render(long object_index, real values[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]); short objects_in_clusters_by_indices(unsigned long class_flags, short cluster_count, short const *cluster_indices, short maximum_object_count, long *object_indices); long object_index_from_name_index(short name_index); /* port: this object takes a scenario name nobody holds, the way a placed diff --git a/source/objects/widgets/glow.c b/source/objects/widgets/glow.c index 64b13269..d8c97239 100644 --- a/source/objects/widgets/glow.c +++ b/source/objects/widgets/glow.c @@ -90,6 +90,7 @@ symbols in this file: #include "game/game.h" #include "memory/data.h" #include "objects/objects.h" +#include "interface/first_person_weapons.h" #include "render/render.h" #include "render/render_sprite.h" #include "saved games/game_state.h" @@ -549,12 +550,19 @@ void glow_render( glow = glow_get(glow_index); definition = glow_definition_get(glow->definition_index); - build_sprites_begin( - &sprite_data, - glow->number_of_particles, - definition->texture.index, - &global_shader_effect_additive, - 0); + /* port: the local weapon's glow is drawn with the viewmodel. The world's + view cuts off a charge held against the camera. */ + { + long local_index = first_person_weapon_get_local_index(object_index); + + build_sprites_begin( + &sprite_data, + glow->number_of_particles, + definition->texture.index, + &global_shader_effect_additive, + local_index != NONE && local_index == render.local_player_index ? + FLAG(_build_sprites_first_person_bit) : 0); + } for (particle = glow->head_particle; particle; particle = particle->next) { @@ -1285,11 +1293,21 @@ static void glow_update( if (!definition) return; - glow->number_of_markers = object_get_marker_by_name( + /* port: the local weapon's charge follows the viewmodel. The body's + markers are not on screen in first person. */ + glow->number_of_markers = first_person_weapon_get_marker_by_name_render( object_index, definition->attachment_marker, glow->markers, MAXIMUM_GLOW_MARKERS); + if (glow->number_of_markers <= 0) + { + glow->number_of_markers = object_get_marker_by_name( + object_index, + definition->attachment_marker, + glow->markers, + MAXIMUM_GLOW_MARKERS); + } if (!glow->initialized && glow->number_of_markers > 1) { diff --git a/source/rasterizer/rasterizer_lights.c b/source/rasterizer/rasterizer_lights.c index 61662307..e9933769 100644 --- a/source/rasterizer/rasterizer_lights.c +++ b/source/rasterizer/rasterizer_lights.c @@ -869,9 +869,14 @@ void rasterizer_lens_flares_submit_occlusion_tests( real occlusion_radius = definition->occlusion_radius; real_point3d occlusion_point; - /* port: the first-person weapon's projection (view_fov.c) */ + /* port: the first-person weapon's projection (view_fov.c). + The gun covers the marker, so a depth test there counts no + pixels and the charge is never drawn. */ if (first_person) + { + rasterizer_widget_set_zbuffer_enable(FALSE); viewmodel_projection_begin(); + } switch (definition->occlusion_offset_direction) { @@ -908,7 +913,10 @@ void rasterizer_lens_flares_submit_occlusion_tests( lens_flare_index); if (first_person) + { viewmodel_projection_end(); + rasterizer_widget_set_zbuffer_enable(TRUE); + } } } @@ -920,6 +928,53 @@ void rasterizer_lens_flares_submit_occlusion_tests( return; } +/* port: the plasma pistol's two front lights are one fixed sprite, 0.0188 +across, on each prong. The player looks at those from behind the gun, so +they stay on the view other players have. The needler's smoke light is +0.003 and the flashlight is 0.05, so this band is those two prongs. */ +static boolean lens_flare_first_person_front_light( + struct lens_flare_definition *definition) +{ + real largest = 0.0f; + short index; + + for (index = 0; index < definition->reflections.count; index++) + { + struct lens_flare_reflection *reflection = TAG_BLOCK_GET_ELEMENT( + &definition->reflections, + index, + struct lens_flare_reflection); + + if (reflection->radius_upper_bounds > reflection->radius_lower_bounds) + return FALSE; + if (reflection->radius_upper_bounds > largest) + largest = reflection->radius_upper_bounds; + } + + return largest >= 0.01f && largest < 0.02f; +} + +/* port: a charge grows with the light (the pistol's reaches 0.175). The +first-person one is drawn with no occlusion fade, so it is pulled down. */ +static boolean lens_flare_charge_grows( + struct lens_flare_definition *definition) +{ + short index; + + for (index = 0; index < definition->reflections.count; index++) + { + struct lens_flare_reflection *reflection = TAG_BLOCK_GET_ELEMENT( + &definition->reflections, + index, + struct lens_flare_reflection); + + if (reflection->radius_upper_bounds > reflection->radius_lower_bounds) + return TRUE; + } + + return FALSE; +} + void rasterizer_lens_flares_draw( void) { @@ -946,12 +1001,22 @@ void rasterizer_lens_flares_draw( boolean first_person = (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag) != 0; struct lens_flare_definition *definition = lens_flare_parameters->definition; + boolean front_light = first_person && + lens_flare_first_person_front_light(definition); - /* port: the first-person weapon's projection (view_fov.c) */ + /* port: the first-person weapon's projection (view_fov.c). + Tested against the scene, the sprite sits behind the gun, + so the charge never shows. */ if (first_person) + { + rasterizer_widget_set_zbuffer_enable(FALSE); viewmodel_projection_begin(); + } - if (lens_flare_parameters->internal__occlusion_pixels > 0 && + /* port: the test is in the gun's camera and the depth is the + world's, so aiming at a wall counted the charge as hidden */ + if ((first_person || lens_flare_parameters->internal__occlusion_pixels > 0) && + !front_light && LENS_FLARE_LIGHT_COLOR_ALPHA(lens_flare_parameters->compressed_light_color) > 0 && definition->reflections.count > 0) { @@ -979,7 +1044,7 @@ void rasterizer_lens_flares_draw( subtract_vectors3d(&corona_axis, &eye_to_corona_vector, &corona_axis); scale_vector3d(&corona_axis, 2.0f, &corona_axis); - occlusion_fraction = *occlusion_test_result*(1.0f/255.0f); + occlusion_fraction = first_person ? 1.0f : *occlusion_test_result*(1.0f/255.0f); if (definition->far_fade_distance > 0.0f) { @@ -995,6 +1060,8 @@ void rasterizer_lens_flares_draw( } light_brightness *= occlusion_fraction; + if (first_person && lens_flare_charge_grows(definition)) + light_brightness *= 0.7f; light_brightness *= uncompress_int8_to_real( (byte)LENS_FLARE_LIGHT_COLOR_ALPHA(lens_flare_parameters->compressed_light_color)); corona_rotation = lens_flare_evaluate_corona_rotation_function( @@ -1171,8 +1238,11 @@ void rasterizer_lens_flares_draw( } rasterizer_widget_set_tint_factor(tint_factor); - if (TEST_FLAG(reflection->flags, _lens_flare_reflection_zbuffer_bit) && - (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag)) + /* port: the gun marked its pixels. A charge light + draws around the body, not through it. */ + if (first_person || + (TEST_FLAG(reflection->flags, _lens_flare_reflection_zbuffer_bit) && + (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag))) { rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_REJECT); } diff --git a/source/rasterizer/xbox/rasterizer_xbox_models.c b/source/rasterizer/xbox/rasterizer_xbox_models.c index f205c9f8..cd1bec73 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_models.c +++ b/source/rasterizer/xbox/rasterizer_xbox_models.c @@ -523,6 +523,25 @@ typedef char verify_shader_model_reflection_cube_map_offset[ /* ---------- globals */ static struct render_animation const *transparent_geometry_cached_animation = NULL; + +/* port: the four function values are the caller's, and the meter is drawn +after that frame is gone. A charge sprite in between was writing over them. */ +static struct render_animation const *rasterizer_model_copy_animation( + struct render_animation const *animation) +{ + struct render_animation *copy; + + if (!animation) + return NULL; + copy = rasterizer_memory_alloc(animation, sizeof(*copy)); + if (copy && animation->values) + { + copy->values = rasterizer_memory_alloc( + animation->values, + sizeof(real) * 4); + } + return copy; +} static struct render_lighting const *transparent_geometry_cached_lighting = NULL; static short transparent_geometry_cached_node_matrix_count = 0; static real_matrix4x3 const *transparent_geometry_cached_node_matrices = NULL; @@ -1679,9 +1698,8 @@ void _rasterizer_model_draw( if (group) { - group->animation = rasterizer_memory_alloc( - &local_parameters->effect.modifier_animation, - sizeof(struct render_animation)); + group->animation = rasterizer_model_copy_animation( + &local_parameters->effect.modifier_animation); } } } @@ -2657,9 +2675,8 @@ struct transparent_geometry_group *_rasterizer_model_transparent_geometry_submit &local_parameters->lighting, sizeof(struct render_lighting)); transparent_geometry_cached_animation = - rasterizer_memory_alloc_const( - &local_parameters->animation, - sizeof(struct render_animation)); + rasterizer_model_copy_animation( + &local_parameters->animation); local_parameters_queued_flag = TRUE; } diff --git a/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c b/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c index d8e06491..adc6f2da 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c +++ b/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c @@ -1492,6 +1492,9 @@ void rasterizer_transparent_geometry_group_draw( continue; rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_ACCEPT); + /* port: these pixels are the gun, and it wrote this + depth. The charge stays behind the body. The other + pass draws it over the world. */ } else { diff --git a/source/rasterizer/xbox/rasterizer_xbox_widgets.c b/source/rasterizer/xbox/rasterizer_xbox_widgets.c index b0039a0e..1b2a8030 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_widgets.c +++ b/source/rasterizer/xbox/rasterizer_xbox_widgets.c @@ -85,6 +85,8 @@ symbols in this file: #include "rasterizer/rasterizer_widgets.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_console_vars.h" +#include "interface/first_person_weapons.h" +#include "render/render.h" /* ---------- constants */ @@ -316,6 +318,15 @@ void _rasterizer_widget_submit( camera_to_centroid.j = centroid->y - global_window_parameters.camera.position.y; camera_to_centroid.k = centroid->z - global_window_parameters.camera.position.z; group->geometry_flags = 0; + /* port: the local weapon's volume is sorted with the viewmodel + (bit 7, the first-person geometry flag) so the near plane that + fits the gun also fits the charge */ + { + long local_index = first_person_weapon_get_local_index(object_index); + + if (local_index != NONE && local_index == render.local_player_index) + group->geometry_flags = FLAG(7); + } group->object_index = 0; group->source_object_index = 0; group->shader = NULL; diff --git a/source/render/render.c b/source/render/render.c index ee4ad8cd..a86f7ded 100644 --- a/source/render/render.c +++ b/source/render/render.c @@ -325,6 +325,7 @@ static void render_window( render.local_player_index = local_player_index; render.camera = *camera; render.frustum = *frustum; + object_functions_set_for_render(TRUE); parameters.camera = *rasterizer_camera; parameters.frustum = *rasterizer_frustum; parameters.rasterizer_target = rasterizer_target; @@ -444,6 +445,7 @@ static void render_window( rasterizer_debug_draw(); rasterizer_window_end(); profile_render_window_end(); + object_functions_set_for_render(FALSE); return; } diff --git a/source/render/render_objects.c b/source/render/render_objects.c index d73d5163..199bd83c 100644 --- a/source/render/render_objects.c +++ b/source/render/render_objects.c @@ -529,6 +529,11 @@ static void render_object_list( if (!data->shadow) { + real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + + /* port: the tick's functions, moved into this frame, so a + charge clicks with the picture instead of once a tick */ + object_function_values_for_render(object_index, shown_functions); if (definition->object.modifier_shader.index != NONE) { model_effect.modifier_shader = shader_definition_get( @@ -546,8 +551,8 @@ static void render_object_list( (port/linux/game/overshield_effect.c) */ model_effect.modifier_animation.values = TEST_FLAG(_object_mask_unit, object->object.type) ? - overshield_effect_values(object_index, object->object.outgoing_function_values, FALSE) : - object->object.outgoing_function_values; + overshield_effect_values(object_index, shown_functions, FALSE) : + shown_functions; } else { @@ -629,7 +634,7 @@ static void render_object_list( node_matrices, object->object.region_permutations, object->object.outgoing_change_colors, - object->object.outgoing_function_values, + shown_functions, data->lighting, &object->object.bounding_sphere_center, object->object.bounding_sphere_radius, @@ -665,9 +670,12 @@ static void render_object_list( if (!data->shadow && !legs && object->object.first_widget_index != NONE) { struct render_animation animation; + real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + /* port: the tick's functions, moved into this frame */ + object_function_values_for_render(object_index, shown_functions); animation.colors = object->object.outgoing_change_colors; - animation.values = object->object.outgoing_function_values; + animation.values = shown_functions; widgets_render(object_index, data->lighting, &animation); } From 0ff1cad7f493062fd4b29f80588b1ecaa196c16f Mon Sep 17 00:00:00 2001 From: justinfilip Date: Thu, 22 Oct 2026 04:43:14 -0500 Subject: [PATCH 2/3] Keep a drawn object's function values until its children are drawn. --- source/render/render_objects.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/render/render_objects.c b/source/render/render_objects.c index 199bd83c..1eae3485 100644 --- a/source/render/render_objects.c +++ b/source/render/render_objects.c @@ -486,6 +486,9 @@ static void render_object_list( real_matrix4x3 const *node_matrices = legs ? first_person_legs_node_matrices(object_index, object_get_node_matrices(object_index)) : object_get_node_matrices(object_index); + /* port: a child copies this object's modifier, and that child is + drawn after the block below. The values have to still be here. */ + real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; if (!object_is_first_person_camera(object_index) || render.camera.mirrored || (legs && node_matrices)) { @@ -529,8 +532,6 @@ static void render_object_list( if (!data->shadow) { - real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; - /* port: the tick's functions, moved into this frame, so a charge clicks with the picture instead of once a tick */ object_function_values_for_render(object_index, shown_functions); From 6cff4f97a6ea3c9bc2834efca9939f46c4bb606e Mon Sep 17 00:00:00 2001 From: bnunu <212295400+bnunu@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:27:08 -0700 Subject: [PATCH 3/3] Function values with the picture: review fixes (storage, timing, split) - The picture's function values are kept in the rasterizer's memory for the frame where they are made (render_objects.c, first_person_weapons.c), not on the stack and copied later: rasterizer_model_copy_animation is gone. A modifier's group, a queued transparent part and a child that copies the modifier read the values after the block that made them, and the overshield finds its own values by their address (overshield_effect.c), which the copy broke (the overshield lost its rise, fade and color on the body and the arms). The values outliving their stack frame were what halted a match on a meter; with them kept there is no stale read, so decals.c's clamp is gone and the original assert is back. - The values are at the picture's moment, between the previous tick and this one (render_interpolation_fraction), not a tick ahead of it, and are the tick's own with Smooth Motion off. - The first-person charge's visuals are out of this pull request, for one of their own with a setting: the lens flares (and their depth state), the glow and widget on the viewmodel, the widget flag and the near plane (view_fov.c). - object_function_addition, the original loop's body as one function, is marked port:. Checked: objects.c, render_objects.c, first_person_weapons.c and render.c compile for the web and Android (also with -Wimplicit-function-declaration, -Wint-conversion, -Wincompatible-pointer-types and -Wuninitialized on: none in the changed code); merged with main, CI's test suite passes (1488). Not run: net_compare.py (objects_update's functions are computed by the split-out helper, the same steps in the same order). Co-Authored-By: Claude Opus 5.5 --- port/linux/game/view_fov.c | 6 +- source/effects/decals.c | 21 +---- source/interface/first_person_weapons.c | 31 ++++--- source/objects/objects.c | 25 ++++-- source/objects/widgets/glow.c | 32 ++------ source/rasterizer/rasterizer_lights.c | 82 ++----------------- .../rasterizer/xbox/rasterizer_xbox_models.c | 29 ++----- .../rasterizer_xbox_transparent_geometry.c | 3 - .../rasterizer/xbox/rasterizer_xbox_widgets.c | 11 --- source/render/render_objects.c | 29 +++++-- 10 files changed, 75 insertions(+), 194 deletions(-) diff --git a/port/linux/game/view_fov.c b/port/linux/game/view_fov.c index a2e530f2..2c0241de 100644 --- a/port/linux/game/view_fov.c +++ b/port/linux/game/view_fov.c @@ -231,11 +231,7 @@ void viewmodel_projection_begin(void) render_camera_build_frustum(&global_window_parameters.camera, &rasterizer_bounds, &global_window_parameters.frustum, TRUE); projection_applied = TRUE; - /* the gun sits closer than the world's near plane. A glow on it was - cut down to a sliver until this plane matches the weapon's. */ - rasterizer_set_frustum_z( - rasterizer_globals.first_person_weapon_near_clip_distance, - rasterizer_globals.first_person_weapon_far_clip_distance); + rasterizer_set_frustum_z(0.0f, 0.0f); } void viewmodel_projection_end(void) diff --git a/source/effects/decals.c b/source/effects/decals.c index 87b8e4b5..18641671 100644 --- a/source/effects/decals.c +++ b/source/effects/decals.c @@ -823,23 +823,10 @@ pixel32 real_a_rgb_color_to_pixel32( real scale = 255.0f; pixel32 result; - /* port: a meter was reading function values after their frame was gone, - and the halt stopped the match. Keep the color in range. */ - if (!(alpha >= 0.0f && alpha <= 1.0f)) - { - static boolean reported = FALSE; - - if (!reported) - { - reported = TRUE; - error( - _error_silent, - "### alpha %f from %p", - alpha, - __builtin_return_address(0)); - } - alpha = PIN(alpha, 0.0f, 1.0f); - } + match_assert( + "..\\bitmaps\\bitmaps_inlines.h", + 243, + alpha>=0.0f && alpha<=1.0f); match_vassert( "..\\bitmaps\\bitmaps_inlines.h", 244, diff --git a/source/interface/first_person_weapons.c b/source/interface/first_person_weapons.c index 2270d3df..75b4947b 100644 --- a/source/interface/first_person_weapons.c +++ b/source/interface/first_person_weapons.c @@ -117,7 +117,7 @@ symbols in this file: #include "models/models.h" #include "networking/network_connection.h" #include "objects/objects.h" -#include "objects/widgets/widgets.h" +#include "rasterizer/rasterizer.h" /* port: rasterizer_memory_alloc */ #include "rasterizer/rasterizer_model_types.h" #include "render/render.h" #include "saved games/game_state.h" @@ -510,13 +510,21 @@ void first_person_weapon_draw( struct animation_graph *animation_graph= animation_graph_definition_get( weapon_definition->weapon.interface_definition.first_person_animations.index); struct render_lighting *lighting= object_get_cached_render_lighting(unit_index, FLT_MAX); - real weapon_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; - real unit_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + real values[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + real const *weapon_functions = weapon->object.outgoing_function_values; + real const *unit_functions = unit->object.outgoing_function_values; + real *kept; /* port: the tick's functions, moved into this frame, so the - charge clicks with the picture instead of once a tick */ - object_function_values_for_render(first_person_weapon->weapon_index, weapon_functions); - object_function_values_for_render(unit_index, unit_functions); + charge clicks with the picture instead of once a tick; kept + in the rasterizer's memory for the frame, as the groups drawn + later read them (render_objects.c) */ + object_function_values_for_render(first_person_weapon->weapon_index, values); + if ((kept = rasterizer_memory_alloc(values, sizeof(values))) != NULL) + weapon_functions = kept; + object_function_values_for_render(unit_index, values); + if ((kept = rasterizer_memory_alloc(values, sizeof(values))) != NULL) + unit_functions = kept; model_effect.modifier_shader= NULL; if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit) || unit->unit.active_camouflage>0.0f) @@ -557,17 +565,6 @@ void first_person_weapon_draw( FLAG(_render_model_first_person_bit)); } - /* port: the charge is a widget on the weapon. The body is - not drawn in first person, so neither was the charge, while - another player still saw it. */ - { - struct render_animation animation; - - animation.colors= weapon->object.outgoing_change_colors; - animation.values= weapon_functions; - widgets_render(first_person_weapon->weapon_index, lighting, &animation); - } - /* port: the shield's flare on the arms as on the body, the unit's own modifier shader (display.viewmodel_shield, view_fov.c), in the color chosen for it as on the body diff --git a/source/objects/objects.c b/source/objects/objects.c index 526c76c0..ff4526cd 100644 --- a/source/objects/objects.c +++ b/source/objects/objects.c @@ -1577,21 +1577,25 @@ void object_function_values_for_render( { struct object_datum *object = object_get(object_index); struct object_definition *definition = object_definition_get(object->definition_index); - real fraction = game_time_get_tick_fraction(); + /* port: where the picture is, between the previous tick (0) and this one + (1) (render_interpolation.c); 1 outside an interpolated frame (Smooth + Motion off), where the tick's own values are shown */ + real fraction = render_interpolation_fraction(); short function_count = (short)MIN( definition->object.functions.count, NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS); short function_index; real huh; + real back; csmemcpy( values, object->object.outgoing_function_values, sizeof(object->object.outgoing_function_values)); - /* a fraction of 0 is the tick itself, and 1 is the paused hold */ - if (!(fraction > 0.0f) || !(fraction < 1.0f)) + if (!(fraction >= 0.0f) || !(fraction < 1.0f)) return; + back = 1.0f - fraction; huh = (57 * DATUM_INDEX_TO_ABSOLUTE_INDEX(object_index) + game_time_get()) * 0.033333335f; for (function_index = 0; function_index < function_count; function_index++) { @@ -1602,8 +1606,9 @@ void object_function_values_for_render( boolean active; real output; - /* port: an additive function steps by a whole tick. Move that step - across the frames. Anything else is the function at this moment. */ + /* port: an additive function steps by a whole tick: the picture + is that part of the step back from this tick's value. Anything else + is the function at the picture's moment. */ if (TEST_FLAG(function->flags, _object_function_clip_to_bounds_and_normalize)) { output = object_function_addition( @@ -1613,14 +1618,14 @@ void object_function_values_for_render( object->object.outgoing_function_values, &active); values[function_index] = object_function_wrap( - object->object.outgoing_function_values[function_index] + fraction * output); + object->object.outgoing_function_values[function_index] - back * output); } else { output = object_function_addition( object, function, - huh + fraction * 0.033333335f, + huh - back * 0.033333335f, values, &active); values[function_index] = output; @@ -5283,8 +5288,10 @@ static real object_function_wrap( return value; } -/* what one tick stores, before an additive function folds it into the value -it already held. `outgoing` is the functions already computed. */ +/* port: the original's loop body, as one function, so that the picture's +values (object_function_values_for_render) are found as the tick's are: what +one tick stores, before an additive function folds it into the value it +already held. `outgoing` is the functions already computed. */ static real object_function_addition( struct object_datum *object, struct object_function_definition *function, diff --git a/source/objects/widgets/glow.c b/source/objects/widgets/glow.c index d8c97239..64b13269 100644 --- a/source/objects/widgets/glow.c +++ b/source/objects/widgets/glow.c @@ -90,7 +90,6 @@ symbols in this file: #include "game/game.h" #include "memory/data.h" #include "objects/objects.h" -#include "interface/first_person_weapons.h" #include "render/render.h" #include "render/render_sprite.h" #include "saved games/game_state.h" @@ -550,19 +549,12 @@ void glow_render( glow = glow_get(glow_index); definition = glow_definition_get(glow->definition_index); - /* port: the local weapon's glow is drawn with the viewmodel. The world's - view cuts off a charge held against the camera. */ - { - long local_index = first_person_weapon_get_local_index(object_index); - - build_sprites_begin( - &sprite_data, - glow->number_of_particles, - definition->texture.index, - &global_shader_effect_additive, - local_index != NONE && local_index == render.local_player_index ? - FLAG(_build_sprites_first_person_bit) : 0); - } + build_sprites_begin( + &sprite_data, + glow->number_of_particles, + definition->texture.index, + &global_shader_effect_additive, + 0); for (particle = glow->head_particle; particle; particle = particle->next) { @@ -1293,21 +1285,11 @@ static void glow_update( if (!definition) return; - /* port: the local weapon's charge follows the viewmodel. The body's - markers are not on screen in first person. */ - glow->number_of_markers = first_person_weapon_get_marker_by_name_render( + glow->number_of_markers = object_get_marker_by_name( object_index, definition->attachment_marker, glow->markers, MAXIMUM_GLOW_MARKERS); - if (glow->number_of_markers <= 0) - { - glow->number_of_markers = object_get_marker_by_name( - object_index, - definition->attachment_marker, - glow->markers, - MAXIMUM_GLOW_MARKERS); - } if (!glow->initialized && glow->number_of_markers > 1) { diff --git a/source/rasterizer/rasterizer_lights.c b/source/rasterizer/rasterizer_lights.c index e9933769..61662307 100644 --- a/source/rasterizer/rasterizer_lights.c +++ b/source/rasterizer/rasterizer_lights.c @@ -869,14 +869,9 @@ void rasterizer_lens_flares_submit_occlusion_tests( real occlusion_radius = definition->occlusion_radius; real_point3d occlusion_point; - /* port: the first-person weapon's projection (view_fov.c). - The gun covers the marker, so a depth test there counts no - pixels and the charge is never drawn. */ + /* port: the first-person weapon's projection (view_fov.c) */ if (first_person) - { - rasterizer_widget_set_zbuffer_enable(FALSE); viewmodel_projection_begin(); - } switch (definition->occlusion_offset_direction) { @@ -913,10 +908,7 @@ void rasterizer_lens_flares_submit_occlusion_tests( lens_flare_index); if (first_person) - { viewmodel_projection_end(); - rasterizer_widget_set_zbuffer_enable(TRUE); - } } } @@ -928,53 +920,6 @@ void rasterizer_lens_flares_submit_occlusion_tests( return; } -/* port: the plasma pistol's two front lights are one fixed sprite, 0.0188 -across, on each prong. The player looks at those from behind the gun, so -they stay on the view other players have. The needler's smoke light is -0.003 and the flashlight is 0.05, so this band is those two prongs. */ -static boolean lens_flare_first_person_front_light( - struct lens_flare_definition *definition) -{ - real largest = 0.0f; - short index; - - for (index = 0; index < definition->reflections.count; index++) - { - struct lens_flare_reflection *reflection = TAG_BLOCK_GET_ELEMENT( - &definition->reflections, - index, - struct lens_flare_reflection); - - if (reflection->radius_upper_bounds > reflection->radius_lower_bounds) - return FALSE; - if (reflection->radius_upper_bounds > largest) - largest = reflection->radius_upper_bounds; - } - - return largest >= 0.01f && largest < 0.02f; -} - -/* port: a charge grows with the light (the pistol's reaches 0.175). The -first-person one is drawn with no occlusion fade, so it is pulled down. */ -static boolean lens_flare_charge_grows( - struct lens_flare_definition *definition) -{ - short index; - - for (index = 0; index < definition->reflections.count; index++) - { - struct lens_flare_reflection *reflection = TAG_BLOCK_GET_ELEMENT( - &definition->reflections, - index, - struct lens_flare_reflection); - - if (reflection->radius_upper_bounds > reflection->radius_lower_bounds) - return TRUE; - } - - return FALSE; -} - void rasterizer_lens_flares_draw( void) { @@ -1001,22 +946,12 @@ void rasterizer_lens_flares_draw( boolean first_person = (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag) != 0; struct lens_flare_definition *definition = lens_flare_parameters->definition; - boolean front_light = first_person && - lens_flare_first_person_front_light(definition); - /* port: the first-person weapon's projection (view_fov.c). - Tested against the scene, the sprite sits behind the gun, - so the charge never shows. */ + /* port: the first-person weapon's projection (view_fov.c) */ if (first_person) - { - rasterizer_widget_set_zbuffer_enable(FALSE); viewmodel_projection_begin(); - } - /* port: the test is in the gun's camera and the depth is the - world's, so aiming at a wall counted the charge as hidden */ - if ((first_person || lens_flare_parameters->internal__occlusion_pixels > 0) && - !front_light && + if (lens_flare_parameters->internal__occlusion_pixels > 0 && LENS_FLARE_LIGHT_COLOR_ALPHA(lens_flare_parameters->compressed_light_color) > 0 && definition->reflections.count > 0) { @@ -1044,7 +979,7 @@ void rasterizer_lens_flares_draw( subtract_vectors3d(&corona_axis, &eye_to_corona_vector, &corona_axis); scale_vector3d(&corona_axis, 2.0f, &corona_axis); - occlusion_fraction = first_person ? 1.0f : *occlusion_test_result*(1.0f/255.0f); + occlusion_fraction = *occlusion_test_result*(1.0f/255.0f); if (definition->far_fade_distance > 0.0f) { @@ -1060,8 +995,6 @@ void rasterizer_lens_flares_draw( } light_brightness *= occlusion_fraction; - if (first_person && lens_flare_charge_grows(definition)) - light_brightness *= 0.7f; light_brightness *= uncompress_int8_to_real( (byte)LENS_FLARE_LIGHT_COLOR_ALPHA(lens_flare_parameters->compressed_light_color)); corona_rotation = lens_flare_evaluate_corona_rotation_function( @@ -1238,11 +1171,8 @@ void rasterizer_lens_flares_draw( } rasterizer_widget_set_tint_factor(tint_factor); - /* port: the gun marked its pixels. A charge light - draws around the body, not through it. */ - if (first_person || - (TEST_FLAG(reflection->flags, _lens_flare_reflection_zbuffer_bit) && - (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag))) + if (TEST_FLAG(reflection->flags, _lens_flare_reflection_zbuffer_bit) && + (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag)) { rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_REJECT); } diff --git a/source/rasterizer/xbox/rasterizer_xbox_models.c b/source/rasterizer/xbox/rasterizer_xbox_models.c index cd1bec73..f205c9f8 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_models.c +++ b/source/rasterizer/xbox/rasterizer_xbox_models.c @@ -523,25 +523,6 @@ typedef char verify_shader_model_reflection_cube_map_offset[ /* ---------- globals */ static struct render_animation const *transparent_geometry_cached_animation = NULL; - -/* port: the four function values are the caller's, and the meter is drawn -after that frame is gone. A charge sprite in between was writing over them. */ -static struct render_animation const *rasterizer_model_copy_animation( - struct render_animation const *animation) -{ - struct render_animation *copy; - - if (!animation) - return NULL; - copy = rasterizer_memory_alloc(animation, sizeof(*copy)); - if (copy && animation->values) - { - copy->values = rasterizer_memory_alloc( - animation->values, - sizeof(real) * 4); - } - return copy; -} static struct render_lighting const *transparent_geometry_cached_lighting = NULL; static short transparent_geometry_cached_node_matrix_count = 0; static real_matrix4x3 const *transparent_geometry_cached_node_matrices = NULL; @@ -1698,8 +1679,9 @@ void _rasterizer_model_draw( if (group) { - group->animation = rasterizer_model_copy_animation( - &local_parameters->effect.modifier_animation); + group->animation = rasterizer_memory_alloc( + &local_parameters->effect.modifier_animation, + sizeof(struct render_animation)); } } } @@ -2675,8 +2657,9 @@ struct transparent_geometry_group *_rasterizer_model_transparent_geometry_submit &local_parameters->lighting, sizeof(struct render_lighting)); transparent_geometry_cached_animation = - rasterizer_model_copy_animation( - &local_parameters->animation); + rasterizer_memory_alloc_const( + &local_parameters->animation, + sizeof(struct render_animation)); local_parameters_queued_flag = TRUE; } diff --git a/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c b/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c index adc6f2da..d8e06491 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c +++ b/source/rasterizer/xbox/rasterizer_xbox_transparent_geometry.c @@ -1492,9 +1492,6 @@ void rasterizer_transparent_geometry_group_draw( continue; rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_ACCEPT); - /* port: these pixels are the gun, and it wrote this - depth. The charge stays behind the body. The other - pass draws it over the world. */ } else { diff --git a/source/rasterizer/xbox/rasterizer_xbox_widgets.c b/source/rasterizer/xbox/rasterizer_xbox_widgets.c index 1b2a8030..b0039a0e 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_widgets.c +++ b/source/rasterizer/xbox/rasterizer_xbox_widgets.c @@ -85,8 +85,6 @@ symbols in this file: #include "rasterizer/rasterizer_widgets.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_console_vars.h" -#include "interface/first_person_weapons.h" -#include "render/render.h" /* ---------- constants */ @@ -318,15 +316,6 @@ void _rasterizer_widget_submit( camera_to_centroid.j = centroid->y - global_window_parameters.camera.position.y; camera_to_centroid.k = centroid->z - global_window_parameters.camera.position.z; group->geometry_flags = 0; - /* port: the local weapon's volume is sorted with the viewmodel - (bit 7, the first-person geometry flag) so the near plane that - fits the gun also fits the charge */ - { - long local_index = first_person_weapon_get_local_index(object_index); - - if (local_index != NONE && local_index == render.local_player_index) - group->geometry_flags = FLAG(7); - } group->object_index = 0; group->source_object_index = 0; group->shader = NULL; diff --git a/source/render/render_objects.c b/source/render/render_objects.c index 1eae3485..212348c5 100644 --- a/source/render/render_objects.c +++ b/source/render/render_objects.c @@ -486,9 +486,13 @@ static void render_object_list( real_matrix4x3 const *node_matrices = legs ? first_person_legs_node_matrices(object_index, object_get_node_matrices(object_index)) : object_get_node_matrices(object_index); - /* port: a child copies this object's modifier, and that child is - drawn after the block below. The values have to still be here. */ - real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + /* port: the function values the picture shows + (object_function_values_for_render), kept in the rasterizer's memory + for the frame: a modifier's group, a queued transparent part and a + child that copies this modifier read them after this block is done, + and the overshield finds its own values by their address + (overshield_effect.c), so they are not copied again */ + real const *shown_functions = object->object.outgoing_function_values; if (!object_is_first_person_camera(object_index) || render.camera.mirrored || (legs && node_matrices)) { @@ -534,7 +538,15 @@ static void render_object_list( { /* port: the tick's functions, moved into this frame, so a charge clicks with the picture instead of once a tick */ - object_function_values_for_render(object_index, shown_functions); + { + real values[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + real *kept; + + object_function_values_for_render(object_index, values); + kept = rasterizer_memory_alloc(values, sizeof(values)); + if (kept) + shown_functions = kept; + } if (definition->object.modifier_shader.index != NONE) { model_effect.modifier_shader = shader_definition_get( @@ -671,12 +683,13 @@ static void render_object_list( if (!data->shadow && !legs && object->object.first_widget_index != NONE) { struct render_animation animation; - real shown_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; + real widget_functions[NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS]; - /* port: the tick's functions, moved into this frame */ - object_function_values_for_render(object_index, shown_functions); + /* port: the tick's functions, moved into this frame (the + widgets read them as they are drawn, here) */ + object_function_values_for_render(object_index, widget_functions); animation.colors = object->object.outgoing_change_colors; - animation.values = shown_functions; + animation.values = widget_functions; widgets_render(object_index, data->lighting, &animation); }