Fix 2d software skinning relative transforms

All my earlier test cases for software skinning had the polys parent transform to be identity. This works fine until you had cases where the user had moved the transform of the parent nodes of skinned polys.

This PR fixes this situation by taking into account the final (concatenated) transform of the polys RELATIVE to the skeleton base transform. It does this by applying the inverse skeleton base transform to the poly final transform.

(cherry picked from commit f33e22001f)
This commit is contained in:
lawnjelly 2021-05-03 12:18:56 +01:00 committed by Rémi Verschelde
parent 87aa694ae5
commit dacd16fd33
No known key found for this signature in database
GPG Key ID: C3336907360768E1
3 changed files with 33 additions and 12 deletions

View File

@ -2194,7 +2194,7 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
_set_uniforms();
if (unshaded || (state.uniforms.final_modulate.a > 0.001 && (!r_ris.shader_cache || r_ris.shader_cache->canvas_item.light_mode != RasterizerStorageGLES2::Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY) && !ci->light_masked))
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, false);
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, false, r_ris);
r_ris.rebind_shader = true; // hacked in for now.
@ -2288,10 +2288,10 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
// this can greatly reduce fill rate ..
// at the cost of glScissor commands, so is optional
if (!bdata.settings_scissor_lights || r_ris.current_clip) {
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, true);
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, true, r_ris);
} else {
bool scissor = _light_scissor_begin(p_bij.bounding_rect, light->xform_cache, light->rect_cache);
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, true);
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, true, r_ris);
if (scissor) {
glDisable(GL_SCISSOR_TEST);
}

View File

@ -1485,7 +1485,7 @@ void RasterizerCanvasGLES3::render_joined_item(const BItemJoined &p_bij, RenderI
}
if (unshaded || (state.canvas_item_modulate.a > 0.001 && (!r_ris.shader_cache || r_ris.shader_cache->canvas_item.light_mode != RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY) && !p_ci->light_masked)) {
RasterizerStorageGLES3::Material *material_ptr = nullptr;
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, false);
render_joined_item_commands(p_bij, NULL, reclip, material_ptr, false, r_ris);
}
if ((blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX || blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_PMALPHA) && r_ris.item_group_light && !unshaded) {
@ -1600,10 +1600,10 @@ void RasterizerCanvasGLES3::render_joined_item(const BItemJoined &p_bij, RenderI
// this can greatly reduce fill rate ..
// at the cost of glScissor commands, so is optional
if (!bdata.settings_scissor_lights || r_ris.current_clip) {
render_joined_item_commands(p_bij, NULL, reclip, nullptr, true);
render_joined_item_commands(p_bij, NULL, reclip, nullptr, true, r_ris);
} else {
bool scissor = _light_scissor_begin(p_bij.bounding_rect, light->xform_cache, light->rect_cache);
render_joined_item_commands(p_bij, NULL, reclip, nullptr, true);
render_joined_item_commands(p_bij, NULL, reclip, nullptr, true, r_ris);
if (scissor) {
glDisable(GL_SCISSOR_TEST);
}

View File

@ -504,6 +504,7 @@ public:
bool extra_matrix_sent; // whether sent on this item (in which case sofware transform can't be used untl end of item)
int transform_extra_command_number_p1; // plus one to allow fast checking against zero
Transform2D transform_combined; // final * extra
Transform2D skeleton_base_inverse_xform; // used in software skinning
};
// used during try_join
@ -587,7 +588,7 @@ protected:
bool _detect_item_batch_break(RenderItemState &r_ris, RasterizerCanvas::Item *p_ci, bool &r_batch_break);
// drives the loop filling batches and flushing
void render_joined_item_commands(const BItemJoined &p_bij, RasterizerCanvas::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit);
void render_joined_item_commands(const BItemJoined &p_bij, RasterizerCanvas::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit, const RenderItemState &p_ris);
private:
// flush once full or end of joined item
@ -1824,9 +1825,12 @@ PREAMBLE(bool)::_software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_po
Vector2 *pTemps = (Vector2 *)alloca(num_verts * sizeof(Vector2));
memset((void *)pTemps, 0, num_verts * sizeof(Vector2));
// these are used in the shader but don't appear to be needed for software transform
// const Transform2D &skel_trans = get_this()->state.skeleton_transform;
// const Transform2D &skel_trans_inv = get_this()->state.skeleton_transform_inverse;
// only the inverse appears to be needed
const Transform2D &skel_trans_inv = p_fill_state.skeleton_base_inverse_xform;
// we can't get this from the state, because more than one skeleton item may have been joined together..
// we need to handle the base skeleton on a per item basis as the joined item is rendered.
// const Transform2D &skel_trans = get_this()->state.skeleton_transform;
// const Transform2D &skel_trans_inv = get_this()->state.skeleton_transform_inverse;
// get the bone transforms.
// this is not ideal because we don't know in advance which bones are needed
@ -1838,7 +1842,10 @@ PREAMBLE(bool)::_software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_po
if (num_verts && (p_poly->bones.size() == num_verts * 4) && (p_poly->weights.size() == p_poly->bones.size())) {
const Transform2D &item_transform = p_item->xform;
// instead of using the p_item->xform we use the final transform,
// because we want the poly transform RELATIVE to the base skeleton.
Transform2D item_transform = skel_trans_inv * p_item->final_transform;
Transform2D item_transform_inv = item_transform.affine_inverse();
for (int n = 0; n < num_verts; n++) {
@ -2534,7 +2541,7 @@ PREAMBLE(void)::flush_render_batches(RasterizerCanvas::Item *p_first_item, Raste
#endif
}
PREAMBLE(void)::render_joined_item_commands(const BItemJoined &p_bij, RasterizerCanvas::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit) {
PREAMBLE(void)::render_joined_item_commands(const BItemJoined &p_bij, RasterizerCanvas::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit, const RenderItemState &p_ris) {
RasterizerCanvas::Item *item = 0;
RasterizerCanvas::Item *first_item = bdata.item_refs[p_bij.first_item_ref].item;
@ -2581,6 +2588,20 @@ PREAMBLE(void)::render_joined_item_commands(const BItemJoined &p_bij, Rasterizer
// prefill_joined_item()
fill_state.transform_combined = item->final_transform;
// calculate skeleton base inverse transform if required for software skinning
// put in the fill state as this is readily accessible from the software skinner
if (item->skeleton.is_valid() && bdata.settings_use_software_skinning && get_storage()->skeleton_owner.owns(item->skeleton)) {
typename T_STORAGE::Skeleton *skeleton = nullptr;
skeleton = get_storage()->skeleton_owner.get(item->skeleton);
if (skeleton->use_2d) {
// with software skinning we still need to know the skeleton inverse transform, the other two aren't needed
// but are left in for simplicity here
Transform2D skeleton_transform = p_ris.item_group_base_transform * skeleton->base_transform_2d;
fill_state.skeleton_base_inverse_xform = skeleton_transform.affine_inverse();
}
}
// decide the initial transform mode, and make a backup
// in orig_transform_mode in case we need to switch back
if (fill_state.use_software_transform) {