2023-01-10 15:26:54 +01:00
/**************************************************************************/
/* rasterizer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "rasterizer.h"
2017-08-27 21:07:15 +02:00
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
# include "core/print_string.h"
2014-02-10 02:10:30 +01:00
2022-04-26 13:59:53 +02:00
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
# include "core/project_settings.h"
# endif
2021-05-04 16:00:45 +02:00
Rasterizer * ( * Rasterizer : : _create_func ) ( ) = nullptr ;
2016-10-03 21:33:42 +02:00
Rasterizer * Rasterizer : : create ( ) {
return _create_func ( ) ;
}
2021-05-04 16:00:45 +02:00
RasterizerStorage * RasterizerStorage : : base_singleton = nullptr ;
2016-10-03 21:33:42 +02:00
RasterizerStorage : : RasterizerStorage ( ) {
2017-05-29 08:08:16 +02:00
base_singleton = this ;
2016-10-03 21:33:42 +02:00
}
2020-07-10 10:25:06 +02:00
bool RasterizerStorage : : material_uses_tangents ( RID p_material ) {
return false ;
}
bool RasterizerStorage : : material_uses_ensure_correct_normals ( RID p_material ) {
return false ;
}
2021-09-15 13:30:45 +02:00
void RasterizerStorage : : InterpolationData : : notify_free_multimesh ( RID p_rid ) {
// print_line("free multimesh " + itos(p_rid.get_id()));
// if the instance was on any of the lists, remove
multimesh_interpolate_update_list . erase_multiple_unordered ( p_rid ) ;
multimesh_transform_update_lists [ 0 ] . erase_multiple_unordered ( p_rid ) ;
multimesh_transform_update_lists [ 1 ] . erase_multiple_unordered ( p_rid ) ;
}
void RasterizerStorage : : update_interpolation_tick ( bool p_process ) {
// detect any that were on the previous transform list that are no longer active,
// we should remove them from the interpolate list
for ( unsigned int n = 0 ; n < _interpolation_data . multimesh_transform_update_list_prev - > size ( ) ; n + + ) {
const RID & rid = ( * _interpolation_data . multimesh_transform_update_list_prev ) [ n ] ;
bool active = true ;
// no longer active? (either the instance deleted or no longer being transformed)
MMInterpolator * mmi = _multimesh_get_interpolator ( rid ) ;
if ( mmi & & ! mmi - > on_transform_update_list ) {
active = false ;
mmi - > on_interpolate_update_list = false ;
// make sure the most recent transform is set
// copy data rather than use Pool = function?
mmi - > _data_interpolated = mmi - > _data_curr ;
// and that both prev and current are the same, just in case of any interpolations
mmi - > _data_prev = mmi - > _data_curr ;
// make sure are updated one more time to ensure the AABBs are correct
//_instance_queue_update(instance, true);
}
if ( ! mmi ) {
active = false ;
}
if ( ! active ) {
_interpolation_data . multimesh_interpolate_update_list . erase ( rid ) ;
}
}
if ( p_process ) {
for ( unsigned int i = 0 ; i < _interpolation_data . multimesh_transform_update_list_curr - > size ( ) ; i + + ) {
const RID & rid = ( * _interpolation_data . multimesh_transform_update_list_curr ) [ i ] ;
MMInterpolator * mmi = _multimesh_get_interpolator ( rid ) ;
if ( mmi ) {
// reset for next tick
mmi - > on_transform_update_list = false ;
mmi - > _data_prev = mmi - > _data_curr ;
}
} // for n
}
// if any have left the transform list, remove from the interpolate list
// we maintain a mirror list for the transform updates, so we can detect when an instance
// is no longer being transformed, and remove it from the interpolate list
SWAP ( _interpolation_data . multimesh_transform_update_list_curr , _interpolation_data . multimesh_transform_update_list_prev ) ;
// prepare for the next iteration
_interpolation_data . multimesh_transform_update_list_curr - > clear ( ) ;
}
void RasterizerStorage : : update_interpolation_frame ( bool p_process ) {
if ( p_process ) {
// Only need 32 bit for interpolation, don't use real_t
float f = Engine : : get_singleton ( ) - > get_physics_interpolation_fraction ( ) ;
for ( unsigned int c = 0 ; c < _interpolation_data . multimesh_interpolate_update_list . size ( ) ; c + + ) {
const RID & rid = _interpolation_data . multimesh_interpolate_update_list [ c ] ;
// We could use the TransformInterpolator here to slerp transforms, but that might be too expensive,
// so just using a Basis lerp for now.
MMInterpolator * mmi = _multimesh_get_interpolator ( rid ) ;
if ( mmi ) {
// make sure arrays are correct size
DEV_ASSERT ( mmi - > _data_prev . size ( ) = = mmi - > _data_curr . size ( ) ) ;
if ( mmi - > _data_interpolated . size ( ) < mmi - > _data_curr . size ( ) ) {
mmi - > _data_interpolated . resize ( mmi - > _data_curr . size ( ) ) ;
}
DEV_ASSERT ( mmi - > _data_interpolated . size ( ) > = mmi - > _data_curr . size ( ) ) ;
DEV_ASSERT ( ( mmi - > _data_curr . size ( ) % mmi - > _stride ) = = 0 ) ;
int num = mmi - > _data_curr . size ( ) / mmi - > _stride ;
PoolVector < float > : : Read r_prev = mmi - > _data_prev . read ( ) ;
PoolVector < float > : : Read r_curr = mmi - > _data_curr . read ( ) ;
PoolVector < float > : : Write w = mmi - > _data_interpolated . write ( ) ;
const float * pf_prev = r_prev . ptr ( ) ;
const float * pf_curr = r_curr . ptr ( ) ;
float * pf_int = w . ptr ( ) ;
bool use_lerp = mmi - > quality = = 0 ;
// temporary transform (needed for swizzling)
// (transform prev, curr and result)
Transform tp , tc , tr ;
// Test for cache friendliness versus doing branchless
for ( int n = 0 ; n < num ; n + + ) {
// Transform
if ( use_lerp ) {
for ( int i = 0 ; i < mmi - > _vf_size_xform ; i + + ) {
float a = pf_prev [ i ] ;
float b = pf_curr [ i ] ;
pf_int [ i ] = ( a + ( ( b - a ) * f ) ) ;
}
} else {
// Silly swizzling, this will slow things down. no idea why it is using this format
// .. maybe due to the shader.
tp . basis . elements [ 0 ] [ 0 ] = pf_prev [ 0 ] ;
tp . basis . elements [ 0 ] [ 1 ] = pf_prev [ 1 ] ;
tp . basis . elements [ 0 ] [ 2 ] = pf_prev [ 2 ] ;
tp . basis . elements [ 1 ] [ 0 ] = pf_prev [ 4 ] ;
tp . basis . elements [ 1 ] [ 1 ] = pf_prev [ 5 ] ;
tp . basis . elements [ 1 ] [ 2 ] = pf_prev [ 6 ] ;
tp . basis . elements [ 2 ] [ 0 ] = pf_prev [ 8 ] ;
tp . basis . elements [ 2 ] [ 1 ] = pf_prev [ 9 ] ;
tp . basis . elements [ 2 ] [ 2 ] = pf_prev [ 10 ] ;
tp . origin . x = pf_prev [ 3 ] ;
tp . origin . y = pf_prev [ 7 ] ;
tp . origin . z = pf_prev [ 11 ] ;
tc . basis . elements [ 0 ] [ 0 ] = pf_curr [ 0 ] ;
tc . basis . elements [ 0 ] [ 1 ] = pf_curr [ 1 ] ;
tc . basis . elements [ 0 ] [ 2 ] = pf_curr [ 2 ] ;
tc . basis . elements [ 1 ] [ 0 ] = pf_curr [ 4 ] ;
tc . basis . elements [ 1 ] [ 1 ] = pf_curr [ 5 ] ;
tc . basis . elements [ 1 ] [ 2 ] = pf_curr [ 6 ] ;
tc . basis . elements [ 2 ] [ 0 ] = pf_curr [ 8 ] ;
tc . basis . elements [ 2 ] [ 1 ] = pf_curr [ 9 ] ;
tc . basis . elements [ 2 ] [ 2 ] = pf_curr [ 10 ] ;
tc . origin . x = pf_curr [ 3 ] ;
tc . origin . y = pf_curr [ 7 ] ;
tc . origin . z = pf_curr [ 11 ] ;
TransformInterpolator : : interpolate_transform ( tp , tc , tr , f ) ;
pf_int [ 0 ] = tr . basis . elements [ 0 ] [ 0 ] ;
pf_int [ 1 ] = tr . basis . elements [ 0 ] [ 1 ] ;
pf_int [ 2 ] = tr . basis . elements [ 0 ] [ 2 ] ;
pf_int [ 4 ] = tr . basis . elements [ 1 ] [ 0 ] ;
pf_int [ 5 ] = tr . basis . elements [ 1 ] [ 1 ] ;
pf_int [ 6 ] = tr . basis . elements [ 1 ] [ 2 ] ;
pf_int [ 8 ] = tr . basis . elements [ 2 ] [ 0 ] ;
pf_int [ 9 ] = tr . basis . elements [ 2 ] [ 1 ] ;
pf_int [ 10 ] = tr . basis . elements [ 2 ] [ 2 ] ;
pf_int [ 3 ] = tr . origin . x ;
pf_int [ 7 ] = tr . origin . y ;
pf_int [ 11 ] = tr . origin . z ;
}
pf_prev + = mmi - > _vf_size_xform ;
pf_curr + = mmi - > _vf_size_xform ;
pf_int + = mmi - > _vf_size_xform ;
// Color
if ( mmi - > _vf_size_color = = 1 ) {
const uint8_t * p8_prev = ( const uint8_t * ) pf_prev ;
const uint8_t * p8_curr = ( const uint8_t * ) pf_curr ;
uint8_t * p8_int = ( uint8_t * ) pf_int ;
_interpolate_RGBA8 ( p8_prev , p8_curr , p8_int , f ) ;
pf_prev + = 1 ;
pf_curr + = 1 ;
pf_int + = 1 ;
} else if ( mmi - > _vf_size_color = = 4 ) {
for ( int i = 0 ; i < 4 ; i + + ) {
pf_int [ i ] = pf_prev [ i ] + ( ( pf_curr [ i ] - pf_prev [ i ] ) * f ) ;
}
pf_prev + = 4 ;
pf_curr + = 4 ;
pf_int + = 4 ;
}
// Custom Data
if ( mmi - > _vf_size_data = = 1 ) {
const uint8_t * p8_prev = ( const uint8_t * ) pf_prev ;
const uint8_t * p8_curr = ( const uint8_t * ) pf_curr ;
uint8_t * p8_int = ( uint8_t * ) pf_int ;
_interpolate_RGBA8 ( p8_prev , p8_curr , p8_int , f ) ;
pf_prev + = 1 ;
pf_curr + = 1 ;
pf_int + = 1 ;
} else if ( mmi - > _vf_size_data = = 4 ) {
for ( int i = 0 ; i < 4 ; i + + ) {
pf_int [ i ] = pf_prev [ i ] + ( ( pf_curr [ i ] - pf_prev [ i ] ) * f ) ;
}
pf_prev + = 4 ;
pf_curr + = 4 ;
pf_int + = 4 ;
}
}
_multimesh_set_as_bulk_array ( rid , mmi - > _data_interpolated ) ;
// make sure AABBs are constantly up to date through the interpolation?
// NYI
}
} // for n
}
}
RID RasterizerStorage : : multimesh_create ( ) {
return _multimesh_create ( ) ;
}
void RasterizerStorage : : multimesh_allocate ( RID p_multimesh , int p_instances , VS : : MultimeshTransformFormat p_transform_format , VS : : MultimeshColorFormat p_color_format , VS : : MultimeshCustomDataFormat p_data ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
mmi - > _transform_format = p_transform_format ;
mmi - > _color_format = p_color_format ;
mmi - > _data_format = p_data ;
mmi - > _num_instances = p_instances ;
mmi - > _vf_size_xform = p_transform_format = = VS : : MULTIMESH_TRANSFORM_3D ? 12 : 8 ;
switch ( p_color_format ) {
default : {
mmi - > _vf_size_color = 0 ;
} break ;
case VS : : MULTIMESH_COLOR_8BIT : {
mmi - > _vf_size_color = 1 ;
} break ;
case VS : : MULTIMESH_COLOR_FLOAT : {
mmi - > _vf_size_color = 4 ;
} break ;
}
switch ( p_data ) {
default : {
mmi - > _vf_size_data = 0 ;
} break ;
case VS : : MULTIMESH_CUSTOM_DATA_8BIT : {
mmi - > _vf_size_data = 1 ;
} break ;
case VS : : MULTIMESH_CUSTOM_DATA_FLOAT : {
mmi - > _vf_size_data = 4 ;
} break ;
}
mmi - > _stride = mmi - > _vf_size_xform + mmi - > _vf_size_color + mmi - > _vf_size_data ;
int size_in_floats = p_instances * mmi - > _stride ;
mmi - > _data_curr . resize ( size_in_floats ) ;
mmi - > _data_prev . resize ( size_in_floats ) ;
mmi - > _data_interpolated . resize ( size_in_floats ) ;
}
return _multimesh_allocate ( p_multimesh , p_instances , p_transform_format , p_color_format , p_data ) ;
}
int RasterizerStorage : : multimesh_get_instance_count ( RID p_multimesh ) const {
return _multimesh_get_instance_count ( p_multimesh ) ;
}
void RasterizerStorage : : multimesh_set_mesh ( RID p_multimesh , RID p_mesh ) {
_multimesh_set_mesh ( p_multimesh , p_mesh ) ;
}
void RasterizerStorage : : multimesh_instance_set_transform ( RID p_multimesh , int p_index , const Transform & p_transform ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
if ( mmi - > interpolated ) {
ERR_FAIL_COND ( p_index > = mmi - > _num_instances ) ;
ERR_FAIL_COND ( mmi - > _vf_size_xform ! = 12 ) ;
PoolVector < float > : : Write w = mmi - > _data_curr . write ( ) ;
int start = p_index * mmi - > _stride ;
float * ptr = w . ptr ( ) ;
ptr + = start ;
const Transform & t = p_transform ;
ptr [ 0 ] = t . basis . elements [ 0 ] [ 0 ] ;
ptr [ 1 ] = t . basis . elements [ 0 ] [ 1 ] ;
ptr [ 2 ] = t . basis . elements [ 0 ] [ 2 ] ;
ptr [ 3 ] = t . origin . x ;
ptr [ 4 ] = t . basis . elements [ 1 ] [ 0 ] ;
ptr [ 5 ] = t . basis . elements [ 1 ] [ 1 ] ;
ptr [ 6 ] = t . basis . elements [ 1 ] [ 2 ] ;
ptr [ 7 ] = t . origin . y ;
ptr [ 8 ] = t . basis . elements [ 2 ] [ 0 ] ;
ptr [ 9 ] = t . basis . elements [ 2 ] [ 1 ] ;
ptr [ 10 ] = t . basis . elements [ 2 ] [ 2 ] ;
ptr [ 11 ] = t . origin . z ;
_multimesh_add_to_interpolation_lists ( p_multimesh , * mmi ) ;
2022-04-26 13:59:53 +02:00
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if ( ! Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
static int32_t warn_count = 0 ;
warn_count + + ;
if ( ( ( warn_count % 2048 ) = = 0 ) & & GLOBAL_GET ( " debug/settings/physics_interpolation/enable_warnings " ) ) {
2022-05-04 18:04:22 +02:00
WARN_PRINT ( " [Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign). " ) ;
2022-04-26 13:59:53 +02:00
}
}
# endif
2021-09-15 13:30:45 +02:00
return ;
}
}
_multimesh_instance_set_transform ( p_multimesh , p_index , p_transform ) ;
}
void RasterizerStorage : : multimesh_instance_set_transform_2d ( RID p_multimesh , int p_index , const Transform2D & p_transform ) {
_multimesh_instance_set_transform_2d ( p_multimesh , p_index , p_transform ) ;
}
void RasterizerStorage : : multimesh_instance_set_color ( RID p_multimesh , int p_index , const Color & p_color ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
if ( mmi - > interpolated ) {
ERR_FAIL_COND ( p_index > = mmi - > _num_instances ) ;
ERR_FAIL_COND ( mmi - > _vf_size_color = = 0 ) ;
PoolVector < float > : : Write w = mmi - > _data_curr . write ( ) ;
int start = ( p_index * mmi - > _stride ) + mmi - > _vf_size_xform ;
float * ptr = w . ptr ( ) ;
ptr + = start ;
if ( mmi - > _vf_size_color = = 4 ) {
for ( int n = 0 ; n < 4 ; n + + ) {
ptr [ n ] = p_color . components [ n ] ;
}
} else {
# ifdef DEV_ENABLED
// The options are currently 4, 1, or zero, but just in case this changes in future...
ERR_FAIL_COND ( mmi - > _vf_size_color ! = 1 ) ;
# endif
uint32_t * pui = ( uint32_t * ) ptr ;
* pui = p_color . to_rgba32 ( ) ;
}
_multimesh_add_to_interpolation_lists ( p_multimesh , * mmi ) ;
return ;
}
}
_multimesh_instance_set_color ( p_multimesh , p_index , p_color ) ;
}
void RasterizerStorage : : multimesh_instance_set_custom_data ( RID p_multimesh , int p_index , const Color & p_color ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
if ( mmi - > interpolated ) {
ERR_FAIL_COND ( p_index > = mmi - > _num_instances ) ;
ERR_FAIL_COND ( mmi - > _vf_size_data = = 0 ) ;
PoolVector < float > : : Write w = mmi - > _data_curr . write ( ) ;
int start = ( p_index * mmi - > _stride ) + mmi - > _vf_size_xform + mmi - > _vf_size_color ;
float * ptr = w . ptr ( ) ;
ptr + = start ;
if ( mmi - > _vf_size_data = = 4 ) {
for ( int n = 0 ; n < 4 ; n + + ) {
ptr [ n ] = p_color . components [ n ] ;
}
} else {
# ifdef DEV_ENABLED
// The options are currently 4, 1, or zero, but just in case this changes in future...
ERR_FAIL_COND ( mmi - > _vf_size_data ! = 1 ) ;
# endif
uint32_t * pui = ( uint32_t * ) ptr ;
* pui = p_color . to_rgba32 ( ) ;
}
_multimesh_add_to_interpolation_lists ( p_multimesh , * mmi ) ;
return ;
}
}
_multimesh_instance_set_custom_data ( p_multimesh , p_index , p_color ) ;
}
RID RasterizerStorage : : multimesh_get_mesh ( RID p_multimesh ) const {
return _multimesh_get_mesh ( p_multimesh ) ;
}
Transform RasterizerStorage : : multimesh_instance_get_transform ( RID p_multimesh , int p_index ) const {
return _multimesh_instance_get_transform ( p_multimesh , p_index ) ;
}
Transform2D RasterizerStorage : : multimesh_instance_get_transform_2d ( RID p_multimesh , int p_index ) const {
return _multimesh_instance_get_transform_2d ( p_multimesh , p_index ) ;
}
Color RasterizerStorage : : multimesh_instance_get_color ( RID p_multimesh , int p_index ) const {
return _multimesh_instance_get_color ( p_multimesh , p_index ) ;
}
Color RasterizerStorage : : multimesh_instance_get_custom_data ( RID p_multimesh , int p_index ) const {
return _multimesh_instance_get_custom_data ( p_multimesh , p_index ) ;
}
void RasterizerStorage : : multimesh_set_physics_interpolated ( RID p_multimesh , bool p_interpolated ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
mmi - > interpolated = p_interpolated ;
}
}
2022-08-26 11:43:48 +02:00
void RasterizerStorage : : multimesh_set_physics_interpolation_quality ( RID p_multimesh , VS : : MultimeshPhysicsInterpolationQuality p_quality ) {
2021-09-15 13:30:45 +02:00
ERR_FAIL_COND ( ( p_quality < 0 ) | | ( p_quality > 1 ) ) ;
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
2022-08-26 11:43:48 +02:00
mmi - > quality = ( int ) p_quality ;
2021-09-15 13:30:45 +02:00
}
}
void RasterizerStorage : : multimesh_instance_reset_physics_interpolation ( RID p_multimesh , int p_index ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
2022-04-25 16:55:33 +02:00
ERR_FAIL_INDEX ( p_index , mmi - > _num_instances ) ;
2021-09-15 13:30:45 +02:00
PoolVector < float > : : Write w = mmi - > _data_prev . write ( ) ;
PoolVector < float > : : Read r = mmi - > _data_curr . read ( ) ;
int start = p_index * mmi - > _stride ;
for ( int n = 0 ; n < mmi - > _stride ; n + + ) {
w [ start + n ] = r [ start + n ] ;
}
}
}
void RasterizerStorage : : _multimesh_add_to_interpolation_lists ( RID p_multimesh , MMInterpolator & r_mmi ) {
if ( ! r_mmi . on_interpolate_update_list ) {
r_mmi . on_interpolate_update_list = true ;
_interpolation_data . multimesh_interpolate_update_list . push_back ( p_multimesh ) ;
}
if ( ! r_mmi . on_transform_update_list ) {
r_mmi . on_transform_update_list = true ;
_interpolation_data . multimesh_transform_update_list_curr - > push_back ( p_multimesh ) ;
}
}
void RasterizerStorage : : multimesh_set_as_bulk_array_interpolated ( RID p_multimesh , const PoolVector < float > & p_array , const PoolVector < float > & p_array_prev ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
2022-10-02 09:30:55 +02:00
ERR_FAIL_COND_MSG ( p_array . size ( ) ! = mmi - > _data_curr . size ( ) , vformat ( " Array for current frame should have %d elements, got %d instead. " , mmi - > _data_curr . size ( ) , p_array . size ( ) ) ) ;
ERR_FAIL_COND_MSG ( p_array_prev . size ( ) ! = mmi - > _data_prev . size ( ) , vformat ( " Array for previous frame should have %d elements, got %d instead. " , mmi - > _data_prev . size ( ) , p_array_prev . size ( ) ) ) ;
2021-09-15 13:30:45 +02:00
// We are assuming that mmi->interpolated is the case,
// (can possibly assert this?)
// even if this flag hasn't been set - just calling this function suggests
// interpolation is desired.
mmi - > _data_prev = p_array_prev ;
mmi - > _data_curr = p_array ;
_multimesh_add_to_interpolation_lists ( p_multimesh , * mmi ) ;
2022-04-26 13:59:53 +02:00
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if ( ! Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
static int32_t warn_count = 0 ;
warn_count + + ;
if ( ( ( warn_count % 2048 ) = = 0 ) & & GLOBAL_GET ( " debug/settings/physics_interpolation/enable_warnings " ) ) {
2022-05-04 18:04:22 +02:00
WARN_PRINT ( " [Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign). " ) ;
2022-04-26 13:59:53 +02:00
}
}
# endif
2021-09-15 13:30:45 +02:00
}
}
void RasterizerStorage : : multimesh_set_as_bulk_array ( RID p_multimesh , const PoolVector < float > & p_array ) {
MMInterpolator * mmi = _multimesh_get_interpolator ( p_multimesh ) ;
if ( mmi ) {
if ( mmi - > interpolated ) {
2022-10-02 09:30:55 +02:00
ERR_FAIL_COND_MSG ( p_array . size ( ) ! = mmi - > _data_curr . size ( ) , vformat ( " Array should have %d elements, got %d instead. " , mmi - > _data_curr . size ( ) , p_array . size ( ) ) ) ;
2021-09-15 13:30:45 +02:00
mmi - > _data_curr = p_array ;
_multimesh_add_to_interpolation_lists ( p_multimesh , * mmi ) ;
2022-04-26 13:59:53 +02:00
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if ( ! Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
static int32_t warn_count = 0 ;
warn_count + + ;
if ( ( ( warn_count % 2048 ) = = 0 ) & & GLOBAL_GET ( " debug/settings/physics_interpolation/enable_warnings " ) ) {
2022-05-04 18:04:22 +02:00
WARN_PRINT ( " [Physics interpolation] MultiMesh interpolation is being triggered from outside physics process, this might lead to issues (possibly benign). " ) ;
2022-04-26 13:59:53 +02:00
}
}
# endif
2021-09-15 13:30:45 +02:00
return ;
}
}
_multimesh_set_as_bulk_array ( p_multimesh , p_array ) ;
}
void RasterizerStorage : : multimesh_set_visible_instances ( RID p_multimesh , int p_visible ) {
_multimesh_set_visible_instances ( p_multimesh , p_visible ) ;
}
int RasterizerStorage : : multimesh_get_visible_instances ( RID p_multimesh ) const {
return _multimesh_get_visible_instances ( p_multimesh ) ;
}
AABB RasterizerStorage : : multimesh_get_aabb ( RID p_multimesh ) const {
return _multimesh_get_aabb ( p_multimesh ) ;
}
2023-04-10 17:19:32 +02:00
// The bone bounds are determined by rigging,
// as such they can be calculated as a one off operation,
// rather than each call to get_rect().
void RasterizerCanvas : : Item : : precalculate_polygon_bone_bounds ( const Item : : CommandPolygon & p_polygon ) const {
p_polygon . skinning_data - > dirty = false ;
p_polygon . skinning_data - > untransformed_bound = Rect2 ( Vector2 ( ) , Vector2 ( - 1 , - 1 ) ) ; // negative means unused.
int num_points = p_polygon . points . size ( ) ;
const Point2 * pp = & p_polygon . points [ 0 ] ;
// Calculate bone AABBs.
int bone_count = RasterizerStorage : : base_singleton - > skeleton_get_bone_count ( skeleton ) ;
// Get some local aliases
LocalVector < Rect2 > & active_bounds = p_polygon . skinning_data - > active_bounds ;
LocalVector < uint16_t > & active_bone_ids = p_polygon . skinning_data - > active_bone_ids ;
active_bounds . clear ( ) ;
active_bone_ids . clear ( ) ;
// Uses dynamic allocation, but shouldn't happen very often.
// If happens more often, use alloca.
LocalVector < int32_t > bone_to_active_bone_mapping ;
bone_to_active_bone_mapping . resize ( bone_count ) ;
for ( int n = 0 ; n < bone_count ; n + + ) {
bone_to_active_bone_mapping [ n ] = - 1 ;
}
const Transform2D & item_transform = skinning_data - > skeleton_relative_xform ;
bool some_were_untransformed = false ;
for ( int n = 0 ; n < num_points ; n + + ) {
Point2 p = pp [ n ] ;
bool bone_space = false ;
float total_weight = 0 ;
for ( int k = 0 ; k < 4 ; k + + ) {
int bone_id = p_polygon . bones [ n * 4 + k ] ;
float w = p_polygon . weights [ n * 4 + k ] ;
if ( w = = 0 ) {
continue ;
}
total_weight + = w ;
// Ensure the point is in "bone space" / rigged space.
if ( ! bone_space ) {
bone_space = true ;
p = item_transform . xform ( p ) ;
}
// get the active bone, or create a new active bone
DEV_ASSERT ( bone_id < bone_count ) ;
int32_t & active_bone = bone_to_active_bone_mapping [ bone_id ] ;
if ( active_bone ! = - 1 ) {
active_bounds [ active_bone ] . expand_to ( p ) ;
} else {
// Increment the number of active bones stored.
active_bone = active_bounds . size ( ) ;
active_bounds . resize ( active_bone + 1 ) ;
active_bone_ids . resize ( active_bone + 1 ) ;
// First point for the bone
DEV_ASSERT ( bone_id < = UINT16_MAX ) ;
active_bone_ids [ active_bone ] = bone_id ;
active_bounds [ active_bone ] = Rect2 ( p , Vector2 ( 0.00001 , 0.00001 ) ) ;
}
}
// If some points were not rigged,
// we want to add them directly to an "untransformed bound",
// and merge this with the skinned bound later.
// Also do this if a point is not FULLY weighted,
// because the untransformed position is still having an influence.
if ( ! bone_space | | ( total_weight < 0.99f ) ) {
if ( some_were_untransformed ) {
p_polygon . skinning_data - > untransformed_bound . expand_to ( pp [ n ] ) ;
} else {
// First point
some_were_untransformed = true ;
p_polygon . skinning_data - > untransformed_bound = Rect2 ( pp [ n ] , Vector2 ( ) ) ;
}
}
}
}
Rect2 RasterizerCanvas : : Item : : calculate_polygon_bounds ( const Item : : CommandPolygon & p_polygon ) const {
int num_points = p_polygon . points . size ( ) ;
// If there is no skeleton, or the bones data is invalid...
// Note : Can we check the second more efficiently? by checking if polygon.skinning_data is set perhaps?
if ( skeleton = = RID ( ) | | ! ( num_points & & p_polygon . bones . size ( ) = = num_points * 4 & & p_polygon . weights . size ( ) = = p_polygon . bones . size ( ) ) ) {
// With no skeleton, all points are untransformed.
Rect2 r ;
const Point2 * pp = & p_polygon . points [ 0 ] ;
r . position = pp [ 0 ] ;
for ( int n = 1 ; n < num_points ; n + + ) {
r . expand_to ( pp [ n ] ) ;
}
return r ;
}
// Skinned skeleton is present.
ERR_FAIL_COND_V_MSG ( ! skinning_data , Rect2 ( ) , " Skinned Polygon2D must have skeleton_relative_xform set for correct culling. " ) ;
// Ensure the polygon skinning data is created...
// (This isn't stored on every polygon to save memory).
if ( ! p_polygon . skinning_data ) {
p_polygon . skinning_data = memnew ( Item : : CommandPolygon : : SkinningData ) ;
}
Item : : CommandPolygon : : SkinningData & pdata = * p_polygon . skinning_data ;
// This should only occur when rigging has changed.
// Usually a one off in games.
if ( pdata . dirty ) {
precalculate_polygon_bone_bounds ( p_polygon ) ;
}
// We only deal with the precalculated ACTIVE bone AABBs using the skeleton.
// (No need to bother with bones that are unused for this poly.)
int num_active_bones = pdata . active_bounds . size ( ) ;
if ( ! num_active_bones ) {
return pdata . untransformed_bound ;
}
// No need to make a dynamic allocation here in 99% of cases.
Rect2 * bptr = nullptr ;
LocalVector < Rect2 > bone_aabbs ;
if ( num_active_bones < = 1024 ) {
bptr = ( Rect2 * ) alloca ( sizeof ( Rect2 ) * num_active_bones ) ;
} else {
bone_aabbs . resize ( num_active_bones ) ;
bptr = bone_aabbs . ptr ( ) ;
}
// Copy across the precalculated bone bounds.
memcpy ( bptr , pdata . active_bounds . ptr ( ) , sizeof ( Rect2 ) * num_active_bones ) ;
const Transform2D & item_transform_inv = skinning_data - > skeleton_relative_xform_inv ;
Rect2 aabb ;
bool first_bone = true ;
for ( int n = 0 ; n < num_active_bones ; n + + ) {
int bone_id = pdata . active_bone_ids [ n ] ;
const Transform2D & mtx = RasterizerStorage : : base_singleton - > skeleton_bone_get_transform_2d ( skeleton , bone_id ) ;
Rect2 baabb = mtx . xform ( bptr [ n ] ) ;
if ( first_bone ) {
aabb = baabb ;
first_bone = false ;
} else {
aabb = aabb . merge ( baabb ) ;
}
}
// Transform the polygon AABB back into local space from bone space.
aabb = item_transform_inv . xform ( aabb ) ;
// If some were untransformed...
if ( pdata . untransformed_bound . size . x > = 0 ) {
return pdata . untransformed_bound . merge ( aabb ) ;
}
return aabb ;
}