Skip to content

Commit

Permalink
Fix 2d software skinning relative transforms
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lawnjelly committed May 3, 2021
1 parent c37464b commit f33e220
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 12 deletions.
6 changes: 3 additions & 3 deletions drivers/gles2/rasterizer_canvas_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions drivers/gles3/rasterizer_canvas_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,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) {
Expand Down Expand Up @@ -1602,10 +1602,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);
}
Expand Down
33 changes: 27 additions & 6 deletions drivers/gles_common/rasterizer_canvas_batcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ class RasterizerCanvasBatcher {
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
Expand Down Expand Up @@ -587,7 +588,7 @@ class RasterizerCanvasBatcher {
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
Expand Down Expand Up @@ -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
Expand All @@ -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++) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit f33e220

Please sign in to comment.