|
@@ -809,6 +809,12 @@ void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) {
|
|
|
}
|
|
|
//delete last, so proxies can be updated
|
|
|
texture_owner.free(p_by_texture);
|
|
|
+
|
|
|
+ if (decal_atlas.textures.has(p_texture)) {
|
|
|
+ //belongs to decal atlas..
|
|
|
+
|
|
|
+ decal_atlas.dirty = true; //mark it dirty since it was most likely modified
|
|
|
+ }
|
|
|
}
|
|
|
void RasterizerStorageRD::texture_set_size_override(RID p_texture, int p_width, int p_height) {
|
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
@@ -3553,6 +3559,94 @@ float RasterizerStorageRD::reflection_probe_get_interior_ambient_probe_contribut
|
|
|
return reflection_probe->interior_ambient_probe_contrib;
|
|
|
}
|
|
|
|
|
|
+RID RasterizerStorageRD::decal_create() {
|
|
|
+ return decal_owner.make_rid(Decal());
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_extents(RID p_decal, const Vector3 &p_extents) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->extents = p_extents;
|
|
|
+ decal->instance_dependency.instance_notify_changed(true, false);
|
|
|
+}
|
|
|
+void RasterizerStorageRD::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ ERR_FAIL_INDEX(p_type, RS::DECAL_TEXTURE_MAX);
|
|
|
+
|
|
|
+ if (decal->textures[p_type] == p_texture) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ERR_FAIL_COND(p_texture.is_valid() && !texture_owner.owns(p_texture));
|
|
|
+
|
|
|
+ if (decal->textures[p_type].is_valid() && texture_owner.owns(decal->textures[p_type])) {
|
|
|
+ texture_remove_from_decal_atlas(decal->textures[p_type]);
|
|
|
+ }
|
|
|
+
|
|
|
+ decal->textures[p_type] = p_texture;
|
|
|
+
|
|
|
+ if (decal->textures[p_type].is_valid()) {
|
|
|
+ texture_add_to_decal_atlas(decal->textures[p_type]);
|
|
|
+ }
|
|
|
+
|
|
|
+ decal->instance_dependency.instance_notify_changed(false, true);
|
|
|
+}
|
|
|
+void RasterizerStorageRD::decal_set_emission_energy(RID p_decal, float p_energy) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->emission_energy = p_energy;
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_albedo_mix(RID p_decal, float p_mix) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->albedo_mix = p_mix;
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_modulate(RID p_decal, const Color &p_modulate) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->modulate = p_modulate;
|
|
|
+}
|
|
|
+void RasterizerStorageRD::decal_set_cull_mask(RID p_decal, uint32_t p_layers) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->cull_mask = p_layers;
|
|
|
+ decal->instance_dependency.instance_notify_changed(true, false);
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {
|
|
|
+
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->distance_fade = p_enabled;
|
|
|
+ decal->distance_fade_begin = p_begin;
|
|
|
+ decal->distance_fade_length = p_length;
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_fade(RID p_decal, float p_above, float p_below) {
|
|
|
+
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->upper_fade = p_above;
|
|
|
+ decal->lower_fade = p_below;
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::decal_set_normal_fade(RID p_decal, float p_fade) {
|
|
|
+
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND(!decal);
|
|
|
+ decal->normal_fade = p_fade;
|
|
|
+}
|
|
|
+
|
|
|
+AABB RasterizerStorageRD::decal_get_aabb(RID p_decal) const {
|
|
|
+ Decal *decal = decal_owner.getornull(p_decal);
|
|
|
+ ERR_FAIL_COND_V(!decal, AABB());
|
|
|
+
|
|
|
+ return AABB(-decal->extents, decal->extents * 2.0);
|
|
|
+}
|
|
|
+
|
|
|
RID RasterizerStorageRD::gi_probe_create() {
|
|
|
|
|
|
return gi_probe_owner.make_rid(GIProbe());
|
|
@@ -4243,6 +4337,9 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
|
|
|
} else if (reflection_probe_owner.owns(p_base)) {
|
|
|
ReflectionProbe *rp = reflection_probe_owner.getornull(p_base);
|
|
|
p_instance->update_dependency(&rp->instance_dependency);
|
|
|
+ } else if (decal_owner.owns(p_base)) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_base);
|
|
|
+ p_instance->update_dependency(&decal->instance_dependency);
|
|
|
} else if (gi_probe_owner.owns(p_base)) {
|
|
|
GIProbe *gip = gi_probe_owner.getornull(p_base);
|
|
|
p_instance->update_dependency(&gip->instance_dependency);
|
|
@@ -4271,6 +4368,9 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
|
|
|
if (reflection_probe_owner.owns(p_rid)) {
|
|
|
return RS::INSTANCE_REFLECTION_PROBE;
|
|
|
}
|
|
|
+ if (decal_owner.owns(p_rid)) {
|
|
|
+ return RS::INSTANCE_DECAL;
|
|
|
+ }
|
|
|
if (gi_probe_owner.owns(p_rid)) {
|
|
|
return RS::INSTANCE_GI_PROBE;
|
|
|
}
|
|
@@ -4280,10 +4380,238 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
|
|
|
|
|
|
return RS::INSTANCE_NONE;
|
|
|
}
|
|
|
+
|
|
|
+void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture) {
|
|
|
+ if (!decal_atlas.textures.has(p_texture)) {
|
|
|
+ DecalAtlas::Texture t;
|
|
|
+ t.users = 1;
|
|
|
+ decal_atlas.textures[p_texture] = t;
|
|
|
+ decal_atlas.dirty = true;
|
|
|
+ } else {
|
|
|
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
|
|
|
+ t->users++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture) {
|
|
|
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
|
|
|
+ ERR_FAIL_COND(!t);
|
|
|
+ t->users--;
|
|
|
+ if (t->users == 0) {
|
|
|
+ decal_atlas.textures.erase(p_texture);
|
|
|
+ //do not mark it dirty, there is no need to since it remains working
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+RID RasterizerStorageRD::decal_atlas_get_texture() const {
|
|
|
+ return decal_atlas.texture;
|
|
|
+}
|
|
|
+
|
|
|
+RID RasterizerStorageRD::decal_atlas_get_texture_srgb() const {
|
|
|
+ return decal_atlas.texture;
|
|
|
+}
|
|
|
+
|
|
|
+void RasterizerStorageRD::_update_decal_atlas() {
|
|
|
+ if (!decal_atlas.dirty) {
|
|
|
+ return; //nothing to do
|
|
|
+ }
|
|
|
+
|
|
|
+ decal_atlas.dirty = false;
|
|
|
+
|
|
|
+ if (decal_atlas.texture.is_valid()) {
|
|
|
+ RD::get_singleton()->free(decal_atlas.texture);
|
|
|
+ decal_atlas.texture = RID();
|
|
|
+ decal_atlas.texture_srgb = RID();
|
|
|
+ decal_atlas.texture_mipmaps.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ int border = 1 << decal_atlas.mipmaps;
|
|
|
+
|
|
|
+ if (decal_atlas.textures.size()) {
|
|
|
+ //generate atlas
|
|
|
+ Vector<DecalAtlas::SortItem> itemsv;
|
|
|
+ itemsv.resize(decal_atlas.textures.size());
|
|
|
+ int base_size = 8;
|
|
|
+ const RID *K = NULL;
|
|
|
+
|
|
|
+ int idx = 0;
|
|
|
+ while ((K = decal_atlas.textures.next(K))) {
|
|
|
+ DecalAtlas::SortItem &si = itemsv.write[idx];
|
|
|
+
|
|
|
+ Texture *src_tex = texture_owner.getornull(*K);
|
|
|
+
|
|
|
+ si.size.width = (src_tex->width / border) + 1;
|
|
|
+ si.size.height = (src_tex->height / border) + 1;
|
|
|
+ si.pixel_size = Size2i(src_tex->width, src_tex->height);
|
|
|
+
|
|
|
+ if (base_size < si.size.width) {
|
|
|
+ base_size = nearest_power_of_2_templated(si.size.width);
|
|
|
+ }
|
|
|
+
|
|
|
+ si.texture = *K;
|
|
|
+ idx++;
|
|
|
+ }
|
|
|
+
|
|
|
+ //sort items by size
|
|
|
+ itemsv.sort();
|
|
|
+
|
|
|
+ //attempt to create atlas
|
|
|
+ int item_count = itemsv.size();
|
|
|
+ DecalAtlas::SortItem *items = itemsv.ptrw();
|
|
|
+
|
|
|
+ int atlas_height = 0;
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+
|
|
|
+ Vector<int> v_offsetsv;
|
|
|
+ v_offsetsv.resize(base_size);
|
|
|
+
|
|
|
+ int *v_offsets = v_offsetsv.ptrw();
|
|
|
+ zeromem(v_offsets, sizeof(int) * base_size);
|
|
|
+
|
|
|
+ int max_height = 0;
|
|
|
+
|
|
|
+ for (int i = 0; i < item_count; i++) {
|
|
|
+ //best fit
|
|
|
+ DecalAtlas::SortItem &si = items[i];
|
|
|
+ int best_idx = -1;
|
|
|
+ int best_height = 0x7FFFFFFF;
|
|
|
+ for (int j = 0; j <= base_size - si.size.width; j++) {
|
|
|
+ int height = 0;
|
|
|
+ for (int k = 0; k < si.size.width; k++) {
|
|
|
+ int h = v_offsets[k + j];
|
|
|
+ if (h > height) {
|
|
|
+ height = h;
|
|
|
+ if (height > best_height) {
|
|
|
+ break; //already bad
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (height < best_height) {
|
|
|
+ best_height = height;
|
|
|
+ best_idx = j;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //update
|
|
|
+ for (int k = 0; k < si.size.width; k++) {
|
|
|
+ v_offsets[k + best_idx] = best_height + si.size.height;
|
|
|
+ }
|
|
|
+
|
|
|
+ si.pos.x = best_idx;
|
|
|
+ si.pos.y = best_height;
|
|
|
+
|
|
|
+ if (si.pos.y + si.size.height > max_height) {
|
|
|
+ max_height = si.pos.y + si.size.height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (max_height <= base_size * 2) {
|
|
|
+ atlas_height = max_height;
|
|
|
+ break; //good ratio, break;
|
|
|
+ }
|
|
|
+
|
|
|
+ base_size *= 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ decal_atlas.size.width = base_size * border;
|
|
|
+ decal_atlas.size.height = nearest_power_of_2_templated(atlas_height * border);
|
|
|
+
|
|
|
+ for (int i = 0; i < item_count; i++) {
|
|
|
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(items[i].texture);
|
|
|
+ t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2);
|
|
|
+ t->uv_rect.size = items[i].pixel_size;
|
|
|
+ //print_line("blitrect: " + t->uv_rect);
|
|
|
+ t->uv_rect.position /= Size2(decal_atlas.size);
|
|
|
+ t->uv_rect.size /= Size2(decal_atlas.size);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+
|
|
|
+ //use border as size, so it at least has enough mipmaps
|
|
|
+ decal_atlas.size.width = border;
|
|
|
+ decal_atlas.size.height = border;
|
|
|
+ }
|
|
|
+
|
|
|
+ //blit textures
|
|
|
+
|
|
|
+ RD::TextureFormat tformat;
|
|
|
+ tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
+ tformat.width = decal_atlas.size.width;
|
|
|
+ tformat.height = decal_atlas.size.height;
|
|
|
+ tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
|
|
|
+ tformat.type = RD::TEXTURE_TYPE_2D;
|
|
|
+ tformat.mipmaps = decal_atlas.mipmaps;
|
|
|
+ tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM);
|
|
|
+ tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB);
|
|
|
+
|
|
|
+ decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView());
|
|
|
+
|
|
|
+ {
|
|
|
+ //create the framebuffer
|
|
|
+
|
|
|
+ Size2i s = decal_atlas.size;
|
|
|
+
|
|
|
+ for (int i = 0; i < decal_atlas.mipmaps; i++) {
|
|
|
+ DecalAtlas::MipMap mm;
|
|
|
+ mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), decal_atlas.texture, 0, i);
|
|
|
+ Vector<RID> fb;
|
|
|
+ fb.push_back(mm.texture);
|
|
|
+ mm.fb = RD::get_singleton()->framebuffer_create(fb);
|
|
|
+ mm.size = s;
|
|
|
+ decal_atlas.texture_mipmaps.push_back(mm);
|
|
|
+
|
|
|
+ s.width = MAX(1, s.width >> 1);
|
|
|
+ s.height = MAX(1, s.height >> 1);
|
|
|
+ }
|
|
|
+ {
|
|
|
+ //create the SRGB variant
|
|
|
+ RD::TextureView rd_view;
|
|
|
+ rd_view.format_override = RD::DATA_FORMAT_R8G8B8A8_SRGB;
|
|
|
+ decal_atlas.texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, decal_atlas.texture);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ RID prev_texture;
|
|
|
+ for (int i = 0; i < decal_atlas.texture_mipmaps.size(); i++) {
|
|
|
+ const DecalAtlas::MipMap &mm = decal_atlas.texture_mipmaps[i];
|
|
|
+
|
|
|
+ Color clear_color(0, 0, 0, 0);
|
|
|
+
|
|
|
+ if (decal_atlas.textures.size()) {
|
|
|
+
|
|
|
+ if (i == 0) {
|
|
|
+ Vector<Color> cc;
|
|
|
+ cc.push_back(clear_color);
|
|
|
+
|
|
|
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, cc);
|
|
|
+
|
|
|
+ const RID *K = NULL;
|
|
|
+ while ((K = decal_atlas.textures.next(K))) {
|
|
|
+ DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
|
|
|
+ Texture *src_tex = texture_owner.getornull(*K);
|
|
|
+ effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list);
|
|
|
+ }
|
|
|
+
|
|
|
+ RD::get_singleton()->draw_list_end();
|
|
|
+
|
|
|
+ prev_texture = mm.texture;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ effects.copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size));
|
|
|
+ prev_texture = mm.texture;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ RD::get_singleton()->texture_clear(mm.texture, clear_color, 0, 1, 0, 1, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void RasterizerStorageRD::update_dirty_resources() {
|
|
|
_update_queued_materials();
|
|
|
_update_dirty_multimeshes();
|
|
|
_update_dirty_skeletons();
|
|
|
+ _update_decal_atlas();
|
|
|
}
|
|
|
|
|
|
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
|
|
@@ -4332,6 +4660,11 @@ bool RasterizerStorageRD::free(RID p_rid) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (decal_atlas.textures.has(p_rid)) {
|
|
|
+ decal_atlas.textures.erase(p_rid);
|
|
|
+ //there is not much a point of making it dirty, just let it be.
|
|
|
+ }
|
|
|
+
|
|
|
for (int i = 0; i < t->proxies.size(); i++) {
|
|
|
Texture *p = texture_owner.getornull(t->proxies[i]);
|
|
|
ERR_CONTINUE(!p);
|
|
@@ -4382,6 +4715,15 @@ bool RasterizerStorageRD::free(RID p_rid) {
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_rid);
|
|
|
reflection_probe->instance_dependency.instance_notify_deleted(p_rid);
|
|
|
reflection_probe_owner.free(p_rid);
|
|
|
+ } else if (decal_owner.owns(p_rid)) {
|
|
|
+ Decal *decal = decal_owner.getornull(p_rid);
|
|
|
+ for (int i = 0; i < RS::DECAL_TEXTURE_MAX; i++) {
|
|
|
+ if (decal->textures[i].is_valid() && texture_owner.owns(decal->textures[i])) {
|
|
|
+ texture_remove_from_decal_atlas(decal->textures[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ decal->instance_dependency.instance_notify_deleted(p_rid);
|
|
|
+ decal_owner.free(p_rid);
|
|
|
} else if (gi_probe_owner.owns(p_rid)) {
|
|
|
gi_probe_allocate(p_rid, Transform(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate
|
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_rid);
|
|
@@ -4485,6 +4827,10 @@ RasterizerStorageRD::RasterizerStorageRD() {
|
|
|
Vector<Vector<uint8_t>> vpv;
|
|
|
vpv.push_back(pv);
|
|
|
default_rd_textures[DEFAULT_RD_TEXTURE_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
|
|
|
+
|
|
|
+ //take the chance and initialize decal atlas to something
|
|
|
+ decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
|
|
|
+ decal_atlas.texture_srgb = decal_atlas.texture;
|
|
|
}
|
|
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
@@ -4676,9 +5022,11 @@ RasterizerStorageRD::RasterizerStorageRD() {
|
|
|
//default rd buffers
|
|
|
{
|
|
|
|
|
|
- { //vertex
|
|
|
+ //vertex
|
|
|
+ {
|
|
|
|
|
|
Vector<uint8_t> buffer;
|
|
|
+
|
|
|
buffer.resize(sizeof(float) * 3);
|
|
|
{
|
|
|
uint8_t *w = buffer.ptrw();
|
|
@@ -4813,4 +5161,12 @@ RasterizerStorageRD::~RasterizerStorageRD() {
|
|
|
RD::get_singleton()->free(mesh_default_rd_buffers[i]);
|
|
|
}
|
|
|
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
|
|
|
+
|
|
|
+ if (decal_atlas.textures.size()) {
|
|
|
+ ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (decal_atlas.texture.is_valid()) {
|
|
|
+ RD::get_singleton()->free(decal_atlas.texture);
|
|
|
+ }
|
|
|
}
|