1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-27 15:57:02 +00:00

Fix Polygon2D skinned bounds (for culling)

The bound Rect2 was previously incorrect because bone transforms need to be applied to verts in bone space, rather than local space. This was previously resulting in skinned Polygon2Ds being incorrectly culled.
This commit is contained in:
lawnjelly
2023-04-10 16:19:32 +01:00
parent 632a544c6e
commit dd6c213dac
10 changed files with 269 additions and 55 deletions

View File

@@ -892,10 +892,24 @@ public:
bool antialiased;
bool antialiasing_use_indices;
struct SkinningData {
bool dirty = true;
LocalVector<Rect2> active_bounds;
LocalVector<uint16_t> active_bone_ids;
Rect2 untransformed_bound;
};
mutable SkinningData *skinning_data = nullptr;
CommandPolygon() {
type = TYPE_POLYGON;
count = 0;
}
virtual ~CommandPolygon() {
if (skinning_data) {
memdelete(skinning_data);
skinning_data = nullptr;
}
}
};
struct CommandMesh : public Command {
@@ -968,6 +982,12 @@ public:
Item *next;
struct SkinningData {
Transform2D skeleton_relative_xform;
Transform2D skeleton_relative_xform_inv;
};
SkinningData *skinning_data = nullptr;
struct CopyBackBuffer {
Rect2 rect;
Rect2 screen_rect;
@@ -984,6 +1004,11 @@ public:
Rect2 global_rect_cache;
private:
Rect2 calculate_polygon_bounds(const Item::CommandPolygon &p_polygon) const;
void precalculate_polygon_bone_bounds(const Item::CommandPolygon &p_polygon) const;
public:
const Rect2 &get_rect() const {
if (custom_rect) {
return rect;
@@ -1068,61 +1093,8 @@ public:
} break;
case Item::Command::TYPE_POLYGON: {
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
int l = polygon->points.size();
const Point2 *pp = &polygon->points[0];
r.position = pp[0];
for (int j = 1; j < l; j++) {
r.expand_to(pp[j]);
}
if (skeleton != RID()) {
// calculate bone AABBs
int bone_count = RasterizerStorage::base_singleton->skeleton_get_bone_count(skeleton);
Vector<Rect2> bone_aabbs;
bone_aabbs.resize(bone_count);
Rect2 *bptr = bone_aabbs.ptrw();
for (int j = 0; j < bone_count; j++) {
bptr[j].size = Vector2(-1, -1); //negative means unused
}
if (l && polygon->bones.size() == l * 4 && polygon->weights.size() == polygon->bones.size()) {
for (int j = 0; j < l; j++) {
Point2 p = pp[j];
for (int k = 0; k < 4; k++) {
int idx = polygon->bones[j * 4 + k];
float w = polygon->weights[j * 4 + k];
if (w == 0) {
continue;
}
if (bptr[idx].size.x < 0) {
//first
bptr[idx] = Rect2(p, Vector2(0.00001, 0.00001));
} else {
bptr[idx].expand_to(p);
}
}
}
Rect2 aabb;
bool first_bone = true;
for (int j = 0; j < bone_count; j++) {
Transform2D mtx = RasterizerStorage::base_singleton->skeleton_bone_get_transform_2d(skeleton, j);
Rect2 baabb = mtx.xform(bone_aabbs[j]);
if (first_bone) {
aabb = baabb;
first_bone = false;
} else {
aabb = aabb.merge(baabb);
}
}
r = r.merge(aabb);
}
}
DEV_ASSERT(polygon);
r = calculate_polygon_bounds(*polygon);
} break;
case Item::Command::TYPE_MESH: {
const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
@@ -1188,6 +1160,11 @@ public:
final_clip_owner = nullptr;
material_owner = nullptr;
light_masked = false;
if (skinning_data) {
memdelete(skinning_data);
skinning_data = nullptr;
}
}
Item() {
light_mask = 1;