mirror of
https://github.com/Relintai/pandemonium_engine_minimal.git
synced 2024-12-21 16:56:50 +01:00
Removed the skeleton api.
This commit is contained in:
parent
037bbc6cb5
commit
3a034b011c
@ -361,12 +361,6 @@ public:
|
||||
|
||||
return m->surfaces[p_surface].blend_shapes;
|
||||
}
|
||||
Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
|
||||
DummyMesh *m = mesh_owner.getornull(p_mesh);
|
||||
ERR_FAIL_COND_V(!m, Vector<AABB>());
|
||||
|
||||
return m->surfaces[p_surface].bone_aabbs;
|
||||
}
|
||||
|
||||
void mesh_remove_surface(RID p_mesh, int p_index) {
|
||||
DummyMesh *m = mesh_owner.getornull(p_mesh);
|
||||
@ -384,7 +378,7 @@ public:
|
||||
void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) {}
|
||||
AABB mesh_get_custom_aabb(RID p_mesh) const { return AABB(); }
|
||||
|
||||
AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const { return AABB(); }
|
||||
AABB mesh_get_aabb(RID p_mesh) const { return AABB(); }
|
||||
void mesh_clear(RID p_mesh) {}
|
||||
|
||||
/* MULTIMESH API */
|
||||
@ -427,20 +421,6 @@ public:
|
||||
RID immediate_get_material(RID p_immediate) const { return RID(); }
|
||||
AABB immediate_get_aabb(RID p_immediate) const { return AABB(); }
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
RID skeleton_create() { return RID(); }
|
||||
void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) {}
|
||||
void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {}
|
||||
void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {}
|
||||
int skeleton_get_bone_count(RID p_skeleton) const { return 0; }
|
||||
void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) {}
|
||||
Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { return Transform(); }
|
||||
void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {}
|
||||
Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { return Transform2D(); }
|
||||
uint32_t skeleton_get_revision(RID p_skeleton) const { return 0; }
|
||||
void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) {}
|
||||
|
||||
/* Light API */
|
||||
|
||||
RID light_create(RS::LightType p_type) { return RID(); }
|
||||
|
@ -149,7 +149,6 @@ void RasterizerCanvasBaseGLES2::canvas_end() {
|
||||
}
|
||||
|
||||
state.using_texture_rect = false;
|
||||
state.using_skeleton = false;
|
||||
state.using_ninepatch = false;
|
||||
state.using_transparent_rt = false;
|
||||
}
|
||||
@ -344,12 +343,6 @@ void RasterizerCanvasBaseGLES2::_set_uniforms() {
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
|
||||
}
|
||||
|
||||
if (state.using_skeleton) {
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SKELETON_TRANSFORM, state.skeleton_transform);
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SKELETON_TRANSFORM_INVERSE, state.skeleton_transform_inverse);
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SKELETON_TEXTURE_SIZE, state.skeleton_texture_size);
|
||||
}
|
||||
|
||||
if (state.using_light) {
|
||||
Light *light = state.using_light;
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::LIGHT_MATRIX, light->light_shader_xform);
|
||||
@ -1053,7 +1046,6 @@ void RasterizerCanvasBaseGLES2::initialize() {
|
||||
|
||||
state.using_light = nullptr;
|
||||
state.using_transparent_rt = false;
|
||||
state.using_skeleton = false;
|
||||
}
|
||||
|
||||
void RasterizerCanvasBaseGLES2::finalize() {
|
||||
|
@ -83,11 +83,6 @@ public:
|
||||
bool using_large_vertex;
|
||||
|
||||
bool using_ninepatch;
|
||||
bool using_skeleton;
|
||||
|
||||
Transform2D skeleton_transform;
|
||||
Transform2D skeleton_transform_inverse;
|
||||
Size2i skeleton_texture_size;
|
||||
|
||||
RID current_tex;
|
||||
RID current_normal;
|
||||
|
@ -1294,45 +1294,6 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
|
||||
join = false;
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Skeleton *skeleton = nullptr;
|
||||
|
||||
{
|
||||
//skeleton handling
|
||||
if (p_ci->skeleton.is_valid() && storage->skeleton_owner.owns(p_ci->skeleton)) {
|
||||
skeleton = storage->skeleton_owner.get(p_ci->skeleton);
|
||||
if (!skeleton->use_2d) {
|
||||
skeleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool skeleton_prevent_join = false;
|
||||
|
||||
bool use_skeleton = skeleton != nullptr;
|
||||
if (r_ris.prev_use_skeleton != use_skeleton) {
|
||||
if (!bdata.settings_use_software_skinning) {
|
||||
r_ris.rebind_shader = true;
|
||||
}
|
||||
|
||||
r_ris.prev_use_skeleton = use_skeleton;
|
||||
// join = false;
|
||||
skeleton_prevent_join = true;
|
||||
}
|
||||
|
||||
if (skeleton) {
|
||||
// join = false;
|
||||
skeleton_prevent_join = true;
|
||||
state.using_skeleton = true;
|
||||
} else {
|
||||
state.using_skeleton = false;
|
||||
}
|
||||
|
||||
if (skeleton_prevent_join) {
|
||||
if (!bdata.settings_use_software_skinning) {
|
||||
join = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item *material_owner = p_ci->material_owner ? p_ci->material_owner : p_ci;
|
||||
|
||||
RID material = material_owner->material;
|
||||
@ -1594,37 +1555,6 @@ void RasterizerCanvasGLES2::_legacy_canvas_render_item(Item *p_ci, RenderItemSta
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Skeleton *skeleton = nullptr;
|
||||
|
||||
{
|
||||
//skeleton handling
|
||||
if (p_ci->skeleton.is_valid() && storage->skeleton_owner.owns(p_ci->skeleton)) {
|
||||
skeleton = storage->skeleton_owner.get(p_ci->skeleton);
|
||||
if (!skeleton->use_2d) {
|
||||
skeleton = nullptr;
|
||||
} else {
|
||||
state.skeleton_transform = r_ris.item_group_base_transform * skeleton->base_transform_2d;
|
||||
state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse();
|
||||
state.skeleton_texture_size = Vector2(skeleton->size * 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool use_skeleton = skeleton != nullptr;
|
||||
if (r_ris.prev_use_skeleton != use_skeleton) {
|
||||
r_ris.rebind_shader = true;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, use_skeleton);
|
||||
r_ris.prev_use_skeleton = use_skeleton;
|
||||
}
|
||||
|
||||
if (skeleton) {
|
||||
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
|
||||
glBindTexture(GL_TEXTURE_2D, skeleton->tex_id);
|
||||
state.using_skeleton = true;
|
||||
} else {
|
||||
state.using_skeleton = false;
|
||||
}
|
||||
}
|
||||
|
||||
Item *material_owner = p_ci->material_owner ? p_ci->material_owner : p_ci;
|
||||
|
||||
RID material = material_owner->material;
|
||||
@ -1958,38 +1888,6 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
|
||||
}
|
||||
}
|
||||
|
||||
if (!bdata.settings_use_batching || !bdata.settings_use_software_skinning) {
|
||||
RasterizerStorageGLES2::Skeleton *skeleton = nullptr;
|
||||
|
||||
//skeleton handling
|
||||
if (ci->skeleton.is_valid() && storage->skeleton_owner.owns(ci->skeleton)) {
|
||||
skeleton = storage->skeleton_owner.get(ci->skeleton);
|
||||
if (!skeleton->use_2d) {
|
||||
skeleton = nullptr;
|
||||
} else {
|
||||
state.skeleton_transform = r_ris.item_group_base_transform * skeleton->base_transform_2d;
|
||||
state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse();
|
||||
state.skeleton_texture_size = Vector2(skeleton->size * 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool use_skeleton = skeleton != nullptr;
|
||||
if (r_ris.prev_use_skeleton != use_skeleton) {
|
||||
r_ris.rebind_shader = true;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, use_skeleton);
|
||||
r_ris.prev_use_skeleton = use_skeleton;
|
||||
}
|
||||
|
||||
if (skeleton) {
|
||||
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
|
||||
glBindTexture(GL_TEXTURE_2D, skeleton->tex_id);
|
||||
state.using_skeleton = true;
|
||||
} else {
|
||||
state.using_skeleton = false;
|
||||
}
|
||||
|
||||
} // if not using batching
|
||||
|
||||
Item *material_owner = ci->material_owner ? ci->material_owner : ci;
|
||||
|
||||
RID material = material_owner->material;
|
||||
|
@ -1257,7 +1257,7 @@ bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_m
|
||||
return shader_rebind;
|
||||
}
|
||||
|
||||
void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) {
|
||||
void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element) {
|
||||
switch (p_element->instance->base_type) {
|
||||
case RS::INSTANCE_MESH: {
|
||||
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
|
||||
@ -1308,134 +1308,6 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste
|
||||
}
|
||||
}
|
||||
|
||||
bool clear_skeleton_buffer = storage->config.use_skeleton_software;
|
||||
|
||||
if (p_skeleton) {
|
||||
if (!storage->config.use_skeleton_software) {
|
||||
//use float texture workflow
|
||||
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id);
|
||||
} else {
|
||||
//use transform buffer workflow
|
||||
ERR_FAIL_COND(p_skeleton->use_2d);
|
||||
|
||||
PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer;
|
||||
|
||||
if (!s->attribs[RS::ARRAY_BONES].enabled || !s->attribs[RS::ARRAY_WEIGHTS].enabled) {
|
||||
break; // the whole instance has a skeleton, but this surface is not affected by it.
|
||||
}
|
||||
|
||||
// 3 * vec4 per vertex
|
||||
if (transform_buffer.size() < s->array_len * 12) {
|
||||
transform_buffer.resize(s->array_len * 12);
|
||||
}
|
||||
|
||||
const size_t bones_offset = s->attribs[RS::ARRAY_BONES].offset;
|
||||
const size_t bones_stride = s->attribs[RS::ARRAY_BONES].stride;
|
||||
const size_t bone_weight_offset = s->attribs[RS::ARRAY_WEIGHTS].offset;
|
||||
const size_t bone_weight_stride = s->attribs[RS::ARRAY_WEIGHTS].stride;
|
||||
|
||||
{
|
||||
PoolVector<float>::Write write = transform_buffer.write();
|
||||
float *buffer = write.ptr();
|
||||
|
||||
PoolVector<uint8_t>::Read vertex_array_read = s->data.read();
|
||||
const uint8_t *vertex_data = vertex_array_read.ptr();
|
||||
|
||||
for (int i = 0; i < s->array_len; i++) {
|
||||
// do magic
|
||||
|
||||
size_t bones[4];
|
||||
float bone_weight[4];
|
||||
|
||||
if (s->attribs[RS::ARRAY_BONES].type == GL_UNSIGNED_BYTE) {
|
||||
// read as byte
|
||||
const uint8_t *bones_ptr = vertex_data + bones_offset + (i * bones_stride);
|
||||
bones[0] = bones_ptr[0];
|
||||
bones[1] = bones_ptr[1];
|
||||
bones[2] = bones_ptr[2];
|
||||
bones[3] = bones_ptr[3];
|
||||
} else {
|
||||
// read as short
|
||||
const uint16_t *bones_ptr = (const uint16_t *)(vertex_data + bones_offset + (i * bones_stride));
|
||||
bones[0] = bones_ptr[0];
|
||||
bones[1] = bones_ptr[1];
|
||||
bones[2] = bones_ptr[2];
|
||||
bones[3] = bones_ptr[3];
|
||||
}
|
||||
|
||||
if (s->attribs[RS::ARRAY_WEIGHTS].type == GL_FLOAT) {
|
||||
// read as float
|
||||
const float *weight_ptr = (const float *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
|
||||
bone_weight[0] = weight_ptr[0];
|
||||
bone_weight[1] = weight_ptr[1];
|
||||
bone_weight[2] = weight_ptr[2];
|
||||
bone_weight[3] = weight_ptr[3];
|
||||
} else {
|
||||
// read as half
|
||||
const uint16_t *weight_ptr = (const uint16_t *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
|
||||
bone_weight[0] = (weight_ptr[0] / (float)0xFFFF);
|
||||
bone_weight[1] = (weight_ptr[1] / (float)0xFFFF);
|
||||
bone_weight[2] = (weight_ptr[2] / (float)0xFFFF);
|
||||
bone_weight[3] = (weight_ptr[3] / (float)0xFFFF);
|
||||
}
|
||||
|
||||
Transform transform;
|
||||
|
||||
Transform bone_transforms[4] = {
|
||||
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[0]),
|
||||
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[1]),
|
||||
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[2]),
|
||||
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[3]),
|
||||
};
|
||||
|
||||
transform.origin =
|
||||
bone_weight[0] * bone_transforms[0].origin +
|
||||
bone_weight[1] * bone_transforms[1].origin +
|
||||
bone_weight[2] * bone_transforms[2].origin +
|
||||
bone_weight[3] * bone_transforms[3].origin;
|
||||
|
||||
transform.basis =
|
||||
bone_transforms[0].basis * bone_weight[0] +
|
||||
bone_transforms[1].basis * bone_weight[1] +
|
||||
bone_transforms[2].basis * bone_weight[2] +
|
||||
bone_transforms[3].basis * bone_weight[3];
|
||||
|
||||
float row[3][4] = {
|
||||
{ transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
|
||||
{ transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
|
||||
{ transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
|
||||
};
|
||||
|
||||
size_t transform_buffer_offset = i * 12;
|
||||
|
||||
memcpy(&buffer[transform_buffer_offset], row, sizeof(row));
|
||||
}
|
||||
}
|
||||
|
||||
storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12);
|
||||
|
||||
//enable transform buffer and bind it
|
||||
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
|
||||
|
||||
glEnableVertexAttribArray(INSTANCE_BONE_BASE + 0);
|
||||
glEnableVertexAttribArray(INSTANCE_BONE_BASE + 1);
|
||||
glEnableVertexAttribArray(INSTANCE_BONE_BASE + 2);
|
||||
|
||||
glVertexAttribPointer(INSTANCE_BONE_BASE + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
|
||||
glVertexAttribPointer(INSTANCE_BONE_BASE + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
|
||||
glVertexAttribPointer(INSTANCE_BONE_BASE + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
|
||||
|
||||
clear_skeleton_buffer = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (clear_skeleton_buffer) {
|
||||
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0);
|
||||
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1);
|
||||
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case RS::INSTANCE_MULTIMESH: {
|
||||
@ -2057,7 +1929,6 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
|
||||
RasterizerStorageGLES2::Material *prev_material = nullptr;
|
||||
RasterizerStorageGLES2::Geometry *prev_geometry = nullptr;
|
||||
RasterizerStorageGLES2::Skeleton *prev_skeleton = nullptr;
|
||||
RasterizerStorageGLES2::GeometryOwner *prev_owner = nullptr;
|
||||
|
||||
bool prev_octahedral_compression = false;
|
||||
@ -2243,23 +2114,11 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
|
||||
rebind = true;
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, false);
|
||||
|
||||
if (skeleton != prev_skeleton) {
|
||||
if ((prev_skeleton == nullptr) != (skeleton == nullptr)) {
|
||||
if (skeleton) {
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, true);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, storage->config.use_skeleton_software);
|
||||
} else {
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, false);
|
||||
}
|
||||
}
|
||||
rebind = true;
|
||||
}
|
||||
|
||||
if (e->owner != prev_owner || e->geometry != prev_geometry || skeleton != prev_skeleton) {
|
||||
_setup_geometry(e, skeleton);
|
||||
if (e->owner != prev_owner || e->geometry != prev_geometry) {
|
||||
_setup_geometry(e);
|
||||
storage->info.render.surface_switch_count++;
|
||||
}
|
||||
|
||||
@ -2276,7 +2135,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
|
||||
bool shader_rebind = false;
|
||||
if (rebind || material != prev_material) {
|
||||
storage->info.render.material_switch_count++;
|
||||
shader_rebind = _setup_material(material, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
|
||||
shader_rebind = _setup_material(material, p_alpha_pass, Size2i(0, 0));
|
||||
if (shader_rebind) {
|
||||
storage->info.render.shader_rebind_count++;
|
||||
}
|
||||
@ -2333,7 +2192,6 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
|
||||
prev_geometry = e->geometry;
|
||||
prev_owner = e->owner;
|
||||
prev_material = material;
|
||||
prev_skeleton = skeleton;
|
||||
prev_instancing = instancing;
|
||||
prev_octahedral_compression = octahedral_compression;
|
||||
prev_light = light;
|
||||
@ -2427,8 +2285,6 @@ void RasterizerSceneGLES2::_post_process(const Projection &p_cam_projection) {
|
||||
//3) Bloom (Glow) //only on desktop
|
||||
//4) Adjustments
|
||||
|
||||
|
||||
|
||||
//Adjustments
|
||||
|
||||
state.tonemap_shader.set_conditional(TonemapShaderGLES2::DISABLE_ALPHA, !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]);
|
||||
|
@ -592,7 +592,7 @@ public:
|
||||
|
||||
_FORCE_INLINE_ void _set_cull(bool p_front, bool p_disabled, bool p_reverse_cull);
|
||||
_FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES2::Material *p_material, bool p_alpha_pass, Size2i p_skeleton_tex_size = Size2i(0, 0));
|
||||
_FORCE_INLINE_ void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton);
|
||||
_FORCE_INLINE_ void _setup_geometry(RenderList::Element *p_element);
|
||||
_FORCE_INLINE_ void _setup_light_type(LightInstance *p_light, ShadowAtlas *shadow_atlas);
|
||||
_FORCE_INLINE_ void _setup_light(LightInstance *p_light, ShadowAtlas *shadow_atlas, const Transform &p_view_transform, bool accum_pass);
|
||||
_FORCE_INLINE_ void _setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform);
|
||||
|
@ -2388,8 +2388,6 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, RS:
|
||||
surface->primitive = p_primitive;
|
||||
surface->mesh = mesh;
|
||||
surface->format = p_format;
|
||||
surface->skeleton_bone_aabb = p_bone_aabbs;
|
||||
surface->skeleton_bone_used.resize(surface->skeleton_bone_aabb.size());
|
||||
|
||||
surface->aabb = p_aabb;
|
||||
surface->max_bone = p_bone_aabbs.size();
|
||||
@ -2399,10 +2397,6 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, RS:
|
||||
surface->index_data = p_index_array;
|
||||
surface->total_data_size += surface->array_byte_size + surface->index_array_byte_size;
|
||||
|
||||
for (int i = 0; i < surface->skeleton_bone_used.size(); i++) {
|
||||
surface->skeleton_bone_used.write[i] = !(surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RS::ARRAY_MAX; i++) {
|
||||
surface->attribs[i] = attribs[i];
|
||||
}
|
||||
@ -2611,13 +2605,6 @@ Vector<PoolVector<uint8_t>> RasterizerStorageGLES2::mesh_surface_get_blend_shape
|
||||
|
||||
return mesh->surfaces[p_surface]->blend_shape_data;
|
||||
}
|
||||
Vector<AABB> RasterizerStorageGLES2::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
|
||||
const Mesh *mesh = mesh_owner.getornull(p_mesh);
|
||||
ERR_FAIL_COND_V(!mesh, Vector<AABB>());
|
||||
ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<AABB>());
|
||||
|
||||
return mesh->surfaces[p_surface]->skeleton_bone_aabb;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::mesh_remove_surface(RID p_mesh, int p_surface) {
|
||||
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
||||
@ -2667,7 +2654,7 @@ AABB RasterizerStorageGLES2::mesh_get_custom_aabb(RID p_mesh) const {
|
||||
return mesh->custom_aabb;
|
||||
}
|
||||
|
||||
AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
|
||||
AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh) const {
|
||||
Mesh *mesh = mesh_owner.get(p_mesh);
|
||||
ERR_FAIL_COND_V(!mesh, AABB());
|
||||
|
||||
@ -2675,105 +2662,13 @@ AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
|
||||
return mesh->custom_aabb;
|
||||
}
|
||||
|
||||
Skeleton *sk = nullptr;
|
||||
if (p_skeleton.is_valid()) {
|
||||
sk = skeleton_owner.get(p_skeleton);
|
||||
}
|
||||
|
||||
AABB aabb;
|
||||
|
||||
if (sk && sk->size != 0) {
|
||||
for (int i = 0; i < mesh->surfaces.size(); i++) {
|
||||
AABB laabb;
|
||||
if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->skeleton_bone_aabb.size()) {
|
||||
int bs = mesh->surfaces[i]->skeleton_bone_aabb.size();
|
||||
const AABB *skbones = mesh->surfaces[i]->skeleton_bone_aabb.ptr();
|
||||
const bool *skused = mesh->surfaces[i]->skeleton_bone_used.ptr();
|
||||
|
||||
int sbs = sk->size;
|
||||
ERR_CONTINUE(bs > sbs);
|
||||
const float *texture = sk->bone_data.ptr();
|
||||
|
||||
bool first = true;
|
||||
if (sk->use_2d) {
|
||||
for (int j = 0; j < bs; j++) {
|
||||
if (!skused[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int base_ofs = j * 2 * 4;
|
||||
|
||||
Transform mtx;
|
||||
|
||||
mtx.basis[0].x = texture[base_ofs + 0];
|
||||
mtx.basis[0].y = texture[base_ofs + 1];
|
||||
mtx.origin.x = texture[base_ofs + 3];
|
||||
base_ofs += 4;
|
||||
mtx.basis[1].x = texture[base_ofs + 0];
|
||||
mtx.basis[1].y = texture[base_ofs + 1];
|
||||
mtx.origin.y = texture[base_ofs + 3];
|
||||
|
||||
AABB baabb = mtx.xform(skbones[j]);
|
||||
|
||||
if (first) {
|
||||
laabb = baabb;
|
||||
first = false;
|
||||
} else {
|
||||
laabb.merge_with(baabb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < bs; j++) {
|
||||
if (!skused[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int base_ofs = j * 3 * 4;
|
||||
|
||||
Transform mtx;
|
||||
|
||||
mtx.basis[0].x = texture[base_ofs + 0];
|
||||
mtx.basis[0].y = texture[base_ofs + 1];
|
||||
mtx.basis[0].z = texture[base_ofs + 2];
|
||||
mtx.origin.x = texture[base_ofs + 3];
|
||||
base_ofs += 4;
|
||||
mtx.basis[1].x = texture[base_ofs + 0];
|
||||
mtx.basis[1].y = texture[base_ofs + 1];
|
||||
mtx.basis[1].z = texture[base_ofs + 2];
|
||||
mtx.origin.y = texture[base_ofs + 3];
|
||||
base_ofs += 4;
|
||||
mtx.basis[2].x = texture[base_ofs + 0];
|
||||
mtx.basis[2].y = texture[base_ofs + 1];
|
||||
mtx.basis[2].z = texture[base_ofs + 2];
|
||||
mtx.origin.z = texture[base_ofs + 3];
|
||||
|
||||
AABB baabb = mtx.xform(skbones[j]);
|
||||
if (first) {
|
||||
laabb = baabb;
|
||||
first = false;
|
||||
} else {
|
||||
laabb.merge_with(baabb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
laabb = mesh->surfaces[i]->aabb;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
aabb = laabb;
|
||||
} else {
|
||||
aabb.merge_with(laabb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < mesh->surfaces.size(); i++) {
|
||||
if (i == 0) {
|
||||
aabb = mesh->surfaces[i]->aabb;
|
||||
} else {
|
||||
aabb.merge_with(mesh->surfaces[i]->aabb);
|
||||
}
|
||||
for (int i = 0; i < mesh->surfaces.size(); i++) {
|
||||
if (i == 0) {
|
||||
aabb = mesh->surfaces[i]->aabb;
|
||||
} else {
|
||||
aabb.merge_with(mesh->surfaces[i]->aabb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3265,7 +3160,7 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() {
|
||||
AABB mesh_aabb;
|
||||
|
||||
if (multimesh->mesh.is_valid()) {
|
||||
mesh_aabb = mesh_get_aabb(multimesh->mesh, RID());
|
||||
mesh_aabb = mesh_get_aabb(multimesh->mesh);
|
||||
}
|
||||
|
||||
mesh_aabb.size += Vector3(0.001, 0.001, 0.001); //in case mesh is empty in one of the sides
|
||||
@ -3481,205 +3376,6 @@ RID RasterizerStorageGLES2::immediate_get_material(RID p_immediate) const {
|
||||
return im->material;
|
||||
}
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
RID RasterizerStorageGLES2::skeleton_create() {
|
||||
Skeleton *skeleton = memnew(Skeleton);
|
||||
|
||||
glGenTextures(1, &skeleton->tex_id);
|
||||
|
||||
return skeleton_owner.make_rid(skeleton);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
ERR_FAIL_COND(p_bones < 0);
|
||||
|
||||
if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
skeleton->size = p_bones;
|
||||
skeleton->use_2d = p_2d_skeleton;
|
||||
|
||||
if (!config.use_skeleton_software) {
|
||||
gl_wrapper.gl_active_texture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, skeleton->tex_id);
|
||||
|
||||
#ifdef GLES_OVER_GL
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, p_bones * (skeleton->use_2d ? 2 : 3), 1, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p_bones * (skeleton->use_2d ? 2 : 3), 1, 0, GL_RGBA, GL_FLOAT, NULL);
|
||||
#endif
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
if (skeleton->use_2d) {
|
||||
skeleton->bone_data.resize(p_bones * 4 * 2);
|
||||
} else {
|
||||
skeleton->bone_data.resize(p_bones * 4 * 3);
|
||||
}
|
||||
}
|
||||
|
||||
int RasterizerStorageGLES2::skeleton_get_bone_count(RID p_skeleton) const {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND_V(!skeleton, 0);
|
||||
|
||||
return skeleton->size;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
|
||||
ERR_FAIL_INDEX(p_bone, skeleton->size);
|
||||
ERR_FAIL_COND(skeleton->use_2d);
|
||||
|
||||
float *bone_data = skeleton->bone_data.ptrw();
|
||||
|
||||
int base_offset = p_bone * 4 * 3;
|
||||
|
||||
bone_data[base_offset + 0] = p_transform.basis[0].x;
|
||||
bone_data[base_offset + 1] = p_transform.basis[0].y;
|
||||
bone_data[base_offset + 2] = p_transform.basis[0].z;
|
||||
bone_data[base_offset + 3] = p_transform.origin.x;
|
||||
|
||||
bone_data[base_offset + 4] = p_transform.basis[1].x;
|
||||
bone_data[base_offset + 5] = p_transform.basis[1].y;
|
||||
bone_data[base_offset + 6] = p_transform.basis[1].z;
|
||||
bone_data[base_offset + 7] = p_transform.origin.y;
|
||||
|
||||
bone_data[base_offset + 8] = p_transform.basis[2].x;
|
||||
bone_data[base_offset + 9] = p_transform.basis[2].y;
|
||||
bone_data[base_offset + 10] = p_transform.basis[2].z;
|
||||
bone_data[base_offset + 11] = p_transform.origin.z;
|
||||
|
||||
if (!skeleton->update_list.in_list()) {
|
||||
skeleton_update_list.add(&skeleton->update_list);
|
||||
}
|
||||
}
|
||||
|
||||
Transform RasterizerStorageGLES2::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND_V(!skeleton, Transform());
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform());
|
||||
ERR_FAIL_COND_V(skeleton->use_2d, Transform());
|
||||
|
||||
const float *bone_data = skeleton->bone_data.ptr();
|
||||
|
||||
Transform ret;
|
||||
|
||||
int base_offset = p_bone * 4 * 3;
|
||||
|
||||
ret.basis[0].x = bone_data[base_offset + 0];
|
||||
ret.basis[0].y = bone_data[base_offset + 1];
|
||||
ret.basis[0].z = bone_data[base_offset + 2];
|
||||
ret.origin.x = bone_data[base_offset + 3];
|
||||
|
||||
ret.basis[1].x = bone_data[base_offset + 4];
|
||||
ret.basis[1].y = bone_data[base_offset + 5];
|
||||
ret.basis[1].z = bone_data[base_offset + 6];
|
||||
ret.origin.y = bone_data[base_offset + 7];
|
||||
|
||||
ret.basis[2].x = bone_data[base_offset + 8];
|
||||
ret.basis[2].y = bone_data[base_offset + 9];
|
||||
ret.basis[2].z = bone_data[base_offset + 10];
|
||||
ret.origin.z = bone_data[base_offset + 11];
|
||||
|
||||
return ret;
|
||||
}
|
||||
void RasterizerStorageGLES2::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
|
||||
ERR_FAIL_INDEX(p_bone, skeleton->size);
|
||||
ERR_FAIL_COND(!skeleton->use_2d);
|
||||
|
||||
float *bone_data = skeleton->bone_data.ptrw();
|
||||
|
||||
int base_offset = p_bone * 4 * 2;
|
||||
|
||||
bone_data[base_offset + 0] = p_transform[0][0];
|
||||
bone_data[base_offset + 1] = p_transform[1][0];
|
||||
bone_data[base_offset + 2] = 0;
|
||||
bone_data[base_offset + 3] = p_transform[2][0];
|
||||
bone_data[base_offset + 4] = p_transform[0][1];
|
||||
bone_data[base_offset + 5] = p_transform[1][1];
|
||||
bone_data[base_offset + 6] = 0;
|
||||
bone_data[base_offset + 7] = p_transform[2][1];
|
||||
|
||||
if (!skeleton->update_list.in_list()) {
|
||||
skeleton_update_list.add(&skeleton->update_list);
|
||||
}
|
||||
|
||||
skeleton->revision++;
|
||||
}
|
||||
|
||||
Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND_V(!skeleton, Transform2D());
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D());
|
||||
ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D());
|
||||
|
||||
const float *bone_data = skeleton->bone_data.ptr();
|
||||
|
||||
Transform2D ret;
|
||||
|
||||
int base_offset = p_bone * 4 * 2;
|
||||
|
||||
ret[0][0] = bone_data[base_offset + 0];
|
||||
ret[1][0] = bone_data[base_offset + 1];
|
||||
ret[2][0] = bone_data[base_offset + 3];
|
||||
ret[0][1] = bone_data[base_offset + 4];
|
||||
ret[1][1] = bone_data[base_offset + 5];
|
||||
ret[2][1] = bone_data[base_offset + 7];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
|
||||
skeleton->base_transform_2d = p_base_transform;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_NULL(skeleton);
|
||||
ERR_FAIL_COND(!p_canvas_item.is_valid());
|
||||
|
||||
if (p_attach) {
|
||||
#ifdef DEV_ENABLED
|
||||
// skeleton_attach_canvas_item() is not bound,
|
||||
// and checks in canvas_item_attach_skeleton() should prevent this,
|
||||
// but there isn't much harm in a DEV_ENABLED check here.
|
||||
int64_t found = skeleton->linked_canvas_items.find(p_canvas_item);
|
||||
ERR_FAIL_COND(found != -1);
|
||||
#endif
|
||||
|
||||
skeleton->linked_canvas_items.push_back(p_canvas_item);
|
||||
} else {
|
||||
int64_t found = skeleton->linked_canvas_items.find(p_canvas_item);
|
||||
ERR_FAIL_COND(found == -1);
|
||||
skeleton->linked_canvas_items.remove_unordered(found);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RasterizerStorageGLES2::skeleton_get_revision(RID p_skeleton) const {
|
||||
const Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND_V(!skeleton, 0);
|
||||
return skeleton->revision;
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::update_dirty_blend_shapes() {
|
||||
while (blend_shapes_update_list.first()) {
|
||||
Mesh *mesh = blend_shapes_update_list.first()->self();
|
||||
@ -3983,66 +3679,6 @@ void RasterizerStorageGLES2::update_dirty_blend_shapes() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);
|
||||
|
||||
uint32_t buffer_size = p_size * sizeof(float);
|
||||
|
||||
if (p_size > resources.skeleton_transform_buffer_size) {
|
||||
// new requested buffer is bigger, so resizing the GPU buffer
|
||||
|
||||
resources.skeleton_transform_buffer_size = p_size;
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer_size, p_data.read().ptr(), GL_DYNAMIC_DRAW);
|
||||
} else {
|
||||
// this may not be best, it could be better to use glBufferData in both cases.
|
||||
buffer_orphan_and_upload(resources.skeleton_transform_buffer_size * sizeof(float), 0, buffer_size, p_data.read().ptr(), GL_ARRAY_BUFFER, true);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::update_dirty_skeletons() {
|
||||
// 2D Skeletons always need to update the polygons so they
|
||||
// know the bounds have changed.
|
||||
// TODO : Could we have a separate list for 2D only?
|
||||
SelfList<Skeleton> *ele = skeleton_update_list.first();
|
||||
|
||||
while (ele) {
|
||||
Skeleton *skeleton = ele->self();
|
||||
|
||||
int num_linked = skeleton->linked_canvas_items.size();
|
||||
for (int n = 0; n < num_linked; n++) {
|
||||
const RID &rid = skeleton->linked_canvas_items[n];
|
||||
RSG::canvas->_canvas_item_invalidate_local_bound(rid);
|
||||
}
|
||||
|
||||
ele = ele->next();
|
||||
}
|
||||
|
||||
if (config.use_skeleton_software) {
|
||||
return;
|
||||
}
|
||||
|
||||
gl_wrapper.gl_active_texture(GL_TEXTURE0);
|
||||
|
||||
while (skeleton_update_list.first()) {
|
||||
Skeleton *skeleton = skeleton_update_list.first()->self();
|
||||
|
||||
if (skeleton->size) {
|
||||
glBindTexture(GL_TEXTURE_2D, skeleton->tex_id);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, skeleton->size * (skeleton->use_2d ? 2 : 3), 1, GL_RGBA, GL_FLOAT, skeleton->bone_data.ptr());
|
||||
}
|
||||
|
||||
for (RBSet<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) {
|
||||
E->get()->base_changed(true, false);
|
||||
}
|
||||
|
||||
skeleton_update_list.remove(skeleton_update_list.first());
|
||||
}
|
||||
}
|
||||
|
||||
/* Light API */
|
||||
|
||||
RID RasterizerStorageGLES2::light_create(RS::LightType p_type) {
|
||||
@ -4471,17 +4107,9 @@ int RasterizerStorageGLES2::reflection_probe_get_resolution(RID p_probe) const {
|
||||
////////
|
||||
|
||||
void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
|
||||
skeleton->instances.insert(p_instance);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::instance_remove_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
|
||||
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
||||
ERR_FAIL_COND(!skeleton);
|
||||
|
||||
skeleton->instances.erase(p_instance);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {
|
||||
@ -5529,27 +5157,6 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
|
||||
material_owner.free(p_rid);
|
||||
memdelete(m);
|
||||
|
||||
return true;
|
||||
} else if (skeleton_owner.owns(p_rid)) {
|
||||
Skeleton *s = skeleton_owner.get(p_rid);
|
||||
|
||||
if (s->update_list.in_list()) {
|
||||
skeleton_update_list.remove(&s->update_list);
|
||||
}
|
||||
|
||||
for (RBSet<RasterizerScene::InstanceBase *>::Element *E = s->instances.front(); E; E = E->next()) {
|
||||
E->get()->skeleton = RID();
|
||||
}
|
||||
|
||||
skeleton_allocate(p_rid, 0, false);
|
||||
|
||||
if (s->tex_id) {
|
||||
glDeleteTextures(1, &s->tex_id);
|
||||
}
|
||||
|
||||
skeleton_owner.free(p_rid);
|
||||
memdelete(s);
|
||||
|
||||
return true;
|
||||
} else if (mesh_owner.owns(p_rid)) {
|
||||
Mesh *mesh = mesh_owner.get(p_rid);
|
||||
@ -5669,10 +5276,6 @@ bool RasterizerStorageGLES2::has_os_feature(const String &p_feature) const {
|
||||
return config.etc1_supported;
|
||||
}
|
||||
|
||||
if (p_feature == "skinning_fallback") {
|
||||
return config.use_skeleton_software;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5985,10 +5588,6 @@ void RasterizerStorageGLES2::initialize() {
|
||||
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &config.max_cubemap_texture_size);
|
||||
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, config.max_viewport_dimensions);
|
||||
|
||||
// the use skeleton software path should be used if either float texture is not supported,
|
||||
// OR max_vertex_texture_image_units is zero
|
||||
config.use_skeleton_software = (config.float_texture_supported == false) || (config.max_vertex_texture_image_units == 0);
|
||||
|
||||
shaders.copy.init();
|
||||
shaders.cubemap_filter.init();
|
||||
bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx");
|
||||
@ -6093,12 +5692,6 @@ void RasterizerStorageGLES2::initialize() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// skeleton buffer
|
||||
{
|
||||
resources.skeleton_transform_buffer_size = 0;
|
||||
glGenBuffers(1, &resources.skeleton_transform_buffer);
|
||||
}
|
||||
|
||||
// blend buffer
|
||||
{
|
||||
resources.blend_shape_transform_cpu_buffer_size = 0;
|
||||
@ -6182,7 +5775,6 @@ void RasterizerStorageGLES2::update_dirty_resources() {
|
||||
update_dirty_shaders();
|
||||
update_dirty_materials();
|
||||
update_dirty_blend_shapes();
|
||||
update_dirty_skeletons();
|
||||
update_dirty_multimeshes();
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,6 @@ public:
|
||||
bool shrink_textures_x2;
|
||||
bool use_fast_texture_filter;
|
||||
bool use_anisotropic_filter;
|
||||
bool use_skeleton_software;
|
||||
bool use_physical_light_attenuation;
|
||||
|
||||
int max_vertex_texture_image_units;
|
||||
@ -123,10 +122,6 @@ public:
|
||||
|
||||
GLuint quadie;
|
||||
|
||||
size_t skeleton_transform_buffer_size;
|
||||
GLuint skeleton_transform_buffer;
|
||||
PoolVector<float> skeleton_transform_cpu_buffer;
|
||||
|
||||
size_t blend_shape_transform_cpu_buffer_size;
|
||||
PoolVector<float> blend_shape_transform_cpu_buffer;
|
||||
} resources;
|
||||
@ -641,9 +636,6 @@ public:
|
||||
|
||||
RS::PrimitiveType primitive;
|
||||
|
||||
Vector<AABB> skeleton_bone_aabb;
|
||||
Vector<bool> skeleton_bone_used;
|
||||
|
||||
bool active;
|
||||
|
||||
PoolVector<uint8_t> data;
|
||||
@ -736,7 +728,6 @@ public:
|
||||
|
||||
virtual AABB mesh_surface_get_aabb(RID p_mesh, int p_surface) const;
|
||||
virtual Vector<PoolVector<uint8_t>> mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const;
|
||||
virtual Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const;
|
||||
|
||||
virtual void mesh_remove_surface(RID p_mesh, int p_surface);
|
||||
virtual int mesh_get_surface_count(RID p_mesh) const;
|
||||
@ -744,7 +735,7 @@ public:
|
||||
virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb);
|
||||
virtual AABB mesh_get_custom_aabb(RID p_mesh) const;
|
||||
|
||||
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const;
|
||||
virtual AABB mesh_get_aabb(RID p_mesh) const;
|
||||
virtual void mesh_clear(RID p_mesh);
|
||||
|
||||
void update_dirty_blend_shapes();
|
||||
@ -874,54 +865,6 @@ public:
|
||||
virtual RID immediate_get_material(RID p_immediate) const;
|
||||
virtual AABB immediate_get_aabb(RID p_immediate) const;
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
struct Skeleton : RID_Data {
|
||||
bool use_2d;
|
||||
|
||||
int size;
|
||||
uint32_t revision;
|
||||
|
||||
// TODO use float textures for storage
|
||||
|
||||
Vector<float> bone_data;
|
||||
|
||||
GLuint tex_id;
|
||||
|
||||
SelfList<Skeleton> update_list;
|
||||
RBSet<RasterizerScene::InstanceBase *> instances;
|
||||
|
||||
Transform2D base_transform_2d;
|
||||
LocalVector<RID> linked_canvas_items;
|
||||
|
||||
Skeleton() :
|
||||
use_2d(false),
|
||||
size(0),
|
||||
revision(1),
|
||||
tex_id(0),
|
||||
update_list(this) {
|
||||
}
|
||||
};
|
||||
|
||||
mutable RID_Owner<Skeleton> skeleton_owner;
|
||||
|
||||
SelfList<Skeleton>::List skeleton_update_list;
|
||||
|
||||
void update_dirty_skeletons();
|
||||
|
||||
virtual RID skeleton_create();
|
||||
virtual void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false);
|
||||
virtual int skeleton_get_bone_count(RID p_skeleton) const;
|
||||
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform);
|
||||
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const;
|
||||
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
|
||||
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
|
||||
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
|
||||
virtual uint32_t skeleton_get_revision(RID p_skeleton) const;
|
||||
virtual void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach);
|
||||
|
||||
void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);
|
||||
|
||||
/* Light API */
|
||||
|
||||
struct Light : Instantiable {
|
||||
|
@ -310,7 +310,6 @@ public:
|
||||
settings_scissor_lights = false;
|
||||
settings_scissor_threshold = -1.0f;
|
||||
settings_use_single_rect_fallback = false;
|
||||
settings_use_software_skinning = true;
|
||||
settings_ninepatch_mode = 0; // default
|
||||
settings_light_max_join_items = 16;
|
||||
|
||||
@ -435,7 +434,6 @@ public:
|
||||
float settings_scissor_threshold; // 0.0 to 1.0
|
||||
int settings_item_reordering_lookahead;
|
||||
bool settings_use_single_rect_fallback;
|
||||
bool settings_use_software_skinning;
|
||||
int settings_light_max_join_items;
|
||||
int settings_ninepatch_mode;
|
||||
|
||||
@ -643,7 +641,6 @@ protected:
|
||||
|
||||
return TM_ALL;
|
||||
}
|
||||
bool _software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_poly, RasterizerCanvas::Item *p_item, BatchVertex *bvs, BatchColor *vertex_colors, const FillState &p_fill_state, const BatchColor *p_precalced_colors);
|
||||
|
||||
typename T_STORAGE::Texture *_get_canvas_texture(const RID &p_texture) const {
|
||||
if (p_texture.is_valid()) {
|
||||
@ -1027,7 +1024,6 @@ PREAMBLE(void)::batch_initialize() {
|
||||
bdata.settings_item_reordering_lookahead = GLOBAL_GET("rendering/batching/parameters/item_reordering_lookahead");
|
||||
bdata.settings_light_max_join_items = GLOBAL_GET("rendering/batching/lights/max_join_items");
|
||||
bdata.settings_use_single_rect_fallback = GLOBAL_GET("rendering/batching/options/single_rect_fallback");
|
||||
bdata.settings_use_software_skinning = GLOBAL_GET("rendering/2d/options/use_software_skinning");
|
||||
bdata.settings_ninepatch_mode = GLOBAL_GET("rendering/2d/options/ninepatch_mode");
|
||||
|
||||
// allow user to override the api usage techniques using project settings
|
||||
@ -1736,173 +1732,12 @@ bool C_PREAMBLE::_prefill_polygon(RasterizerCanvas::Item::CommandPolygon *p_poly
|
||||
precalced_colors[n] = vcol;
|
||||
}
|
||||
|
||||
if (!_software_skin_poly(p_poly, p_item, bvs, vertex_colors, r_fill_state, precalced_colors)) {
|
||||
bool software_transform = (r_fill_state.transform_mode != TM_NONE) && (!use_large_verts);
|
||||
bool software_transform = (r_fill_state.transform_mode != TM_NONE) && (!use_large_verts);
|
||||
|
||||
for (int n = 0; n < num_inds; n++) {
|
||||
int ind = p_poly->indices[n];
|
||||
|
||||
DEV_CHECK_ONCE(ind < p_poly->points.size());
|
||||
|
||||
// recover at runtime from invalid polys (the editor may send invalid polys)
|
||||
if ((unsigned int)ind >= (unsigned int)num_verts) {
|
||||
// will recover as long as there is at least one vertex.
|
||||
// if there are no verts, we will have quick rejected earlier in this function
|
||||
ind = 0;
|
||||
}
|
||||
|
||||
// this could be moved outside the loop
|
||||
if (software_transform) {
|
||||
Vector2 pos = p_poly->points[ind];
|
||||
_software_transform_vertex(pos, r_fill_state.transform_combined);
|
||||
bvs[n].pos.set(pos.x, pos.y);
|
||||
} else {
|
||||
const Point2 &pos = p_poly->points[ind];
|
||||
bvs[n].pos.set(pos.x, pos.y);
|
||||
}
|
||||
|
||||
if (ind < p_poly->uvs.size()) {
|
||||
const Point2 &uv = p_poly->uvs[ind];
|
||||
bvs[n].uv.set(uv.x, uv.y);
|
||||
} else {
|
||||
bvs[n].uv.set(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
vertex_colors[n] = precalced_colors[ind];
|
||||
|
||||
if (use_modulate) {
|
||||
vertex_modulates[n] = vertex_modulates[0];
|
||||
}
|
||||
|
||||
if (use_large_verts) {
|
||||
// reuse precalced transform (same for each vertex within polygon)
|
||||
pBT[n] = pBT[0];
|
||||
}
|
||||
}
|
||||
} // if not software skinning
|
||||
else {
|
||||
// software skinning extra passes
|
||||
if (use_modulate) {
|
||||
for (int n = 0; n < num_inds; n++) {
|
||||
vertex_modulates[n] = vertex_modulates[0];
|
||||
}
|
||||
}
|
||||
// not sure if this will produce garbage if software skinning is changing vertex pos
|
||||
// in the shader, but is included for completeness
|
||||
if (use_large_verts) {
|
||||
for (int n = 0; n < num_inds; n++) {
|
||||
pBT[n] = pBT[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// increment total vert count
|
||||
bdata.total_verts += num_inds;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PREAMBLE(bool)::_software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_poly, RasterizerCanvas::Item *p_item, BatchVertex *bvs, BatchColor *vertex_colors, const FillState &p_fill_state, const BatchColor *p_precalced_colors) {
|
||||
// alternatively could check get_this()->state.using_skeleton
|
||||
if (p_item->skeleton == RID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_inds = p_poly->indices.size();
|
||||
int num_verts = p_poly->points.size();
|
||||
|
||||
RID skeleton = p_item->skeleton;
|
||||
int bone_count = RasterizerStorage::base_singleton->skeleton_get_bone_count(skeleton);
|
||||
|
||||
// we want a temporary buffer of positions to transform
|
||||
Vector2 *pTemps = (Vector2 *)alloca(num_verts * sizeof(Vector2));
|
||||
memset((void *)pTemps, 0, num_verts * sizeof(Vector2));
|
||||
|
||||
// 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
|
||||
// for any particular poly, but depends how cheap the skeleton_bone_get_transform_2d call is
|
||||
Transform2D *bone_transforms = (Transform2D *)alloca(bone_count * sizeof(Transform2D));
|
||||
for (int b = 0; b < bone_count; b++) {
|
||||
bone_transforms[b] = RasterizerStorage::base_singleton->skeleton_bone_get_transform_2d(skeleton, b);
|
||||
}
|
||||
|
||||
if (num_verts && (p_poly->bones.size() == num_verts * 4) && (p_poly->weights.size() == p_poly->bones.size())) {
|
||||
// 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++) {
|
||||
const Vector2 &src_pos = p_poly->points[n];
|
||||
Vector2 &dst_pos = pTemps[n];
|
||||
|
||||
// there can be an offset on the polygon at rigging time, this has to be accounted for
|
||||
// note it may be possible that this could be concatenated with the bone transforms to save extra transforms - not sure yet
|
||||
Vector2 src_pos_back_transformed = item_transform.xform(src_pos);
|
||||
|
||||
float total_weight = 0.0f;
|
||||
|
||||
for (int k = 0; k < 4; k++) {
|
||||
int bone_id = p_poly->bones[n * 4 + k];
|
||||
float weight = p_poly->weights[n * 4 + k];
|
||||
if (weight == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
total_weight += weight;
|
||||
|
||||
DEV_CHECK_ONCE(bone_id < bone_count);
|
||||
const Transform2D &bone_tr = bone_transforms[bone_id];
|
||||
|
||||
Vector2 pos = bone_tr.xform(src_pos_back_transformed);
|
||||
|
||||
dst_pos += pos * weight;
|
||||
}
|
||||
|
||||
// this is some unexplained weirdness with verts with no weights,
|
||||
// but it seemed to work for the example project ... watch for regressions
|
||||
if (total_weight < 0.01f) {
|
||||
dst_pos = src_pos;
|
||||
} else {
|
||||
dst_pos /= total_weight;
|
||||
|
||||
// retransform back from the poly offset space
|
||||
dst_pos = item_transform_inv.xform(dst_pos);
|
||||
}
|
||||
}
|
||||
|
||||
} // if bone format matches
|
||||
else {
|
||||
// not rigged properly, just copy the verts directly
|
||||
for (int n = 0; n < num_verts; n++) {
|
||||
const Vector2 &src_pos = p_poly->points[n];
|
||||
Vector2 &dst_pos = pTemps[n];
|
||||
|
||||
dst_pos = src_pos;
|
||||
}
|
||||
}
|
||||
|
||||
// software transform with combined matrix?
|
||||
if (p_fill_state.transform_mode != TM_NONE) {
|
||||
for (int n = 0; n < num_verts; n++) {
|
||||
Vector2 &dst_pos = pTemps[n];
|
||||
_software_transform_vertex(dst_pos, p_fill_state.transform_combined);
|
||||
}
|
||||
}
|
||||
|
||||
// output to the batch verts
|
||||
for (int n = 0; n < num_inds; n++) {
|
||||
int ind = p_poly->indices[n];
|
||||
|
||||
DEV_CHECK_ONCE(ind < num_verts);
|
||||
DEV_CHECK_ONCE(ind < p_poly->points.size());
|
||||
|
||||
// recover at runtime from invalid polys (the editor may send invalid polys)
|
||||
if ((unsigned int)ind >= (unsigned int)num_verts) {
|
||||
@ -1911,8 +1746,15 @@ PREAMBLE(bool)::_software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_po
|
||||
ind = 0;
|
||||
}
|
||||
|
||||
const Point2 &pos = pTemps[ind];
|
||||
bvs[n].pos.set(pos.x, pos.y);
|
||||
// this could be moved outside the loop
|
||||
if (software_transform) {
|
||||
Vector2 pos = p_poly->points[ind];
|
||||
_software_transform_vertex(pos, r_fill_state.transform_combined);
|
||||
bvs[n].pos.set(pos.x, pos.y);
|
||||
} else {
|
||||
const Point2 &pos = p_poly->points[ind];
|
||||
bvs[n].pos.set(pos.x, pos.y);
|
||||
}
|
||||
|
||||
if (ind < p_poly->uvs.size()) {
|
||||
const Point2 &uv = p_poly->uvs[ind];
|
||||
@ -1921,10 +1763,22 @@ PREAMBLE(bool)::_software_skin_poly(RasterizerCanvas::Item::CommandPolygon *p_po
|
||||
bvs[n].uv.set(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
vertex_colors[n] = p_precalced_colors[ind];
|
||||
vertex_colors[n] = precalced_colors[ind];
|
||||
|
||||
if (use_modulate) {
|
||||
vertex_modulates[n] = vertex_modulates[0];
|
||||
}
|
||||
|
||||
if (use_large_verts) {
|
||||
// reuse precalced transform (same for each vertex within polygon)
|
||||
pBT[n] = pBT[0];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// increment total vert count
|
||||
bdata.total_verts += num_inds;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
T_PREAMBLE
|
||||
@ -2760,7 +2614,7 @@ PREAMBLE(bool)::prefill_joined_item(FillState &r_fill_state, int &r_command_star
|
||||
if (buffer_full) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_MULTIRECT: {
|
||||
RasterizerCanvas::Item::CommandMultiRect *mrect = static_cast<RasterizerCanvas::Item::CommandMultiRect *>(command);
|
||||
@ -2828,48 +2682,23 @@ PREAMBLE(bool)::prefill_joined_item(FillState &r_fill_state, int &r_command_star
|
||||
|
||||
case RasterizerCanvas::Item::Command::TYPE_POLYGON: {
|
||||
RasterizerCanvas::Item::CommandPolygon *polygon = static_cast<RasterizerCanvas::Item::CommandPolygon *>(command);
|
||||
#ifdef GLES_OVER_GL
|
||||
// anti aliasing not accelerated .. it is problematic because it requires a 2nd line drawn around the outside of each
|
||||
// poly, which would require either a second list of indices or a second list of vertices for this step
|
||||
bool use_legacy_path = false;
|
||||
|
||||
if (polygon->antialiased) {
|
||||
// anti aliasing is also not supported for software skinned meshes.
|
||||
// we can't easily revert, so we force software skinned meshes to run through
|
||||
// batching path with no AA.
|
||||
use_legacy_path = !bdata.settings_use_software_skinning || p_item->skeleton == RID();
|
||||
}
|
||||
// unoptimized - could this be done once per batch / batch texture?
|
||||
bool send_light_angles = polygon->normal_map != RID();
|
||||
|
||||
if (use_legacy_path) {
|
||||
// not accelerated
|
||||
bool buffer_full = false;
|
||||
|
||||
if (send_light_angles) {
|
||||
// polygon with light angles is not yet implemented
|
||||
// for batching .. this means software skinned with light angles won't work
|
||||
_prefill_default_batch(r_fill_state, command_num, *p_item);
|
||||
} else {
|
||||
#endif
|
||||
// not using software skinning?
|
||||
if (!bdata.settings_use_software_skinning && get_this()->state.using_skeleton) {
|
||||
// not accelerated
|
||||
_prefill_default_batch(r_fill_state, command_num, *p_item);
|
||||
} else {
|
||||
// unoptimized - could this be done once per batch / batch texture?
|
||||
bool send_light_angles = polygon->normal_map != RID();
|
||||
buffer_full = _prefill_polygon<false>(polygon, r_fill_state, r_command_start, command_num, command_count, p_item, multiply_final_modulate);
|
||||
}
|
||||
|
||||
bool buffer_full = false;
|
||||
|
||||
if (send_light_angles) {
|
||||
// polygon with light angles is not yet implemented
|
||||
// for batching .. this means software skinned with light angles won't work
|
||||
_prefill_default_batch(r_fill_state, command_num, *p_item);
|
||||
} else {
|
||||
buffer_full = _prefill_polygon<false>(polygon, r_fill_state, r_command_start, command_num, command_count, p_item, multiply_final_modulate);
|
||||
}
|
||||
|
||||
if (buffer_full) {
|
||||
return true;
|
||||
}
|
||||
} // if not using hardware skinning path
|
||||
#ifdef GLES_OVER_GL
|
||||
} // if not anti-aliased poly
|
||||
#endif
|
||||
if (buffer_full) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
@ -3032,20 +2861,6 @@ 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) {
|
||||
@ -3647,10 +3462,6 @@ PREAMBLE(bool)::_detect_item_batch_break(RenderItemState &r_ris, RasterizerCanva
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!get_this()->bdata.settings_use_software_skinning && poly->bones.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_disallow_item_join_if_batch_types_too_different(r_ris, RasterizerStorageCommon::BTF_POLY)) {
|
||||
//r_batch_break = true;
|
||||
return true;
|
||||
|
@ -624,14 +624,6 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
d["format"] = RS::get_singleton()->mesh_surface_get_format(mesh, idx);
|
||||
d["aabb"] = RS::get_singleton()->mesh_surface_get_aabb(mesh, idx);
|
||||
|
||||
Vector<AABB> skel_aabb = RS::get_singleton()->mesh_surface_get_skeleton_aabb(mesh, idx);
|
||||
Array arr;
|
||||
arr.resize(skel_aabb.size());
|
||||
for (int i = 0; i < skel_aabb.size(); i++) {
|
||||
arr[i] = skel_aabb[i];
|
||||
}
|
||||
d["skeleton_aabb"] = arr;
|
||||
|
||||
Vector<PoolVector<uint8_t>> blend_shape_data = RS::get_singleton()->mesh_surface_get_blend_shapes(mesh, idx);
|
||||
|
||||
Array md;
|
||||
|
@ -530,172 +530,17 @@ AABB RasterizerStorage::multimesh_get_aabb(RID p_multimesh) const {
|
||||
return _multimesh_get_aabb(p_multimesh);
|
||||
}
|
||||
|
||||
// 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];
|
||||
// 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;
|
||||
for (int n = 1; n < num_points; n++) {
|
||||
r.expand_to(pp[n]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
return r;
|
||||
}
|
||||
|
@ -275,7 +275,6 @@ public:
|
||||
|
||||
virtual AABB mesh_surface_get_aabb(RID p_mesh, int p_surface) const = 0;
|
||||
virtual Vector<PoolVector<uint8_t>> mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const = 0;
|
||||
virtual Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const = 0;
|
||||
|
||||
virtual void mesh_remove_surface(RID p_mesh, int p_index) = 0;
|
||||
virtual int mesh_get_surface_count(RID p_mesh) const = 0;
|
||||
@ -283,7 +282,7 @@ public:
|
||||
virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) = 0;
|
||||
virtual AABB mesh_get_custom_aabb(RID p_mesh) const = 0;
|
||||
|
||||
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const = 0;
|
||||
virtual AABB mesh_get_aabb(RID p_mesh) const = 0;
|
||||
|
||||
virtual void mesh_clear(RID p_mesh) = 0;
|
||||
|
||||
@ -382,19 +381,6 @@ public:
|
||||
virtual RID immediate_get_material(RID p_immediate) const = 0;
|
||||
virtual AABB immediate_get_aabb(RID p_immediate) const = 0;
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
virtual RID skeleton_create() = 0;
|
||||
virtual void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) = 0;
|
||||
virtual int skeleton_get_bone_count(RID p_skeleton) const = 0;
|
||||
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) = 0;
|
||||
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const = 0;
|
||||
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
|
||||
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
|
||||
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
|
||||
virtual uint32_t skeleton_get_revision(RID p_skeleton) const = 0;
|
||||
virtual void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) = 0;
|
||||
|
||||
/* Light API */
|
||||
|
||||
virtual RID light_create(RS::LightType p_type) = 0;
|
||||
@ -838,7 +824,6 @@ public:
|
||||
Vector<Command *> commands;
|
||||
mutable Rect2 rect;
|
||||
RID material;
|
||||
RID skeleton;
|
||||
|
||||
//RS::MaterialBlendMode blend_mode;
|
||||
int32_t light_mask;
|
||||
@ -870,7 +855,6 @@ public:
|
||||
|
||||
private:
|
||||
Rect2 calculate_polygon_bounds(const Item::CommandPolygon &p_polygon) const;
|
||||
void precalculate_polygon_bone_bounds(const Item::CommandPolygon &p_polygon) const;
|
||||
|
||||
public:
|
||||
// the rect containing this item and all children,
|
||||
@ -890,20 +874,7 @@ public:
|
||||
}
|
||||
|
||||
if (!rect_dirty && !update_when_visible) {
|
||||
if (skeleton == RID()) {
|
||||
return rect;
|
||||
} else {
|
||||
// special case for skeletons
|
||||
uint32_t rev = RasterizerStorage::base_singleton->skeleton_get_revision(skeleton);
|
||||
if (rev == skeleton_revision) {
|
||||
// no change to the skeleton since we last calculated the bounding rect
|
||||
return rect;
|
||||
} else {
|
||||
// We need to recalculate.
|
||||
// Mark as done for next time.
|
||||
skeleton_revision = rev;
|
||||
}
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
//must update rect
|
||||
@ -984,7 +955,7 @@ public:
|
||||
} break;
|
||||
case Item::Command::TYPE_MESH: {
|
||||
const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
|
||||
AABB aabb = RasterizerStorage::base_singleton->mesh_get_aabb(mesh->mesh, RID());
|
||||
AABB aabb = RasterizerStorage::base_singleton->mesh_get_aabb(mesh->mesh);
|
||||
|
||||
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
|
||||
|
||||
|
@ -1645,31 +1645,6 @@ void RenderingServerCanvas::canvas_light_occluder_transform_physics_interpolatio
|
||||
}
|
||||
|
||||
void RenderingServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skeleton) {
|
||||
Item *canvas_item = canvas_item_owner.getornull(p_item);
|
||||
ERR_FAIL_COND(!canvas_item);
|
||||
|
||||
if (_canvas_cull_mode == CANVAS_CULL_MODE_NODE) {
|
||||
// No op?
|
||||
if (canvas_item->skeleton == p_skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detach from any previous skeleton.
|
||||
if (canvas_item->skeleton.is_valid()) {
|
||||
RSG::storage->skeleton_attach_canvas_item(canvas_item->skeleton, p_item, false);
|
||||
}
|
||||
|
||||
canvas_item->skeleton = p_skeleton;
|
||||
|
||||
// Attach to new skeleton.
|
||||
if (p_skeleton.is_valid()) {
|
||||
RSG::storage->skeleton_attach_canvas_item(p_skeleton, p_item, true);
|
||||
}
|
||||
|
||||
_make_bound_dirty(canvas_item);
|
||||
} else {
|
||||
canvas_item->skeleton = p_skeleton;
|
||||
}
|
||||
}
|
||||
|
||||
// Canvas items may contain references to other resources (such as MultiMesh).
|
||||
|
@ -243,7 +243,6 @@ public:
|
||||
|
||||
BIND2RC(AABB, mesh_surface_get_aabb, RID, int)
|
||||
BIND2RC(Vector<PoolVector<uint8_t>>, mesh_surface_get_blend_shapes, RID, int)
|
||||
BIND2RC(Vector<AABB>, mesh_surface_get_skeleton_aabb, RID, int)
|
||||
|
||||
BIND2(mesh_remove_surface, RID, int)
|
||||
BIND1RC(int, mesh_get_surface_count, RID)
|
||||
@ -299,17 +298,6 @@ public:
|
||||
BIND2(immediate_set_material, RID, RID)
|
||||
BIND1RC(RID, immediate_get_material, RID)
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
BIND0R(RID, skeleton_create)
|
||||
BIND3(skeleton_allocate, RID, int, bool)
|
||||
BIND1RC(int, skeleton_get_bone_count, RID)
|
||||
BIND3(skeleton_bone_set_transform, RID, int, const Transform &)
|
||||
BIND2RC(Transform, skeleton_bone_get_transform, RID, int)
|
||||
BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
|
||||
BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
|
||||
BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &)
|
||||
|
||||
/* Light API */
|
||||
|
||||
BIND0R(RID, directional_light_create)
|
||||
|
@ -1320,7 +1320,7 @@ void RenderingServerScene::_update_instance_aabb(Instance *p_instance) {
|
||||
if (p_instance->custom_aabb) {
|
||||
new_aabb = *p_instance->custom_aabb;
|
||||
} else {
|
||||
new_aabb = RSG::storage->mesh_get_aabb(p_instance->base, p_instance->skeleton);
|
||||
new_aabb = RSG::storage->mesh_get_aabb(p_instance->base);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
@ -146,7 +146,6 @@ void RenderingServerWrapMT::finish() {
|
||||
mesh_free_cached_ids();
|
||||
multimesh_free_cached_ids();
|
||||
immediate_free_cached_ids();
|
||||
skeleton_free_cached_ids();
|
||||
directional_light_free_cached_ids();
|
||||
omni_light_free_cached_ids();
|
||||
spot_light_free_cached_ids();
|
||||
|
@ -169,7 +169,6 @@ public:
|
||||
|
||||
FUNC2RC(AABB, mesh_surface_get_aabb, RID, int)
|
||||
FUNC2RC(Vector<PoolVector<uint8_t>>, mesh_surface_get_blend_shapes, RID, int)
|
||||
FUNC2RC(Vector<AABB>, mesh_surface_get_skeleton_aabb, RID, int)
|
||||
|
||||
FUNC2(mesh_remove_surface, RID, int)
|
||||
FUNC1RC(int, mesh_get_surface_count, RID)
|
||||
@ -225,17 +224,6 @@ public:
|
||||
FUNC2(immediate_set_material, RID, RID)
|
||||
FUNC1RC(RID, immediate_get_material, RID)
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
FUNCRID(skeleton)
|
||||
FUNC3(skeleton_allocate, RID, int, bool)
|
||||
FUNC1RC(int, skeleton_get_bone_count, RID)
|
||||
FUNC3(skeleton_bone_set_transform, RID, int, const Transform &)
|
||||
FUNC2RC(Transform, skeleton_bone_get_transform, RID, int)
|
||||
FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
|
||||
FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
|
||||
FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &)
|
||||
|
||||
/* Light API */
|
||||
|
||||
FUNCRID(directional_light)
|
||||
|
@ -1820,15 +1820,6 @@ Array RenderingServer::mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_sur
|
||||
}
|
||||
}
|
||||
|
||||
Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const {
|
||||
Vector<AABB> vec = RS::get_singleton()->mesh_surface_get_skeleton_aabb(p_mesh, p_surface);
|
||||
Array arr;
|
||||
for (int i = 0; i < vec.size(); i++) {
|
||||
arr[i] = vec[i];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
void RenderingServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("force_sync"), &RenderingServer::sync);
|
||||
ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &RenderingServer::draw, DEFVAL(true), DEFVAL(0.0));
|
||||
@ -1901,7 +1892,6 @@ void RenderingServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("mesh_surface_get_format", "mesh", "surface"), &RenderingServer::mesh_surface_get_format);
|
||||
ClassDB::bind_method(D_METHOD("mesh_surface_get_primitive_type", "mesh", "surface"), &RenderingServer::mesh_surface_get_primitive_type);
|
||||
ClassDB::bind_method(D_METHOD("mesh_surface_get_aabb", "mesh", "surface"), &RenderingServer::mesh_surface_get_aabb);
|
||||
ClassDB::bind_method(D_METHOD("mesh_surface_get_skeleton_aabb", "mesh", "surface"), &RenderingServer::_mesh_surface_get_skeleton_aabb_bind);
|
||||
ClassDB::bind_method(D_METHOD("mesh_remove_surface", "mesh", "index"), &RenderingServer::mesh_remove_surface);
|
||||
ClassDB::bind_method(D_METHOD("mesh_get_surface_count", "mesh"), &RenderingServer::mesh_get_surface_count);
|
||||
ClassDB::bind_method(D_METHOD("mesh_set_custom_aabb", "mesh", "aabb"), &RenderingServer::mesh_set_custom_aabb);
|
||||
@ -1945,14 +1935,6 @@ void RenderingServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("immediate_get_material", "immediate"), &RenderingServer::immediate_get_material);
|
||||
#endif
|
||||
|
||||
ClassDB::bind_method(D_METHOD("skeleton_create"), &RenderingServer::skeleton_create);
|
||||
ClassDB::bind_method(D_METHOD("skeleton_allocate", "skeleton", "bones", "is_2d_skeleton"), &RenderingServer::skeleton_allocate, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("skeleton_get_bone_count", "skeleton"), &RenderingServer::skeleton_get_bone_count);
|
||||
ClassDB::bind_method(D_METHOD("skeleton_bone_set_transform", "skeleton", "bone", "transform"), &RenderingServer::skeleton_bone_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("skeleton_bone_get_transform", "skeleton", "bone"), &RenderingServer::skeleton_bone_get_transform);
|
||||
ClassDB::bind_method(D_METHOD("skeleton_bone_set_transform_2d", "skeleton", "bone", "transform"), &RenderingServer::skeleton_bone_set_transform_2d);
|
||||
ClassDB::bind_method(D_METHOD("skeleton_bone_get_transform_2d", "skeleton", "bone"), &RenderingServer::skeleton_bone_get_transform_2d);
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
ClassDB::bind_method(D_METHOD("directional_light_create"), &RenderingServer::directional_light_create);
|
||||
ClassDB::bind_method(D_METHOD("omni_light_create"), &RenderingServer::omni_light_create);
|
||||
|
@ -322,8 +322,6 @@ public:
|
||||
|
||||
virtual AABB mesh_surface_get_aabb(RID p_mesh, int p_surface) const = 0;
|
||||
virtual Vector<PoolVector<uint8_t>> mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const = 0;
|
||||
virtual Vector<AABB> mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const = 0;
|
||||
Array _mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const;
|
||||
|
||||
virtual void mesh_remove_surface(RID p_mesh, int p_index) = 0;
|
||||
virtual int mesh_get_surface_count(RID p_mesh) const = 0;
|
||||
@ -405,17 +403,6 @@ public:
|
||||
virtual void immediate_set_material(RID p_immediate, RID p_material) = 0;
|
||||
virtual RID immediate_get_material(RID p_immediate) const = 0;
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
virtual RID skeleton_create() = 0;
|
||||
virtual void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) = 0;
|
||||
virtual int skeleton_get_bone_count(RID p_skeleton) const = 0;
|
||||
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) = 0;
|
||||
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const = 0;
|
||||
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
|
||||
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
|
||||
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
|
||||
|
||||
/* Light API */
|
||||
|
||||
enum LightType {
|
||||
|
Loading…
Reference in New Issue
Block a user