summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp8
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.h1
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp292
-rw-r--r--servers/visual/rasterizer_rd/rasterizer_storage_rd.h63
-rw-r--r--servers/visual/rasterizer_rd/shaders/scene_forward.glsl24
-rw-r--r--servers/visual/rasterizer_rd/shaders/scene_forward_inc.glsl1
6 files changed, 376 insertions, 13 deletions
diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp
index 503d36e7b1..e44955d666 100644
--- a/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.cpp
@@ -626,7 +626,12 @@ void RasterizerSceneForwardRD::_fill_instances(RenderList::Element **p_elements,
}
id.flags |= (stride << INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT);
+ } else if (e->instance->base_type == VS::INSTANCE_MESH) {
+ if (e->instance->skeleton.is_valid()) {
+ id.flags |= INSTANCE_DATA_FLAG_SKELETON;
+ }
}
+
//forward
uint32_t reflection_count = 0;
@@ -746,6 +751,9 @@ void RasterizerSceneForwardRD::_render_list(RenderingDevice::DrawListID p_draw_l
switch (e->instance->base_type) {
case VS::INSTANCE_MESH: {
primitive = storage->mesh_surface_get_primitive(e->instance->base, e->surface_index);
+ if (e->instance->skeleton.is_valid()) {
+ xforms_uniform_set = storage->skeleton_get_3d_uniform_set(e->instance->skeleton, default_shader_rd, 1);
+ }
} break;
case VS::INSTANCE_MULTIMESH: {
RID mesh = storage->multimesh_get_mesh(e->instance->base);
diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.h b/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.h
index bd6d6563f5..73d48c5fe8 100644
--- a/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.h
+++ b/servers/visual/rasterizer_rd/rasterizer_scene_forward_rd.h
@@ -254,6 +254,7 @@ class RasterizerSceneForwardRD : public RasterizerSceneRD {
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT = 16,
INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_MASK = 0x7,
+ INSTANCE_DATA_FLAG_SKELETON = 1 << 19,
};
struct InstanceData {
diff --git a/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
index fc6147316f..c90a280fdc 100644
--- a/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
+++ b/servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
@@ -2010,11 +2010,104 @@ AABB RasterizerStorageRD::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
return mesh->custom_aabb;
}
- if (!p_skeleton.is_valid()) {
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+
+ if (!skeleton || skeleton->size == 0) {
return mesh->aabb;
}
- return mesh->aabb;
+ AABB aabb;
+
+ for (int i = 0; i < mesh->surface_count; i++) {
+
+ AABB laabb;
+ if ((mesh->surfaces[i]->format & VS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) {
+
+ int bs = mesh->surfaces[i]->bone_aabbs.size();
+ const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr();
+
+ int sbs = skeleton->size;
+ ERR_CONTINUE(bs > sbs);
+ const float *baseptr = skeleton->data.ptr();
+
+ bool first = true;
+
+ if (skeleton->use_2d) {
+ for (int j = 0; j < bs; j++) {
+
+ if (skbones[0].size == Vector3())
+ continue; //bone is unused
+
+ const float *dataptr = baseptr + j * 8;
+
+ Transform mtx;
+
+ mtx.basis.elements[0].x = dataptr[0];
+ mtx.basis.elements[1].x = dataptr[1];
+ mtx.origin.x = dataptr[3];
+
+ mtx.basis.elements[0].y = dataptr[4];
+ mtx.basis.elements[1].y = dataptr[5];
+ mtx.origin.y = dataptr[7];
+
+ 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 (skbones[0].size == Vector3())
+ continue; //bone is unused
+
+ const float *dataptr = baseptr + j * 12;
+
+ Transform mtx;
+
+ mtx.basis.elements[0][0] = dataptr[0];
+ mtx.basis.elements[0][1] = dataptr[1];
+ mtx.basis.elements[0][2] = dataptr[2];
+ mtx.origin.x = dataptr[3];
+ mtx.basis.elements[1][0] = dataptr[4];
+ mtx.basis.elements[1][1] = dataptr[5];
+ mtx.basis.elements[1][2] = dataptr[6];
+ mtx.origin.y = dataptr[7];
+ mtx.basis.elements[2][0] = dataptr[8];
+ mtx.basis.elements[2][1] = dataptr[9];
+ mtx.basis.elements[2][2] = dataptr[10];
+ mtx.origin.z = dataptr[11];
+
+ AABB baabb = mtx.xform(skbones[j]);
+ if (first) {
+ laabb = baabb;
+ first = false;
+ } else {
+ laabb.merge_with(baabb);
+ }
+ }
+ }
+
+ if (laabb.size == Vector3()) {
+ laabb = mesh->surfaces[i]->aabb;
+ }
+ } else {
+
+ laabb = mesh->surfaces[i]->aabb;
+ }
+
+ if (i == 0) {
+ aabb = laabb;
+ } else {
+ aabb.merge_with(laabb);
+ }
+ }
+
+ return aabb;
}
void RasterizerStorageRD::mesh_clear(RID p_mesh) {
@@ -2783,6 +2876,186 @@ void RasterizerStorageRD::_update_dirty_multimeshes() {
multimesh_dirty_list = nullptr;
}
+/* SKELETON */
+
+/* SKELETON API */
+
+RID RasterizerStorageRD::skeleton_create() {
+
+ return skeleton_owner.make_rid(Skeleton());
+}
+
+void RasterizerStorageRD::_skeleton_make_dirty(Skeleton *skeleton) {
+
+ if (!skeleton->dirty) {
+ skeleton->dirty = true;
+ skeleton->dirty_list = skeleton_dirty_list;
+ skeleton_dirty_list = skeleton;
+ }
+}
+
+void RasterizerStorageRD::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;
+ skeleton->uniform_set_3d = RID();
+
+ if (skeleton->buffer.is_valid()) {
+ RD::get_singleton()->free(skeleton->buffer);
+ skeleton->buffer = RID();
+ skeleton->data.resize(0);
+ }
+
+ if (skeleton->size) {
+
+ skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12));
+ skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float));
+ zeromem(skeleton->data.ptrw(), skeleton->data.size() * sizeof(float));
+
+ _skeleton_make_dirty(skeleton);
+ }
+}
+int RasterizerStorageRD::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 RasterizerStorageRD::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 *dataptr = skeleton->data.ptrw() + p_bone * 12;
+
+ dataptr[0] = p_transform.basis.elements[0][0];
+ dataptr[1] = p_transform.basis.elements[0][1];
+ dataptr[2] = p_transform.basis.elements[0][2];
+ dataptr[3] = p_transform.origin.x;
+ dataptr[4] = p_transform.basis.elements[1][0];
+ dataptr[5] = p_transform.basis.elements[1][1];
+ dataptr[6] = p_transform.basis.elements[1][2];
+ dataptr[7] = p_transform.origin.y;
+ dataptr[8] = p_transform.basis.elements[2][0];
+ dataptr[9] = p_transform.basis.elements[2][1];
+ dataptr[10] = p_transform.basis.elements[2][2];
+ dataptr[11] = p_transform.origin.z;
+
+ _skeleton_make_dirty(skeleton);
+}
+
+Transform RasterizerStorageRD::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 *dataptr = skeleton->data.ptr() + p_bone * 12;
+
+ Transform t;
+
+ t.basis.elements[0][0] = dataptr[0];
+ t.basis.elements[0][1] = dataptr[1];
+ t.basis.elements[0][2] = dataptr[2];
+ t.origin.x = dataptr[3];
+ t.basis.elements[1][0] = dataptr[4];
+ t.basis.elements[1][1] = dataptr[5];
+ t.basis.elements[1][2] = dataptr[6];
+ t.origin.y = dataptr[7];
+ t.basis.elements[2][0] = dataptr[8];
+ t.basis.elements[2][1] = dataptr[9];
+ t.basis.elements[2][2] = dataptr[10];
+ t.origin.z = dataptr[11];
+
+ return t;
+}
+void RasterizerStorageRD::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 *dataptr = skeleton->data.ptrw() + p_bone * 8;
+
+ dataptr[0] = p_transform.elements[0][0];
+ dataptr[1] = p_transform.elements[1][0];
+ dataptr[2] = 0;
+ dataptr[3] = p_transform.elements[2][0];
+ dataptr[4] = p_transform.elements[0][1];
+ dataptr[5] = p_transform.elements[1][1];
+ dataptr[6] = 0;
+ dataptr[7] = p_transform.elements[2][1];
+
+ _skeleton_make_dirty(skeleton);
+}
+Transform2D RasterizerStorageRD::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 *dataptr = skeleton->data.ptr() + p_bone * 8;
+
+ Transform2D t;
+ t.elements[0][0] = dataptr[0];
+ t.elements[1][0] = dataptr[1];
+ t.elements[2][0] = dataptr[3];
+ t.elements[0][1] = dataptr[4];
+ t.elements[1][1] = dataptr[5];
+ t.elements[2][1] = dataptr[7];
+
+ return t;
+}
+
+void RasterizerStorageRD::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
+
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+
+ ERR_FAIL_COND(!skeleton->use_2d);
+
+ skeleton->base_transform_2d = p_base_transform;
+}
+
+void RasterizerStorageRD::_update_dirty_skeletons() {
+
+ while (skeleton_dirty_list) {
+
+ Skeleton *skeleton = skeleton_dirty_list;
+
+ if (skeleton->size) {
+
+ RD::get_singleton()->buffer_update(skeleton->buffer, 0, skeleton->data.size() * sizeof(float), skeleton->data.ptr(), false);
+ }
+
+ skeleton_dirty_list = skeleton->dirty_list;
+
+ skeleton->instance_dependency.instance_notify_changed(true, false);
+
+ skeleton->dirty = false;
+ skeleton->dirty_list = nullptr;
+ }
+
+ skeleton_dirty_list = nullptr;
+}
+
/* LIGHT */
RID RasterizerStorageRD::light_create(VS::LightType p_type) {
@@ -3601,6 +3874,14 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
}
}
+void RasterizerStorageRD::skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
+
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+ ERR_FAIL_COND(!skeleton);
+
+ p_instance->update_dependency(&skeleton->instance_dependency);
+}
+
VS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
if (mesh_owner.owns(p_rid)) {
@@ -3621,6 +3902,7 @@ VS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
void RasterizerStorageRD::update_dirty_resources() {
_update_queued_materials();
_update_dirty_multimeshes();
+ _update_dirty_skeletons();
}
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
@@ -3705,6 +3987,12 @@ bool RasterizerStorageRD::free(RID p_rid) {
MultiMesh *multimesh = multimesh_owner.getornull(p_rid);
multimesh->instance_dependency.instance_notify_deleted(p_rid);
multimesh_owner.free(p_rid);
+ } else if (skeleton_owner.owns(p_rid)) {
+ _update_dirty_skeletons();
+ skeleton_allocate(p_rid, 0);
+ Skeleton *skeleton = skeleton_owner.getornull(p_rid);
+ skeleton->instance_dependency.instance_notify_deleted(p_rid);
+ skeleton_owner.free(p_rid);
} else if (reflection_probe_owner.owns(p_rid)) {
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_rid);
reflection_probe->instance_dependency.instance_notify_deleted(p_rid);
diff --git a/servers/visual/rasterizer_rd/rasterizer_storage_rd.h b/servers/visual/rasterizer_rd/rasterizer_storage_rd.h
index 670c97a1c1..397e3c1b95 100644
--- a/servers/visual/rasterizer_rd/rasterizer_storage_rd.h
+++ b/servers/visual/rasterizer_rd/rasterizer_storage_rd.h
@@ -327,6 +327,32 @@ private:
_FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb);
_FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances);
void _update_dirty_multimeshes();
+
+ /* Skeleton */
+
+ struct Skeleton {
+ bool use_2d = false;
+ int size = 0;
+ Vector<float> data;
+ RID buffer;
+
+ bool dirty = false;
+ Skeleton *dirty_list = nullptr;
+ Transform2D base_transform_2d;
+
+ RID uniform_set_3d;
+
+ RasterizerScene::InstanceDependency instance_dependency;
+ };
+
+ mutable RID_Owner<Skeleton> skeleton_owner;
+
+ _FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton);
+
+ Skeleton *skeleton_dirty_list = nullptr;
+
+ void _update_dirty_skeletons();
+
/* LIGHT */
struct Light {
@@ -740,16 +766,33 @@ public:
/* 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(); }
+ RID skeleton_create();
+ 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;
+ 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;
+ 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;
+
+ _FORCE_INLINE_ RID skeleton_get_3d_uniform_set(RID p_skeleton, RID p_shader, uint32_t p_set) const {
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+ if (skeleton->use_2d) {
+ return RID();
+ }
+ if (!skeleton->uniform_set_3d.is_valid()) {
+ Vector<RD::Uniform> uniforms;
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 0;
+ u.ids.push_back(skeleton->buffer);
+ uniforms.push_back(u);
+ skeleton->uniform_set_3d = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set);
+ }
+ return skeleton->uniform_set_3d;
+ }
/* Light API */
RID light_create(VS::LightType p_type);
@@ -873,7 +916,7 @@ public:
float reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const;
void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance);
- void skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {}
+ void skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance);
/* GI PROBE API */
diff --git a/servers/visual/rasterizer_rd/shaders/scene_forward.glsl b/servers/visual/rasterizer_rd/shaders/scene_forward.glsl
index 3f5a81ee93..711da3be9b 100644
--- a/servers/visual/rasterizer_rd/shaders/scene_forward.glsl
+++ b/servers/visual/rasterizer_rd/shaders/scene_forward.glsl
@@ -136,13 +136,35 @@ void main() {
#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED)
vec3 tangent = tangent_attrib.xyz;
float binormalf = tangent_attrib.a;
+ vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
+
+ if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_SKELETON)) {
+ //multimesh, instances are for it
+
+ uvec2 bones_01 = uvec2(bone_attrib.x&0xFFFF,bone_attrib.x>>16) * 3;
+ uvec2 bones_23 = uvec2(bone_attrib.y&0xFFFF,bone_attrib.y>>16) * 3;
+ vec2 weights_01 = unpackUnorm2x16(bone_attrib.z);
+ vec2 weights_23 = unpackUnorm2x16(bone_attrib.w);
+
+ mat4 m = mat4(transforms.data[bones_01.x],transforms.data[bones_01.x+1],transforms.data[bones_01.x+2],vec4(0.0,0.0,0.0,1.0)) * weights_01.x;
+ m += mat4(transforms.data[bones_01.y],transforms.data[bones_01.y+1],transforms.data[bones_01.y+2],vec4(0.0,0.0,0.0,1.0)) * weights_01.y;
+ m += mat4(transforms.data[bones_23.x],transforms.data[bones_23.x+1],transforms.data[bones_23.x+2],vec4(0.0,0.0,0.0,1.0)) * weights_23.x;
+ m += mat4(transforms.data[bones_23.y],transforms.data[bones_23.y+1],transforms.data[bones_23.y+2],vec4(0.0,0.0,0.0,1.0)) * weights_23.y;
+
+ //reverse order because its transposed
+ vertex = (vec4(vertex,1.0) * m).xyz;
+ normal = (vec4(normal,0.0) * m).xyz;
+
#if defined(TANGENT_USED) || defined(NORMALMAP_USED) || defined(LIGHT_ANISOTROPY_USED)
- vec3 binormal = normalize(cross(normal, tangent) * binormalf);
+ tangent = (vec4(tangent,0.0) * m).xyz;
+ binormal = (vec4(binormal,0.0) * m).xyz;
#endif
+ }
+
#if defined(UV_USED)
uv_interp = uv_attrib;
#endif
diff --git a/servers/visual/rasterizer_rd/shaders/scene_forward_inc.glsl b/servers/visual/rasterizer_rd/shaders/scene_forward_inc.glsl
index 4d1a1c9495..81cf47b192 100644
--- a/servers/visual/rasterizer_rd/shaders/scene_forward_inc.glsl
+++ b/servers/visual/rasterizer_rd/shaders/scene_forward_inc.glsl
@@ -131,6 +131,7 @@ layout(set=0,binding=8,std140) uniform SceneData {
//3 bits of stride
#define INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK 0x7
+#define INSTANCE_FLAGS_SKELETON (1 << 19)
struct InstanceData {