1/**************************************************************************/
2/* decal.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "decal.h"
32
33void Decal::set_size(const Vector3 &p_size) {
34 size = Vector3(MAX(0.001, p_size.x), MAX(0.001, p_size.y), MAX(0.001, p_size.z));
35 RS::get_singleton()->decal_set_size(decal, p_size);
36 update_gizmos();
37}
38
39Vector3 Decal::get_size() const {
40 return size;
41}
42
43void Decal::set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture) {
44 ERR_FAIL_INDEX(p_type, TEXTURE_MAX);
45 textures[p_type] = p_texture;
46 RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
47 RS::get_singleton()->decal_set_texture(decal, RS::DecalTexture(p_type), texture_rid);
48 update_configuration_warnings();
49}
50
51Ref<Texture2D> Decal::get_texture(DecalTexture p_type) const {
52 ERR_FAIL_INDEX_V(p_type, TEXTURE_MAX, Ref<Texture2D>());
53 return textures[p_type];
54}
55
56void Decal::set_emission_energy(real_t p_energy) {
57 emission_energy = p_energy;
58 RS::get_singleton()->decal_set_emission_energy(decal, emission_energy);
59}
60
61real_t Decal::get_emission_energy() const {
62 return emission_energy;
63}
64
65void Decal::set_albedo_mix(real_t p_mix) {
66 albedo_mix = p_mix;
67 RS::get_singleton()->decal_set_albedo_mix(decal, albedo_mix);
68}
69
70real_t Decal::get_albedo_mix() const {
71 return albedo_mix;
72}
73
74void Decal::set_upper_fade(real_t p_fade) {
75 upper_fade = MAX(p_fade, 0.0);
76 RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade);
77}
78
79real_t Decal::get_upper_fade() const {
80 return upper_fade;
81}
82
83void Decal::set_lower_fade(real_t p_fade) {
84 lower_fade = MAX(p_fade, 0.0);
85 RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade);
86}
87
88real_t Decal::get_lower_fade() const {
89 return lower_fade;
90}
91
92void Decal::set_normal_fade(real_t p_fade) {
93 normal_fade = p_fade;
94 RS::get_singleton()->decal_set_normal_fade(decal, normal_fade);
95}
96
97real_t Decal::get_normal_fade() const {
98 return normal_fade;
99}
100
101void Decal::set_modulate(Color p_modulate) {
102 modulate = p_modulate;
103 RS::get_singleton()->decal_set_modulate(decal, p_modulate);
104}
105
106Color Decal::get_modulate() const {
107 return modulate;
108}
109
110void Decal::set_enable_distance_fade(bool p_enable) {
111 distance_fade_enabled = p_enable;
112 RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
113 notify_property_list_changed();
114}
115
116bool Decal::is_distance_fade_enabled() const {
117 return distance_fade_enabled;
118}
119
120void Decal::set_distance_fade_begin(real_t p_distance) {
121 distance_fade_begin = p_distance;
122 RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
123}
124
125real_t Decal::get_distance_fade_begin() const {
126 return distance_fade_begin;
127}
128
129void Decal::set_distance_fade_length(real_t p_length) {
130 distance_fade_length = p_length;
131 RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length);
132}
133
134real_t Decal::get_distance_fade_length() const {
135 return distance_fade_length;
136}
137
138void Decal::set_cull_mask(uint32_t p_layers) {
139 cull_mask = p_layers;
140 RS::get_singleton()->decal_set_cull_mask(decal, cull_mask);
141 update_configuration_warnings();
142}
143
144uint32_t Decal::get_cull_mask() const {
145 return cull_mask;
146}
147
148AABB Decal::get_aabb() const {
149 AABB aabb;
150 aabb.position = -size / 2;
151 aabb.size = size;
152 return aabb;
153}
154
155void Decal::_validate_property(PropertyInfo &p_property) const {
156 if (!distance_fade_enabled && (p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_length")) {
157 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
158 }
159
160 if (p_property.name == "sorting_offset") {
161 p_property.usage = PROPERTY_USAGE_DEFAULT;
162 }
163}
164
165PackedStringArray Decal::get_configuration_warnings() const {
166 PackedStringArray warnings = Node::get_configuration_warnings();
167
168 if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
169 warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile rendering backends."));
170 return warnings;
171 }
172
173 if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) {
174 warnings.push_back(RTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible."));
175 }
176
177 if ((textures[TEXTURE_NORMAL].is_valid() || textures[TEXTURE_ORM].is_valid()) && textures[TEXTURE_ALBEDO].is_null()) {
178 warnings.push_back(RTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0."));
179 }
180
181 if (cull_mask == 0) {
182 warnings.push_back(RTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property."));
183 }
184
185 return warnings;
186}
187
188void Decal::_bind_methods() {
189 ClassDB::bind_method(D_METHOD("set_size", "size"), &Decal::set_size);
190 ClassDB::bind_method(D_METHOD("get_size"), &Decal::get_size);
191
192 ClassDB::bind_method(D_METHOD("set_texture", "type", "texture"), &Decal::set_texture);
193 ClassDB::bind_method(D_METHOD("get_texture", "type"), &Decal::get_texture);
194
195 ClassDB::bind_method(D_METHOD("set_emission_energy", "energy"), &Decal::set_emission_energy);
196 ClassDB::bind_method(D_METHOD("get_emission_energy"), &Decal::get_emission_energy);
197
198 ClassDB::bind_method(D_METHOD("set_albedo_mix", "energy"), &Decal::set_albedo_mix);
199 ClassDB::bind_method(D_METHOD("get_albedo_mix"), &Decal::get_albedo_mix);
200
201 ClassDB::bind_method(D_METHOD("set_modulate", "color"), &Decal::set_modulate);
202 ClassDB::bind_method(D_METHOD("get_modulate"), &Decal::get_modulate);
203
204 ClassDB::bind_method(D_METHOD("set_upper_fade", "fade"), &Decal::set_upper_fade);
205 ClassDB::bind_method(D_METHOD("get_upper_fade"), &Decal::get_upper_fade);
206
207 ClassDB::bind_method(D_METHOD("set_lower_fade", "fade"), &Decal::set_lower_fade);
208 ClassDB::bind_method(D_METHOD("get_lower_fade"), &Decal::get_lower_fade);
209
210 ClassDB::bind_method(D_METHOD("set_normal_fade", "fade"), &Decal::set_normal_fade);
211 ClassDB::bind_method(D_METHOD("get_normal_fade"), &Decal::get_normal_fade);
212
213 ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &Decal::set_enable_distance_fade);
214 ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &Decal::is_distance_fade_enabled);
215
216 ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &Decal::set_distance_fade_begin);
217 ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &Decal::get_distance_fade_begin);
218
219 ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &Decal::set_distance_fade_length);
220 ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &Decal::get_distance_fade_length);
221
222 ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Decal::set_cull_mask);
223 ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask);
224
225 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_size", "get_size");
226
227 ADD_GROUP("Textures", "texture_");
228 ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO);
229 ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL);
230 ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM);
231 ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
232
233 ADD_GROUP("Parameters", "");
234 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy");
235 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
236 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "albedo_mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_albedo_mix", "get_albedo_mix");
237 // A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface.
238 // Due to this, limit Normal Fade to 0.999.
239 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade");
240
241 ADD_GROUP("Vertical Fade", "");
242 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade");
243 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade");
244
245 ADD_GROUP("Distance Fade", "distance_fade_");
246 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
247 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
248 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
249
250 ADD_GROUP("Cull Mask", "");
251 ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
252
253 BIND_ENUM_CONSTANT(TEXTURE_ALBEDO);
254 BIND_ENUM_CONSTANT(TEXTURE_NORMAL);
255 BIND_ENUM_CONSTANT(TEXTURE_ORM);
256 BIND_ENUM_CONSTANT(TEXTURE_EMISSION);
257 BIND_ENUM_CONSTANT(TEXTURE_MAX);
258}
259
260#ifndef DISABLE_DEPRECATED
261bool Decal::_set(const StringName &p_name, const Variant &p_value) {
262 if (p_name == "extents") { // Compatibility with Godot 3.x.
263 set_size((Vector3)p_value * 2);
264 return true;
265 }
266 return false;
267}
268
269bool Decal::_get(const StringName &p_name, Variant &r_property) const {
270 if (p_name == "extents") { // Compatibility with Godot 3.x.
271 r_property = size / 2;
272 return true;
273 }
274 return false;
275}
276#endif // DISABLE_DEPRECATED
277
278Decal::Decal() {
279 decal = RenderingServer::get_singleton()->decal_create();
280 RS::get_singleton()->instance_set_base(get_instance(), decal);
281}
282
283Decal::~Decal() {
284 ERR_FAIL_NULL(RenderingServer::get_singleton());
285 RS::get_singleton()->free(decal);
286}
287