summaryrefslogtreecommitdiff
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/arvr/arvr_positional_tracker.cpp14
-rw-r--r--servers/arvr/arvr_positional_tracker.h12
-rw-r--r--servers/arvr_server.cpp9
-rw-r--r--servers/audio/effects/audio_effect_limiter.cpp5
-rw-r--r--servers/audio_server.cpp3
-rw-r--r--servers/physics/broad_phase_basic.cpp3
-rw-r--r--servers/physics/collision_object_sw.cpp32
-rw-r--r--servers/physics/collision_object_sw.h2
-rw-r--r--servers/physics/physics_server_sw.cpp21
-rw-r--r--servers/physics/physics_server_sw.h4
-rw-r--r--servers/physics/shape_sw.cpp3
-rw-r--r--servers/physics/step_sw.cpp2
-rw-r--r--servers/visual/rasterizer.h2
-rw-r--r--servers/visual/shader_language.cpp135
-rw-r--r--servers/visual/shader_language.h22
-rw-r--r--servers/visual/shader_types.cpp3
-rw-r--r--servers/visual/visual_server_raster.h1
-rw-r--r--servers/visual/visual_server_scene.cpp104
-rw-r--r--servers/visual/visual_server_wrap_mt.h1
-rw-r--r--servers/visual_server.cpp23
-rw-r--r--servers/visual_server.h10
21 files changed, 341 insertions, 70 deletions
diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp
index 539bac6703..4ecd7a3898 100644
--- a/servers/arvr/arvr_positional_tracker.cpp
+++ b/servers/arvr/arvr_positional_tracker.cpp
@@ -31,6 +31,10 @@
#include "core/os/input.h"
void ARVRPositionalTracker::_bind_methods() {
+ BIND_ENUM_CONSTANT(TRACKER_HAND_UNKNOWN);
+ BIND_ENUM_CONSTANT(TRACKER_LEFT_HAND);
+ BIND_ENUM_CONSTANT(TRACKER_RIGHT_HAND);
+
// this class is read only from GDScript, so we only have access to getters..
ClassDB::bind_method(D_METHOD("get_type"), &ARVRPositionalTracker::get_type);
ClassDB::bind_method(D_METHOD("get_name"), &ARVRPositionalTracker::get_name);
@@ -39,6 +43,7 @@ void ARVRPositionalTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_orientation"), &ARVRPositionalTracker::get_orientation);
ClassDB::bind_method(D_METHOD("get_tracks_position"), &ARVRPositionalTracker::get_tracks_position);
ClassDB::bind_method(D_METHOD("get_position"), &ARVRPositionalTracker::get_position);
+ ClassDB::bind_method(D_METHOD("get_hand"), &ARVRPositionalTracker::get_hand);
ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &ARVRPositionalTracker::get_transform);
// these functions we don't want to expose to normal users but do need to be callable from GDNative
@@ -141,6 +146,14 @@ Vector3 ARVRPositionalTracker::get_rw_position() const {
return rw_position;
};
+ARVRPositionalTracker::TrackerHand ARVRPositionalTracker::get_hand() const {
+ return hand;
+};
+
+void ARVRPositionalTracker::set_hand(const ARVRPositionalTracker::TrackerHand p_hand) {
+ hand = p_hand;
+};
+
Transform ARVRPositionalTracker::get_transform(bool p_adjust_by_reference_frame) const {
Transform new_transform;
@@ -164,6 +177,7 @@ ARVRPositionalTracker::ARVRPositionalTracker() {
tracker_id = 0;
tracks_orientation = false;
tracks_position = false;
+ hand = TRACKER_HAND_UNKNOWN;
};
ARVRPositionalTracker::~ARVRPositionalTracker(){
diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h
index f91f862ba3..ff0c150f89 100644
--- a/servers/arvr/arvr_positional_tracker.h
+++ b/servers/arvr/arvr_positional_tracker.h
@@ -48,6 +48,13 @@ class ARVRPositionalTracker : public Object {
GDCLASS(ARVRPositionalTracker, Object);
_THREAD_SAFE_CLASS_
+public:
+ enum TrackerHand {
+ TRACKER_HAND_UNKNOWN, /* unknown or not applicable */
+ TRACKER_LEFT_HAND, /* controller is the left hand controller */
+ TRACKER_RIGHT_HAND /* controller is the right hand controller */
+ };
+
private:
ARVRServer::TrackerType type; // type of tracker
StringName name; // (unique) name of the tracker
@@ -57,6 +64,7 @@ private:
Basis orientation; // our orientation
bool tracks_position; // do we track position?
Vector3 rw_position; // our position "in the real world, so without world_scale applied"
+ TrackerHand hand; // if known, the hand this tracker is held in
protected:
static void _bind_methods();
@@ -77,6 +85,8 @@ public:
Vector3 get_position() const; // get position with world_scale applied
void set_rw_position(const Vector3 &p_rw_position);
Vector3 get_rw_position() const;
+ ARVRPositionalTracker::TrackerHand get_hand() const;
+ void set_hand(const ARVRPositionalTracker::TrackerHand p_hand);
Transform get_transform(bool p_adjust_by_reference_frame) const;
@@ -84,4 +94,6 @@ public:
~ARVRPositionalTracker();
};
+VARIANT_ENUM_CAST(ARVRPositionalTracker::TrackerHand);
+
#endif
diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp
index bac24f6438..5d8cf20c92 100644
--- a/servers/arvr_server.cpp
+++ b/servers/arvr_server.cpp
@@ -68,8 +68,8 @@ void ARVRServer::_bind_methods() {
ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING, "name")));
- ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type")));
- ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING, "name")));
+ ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id")));
};
real_t ARVRServer::get_world_scale() const {
@@ -130,7 +130,6 @@ void ARVRServer::request_reference_frame(bool p_ignore_tilt, bool p_keep_height)
void ARVRServer::add_interface(const Ref<ARVRInterface> &p_interface) {
ERR_FAIL_COND(p_interface.is_null());
- int idx = -1;
for (int i = 0; i < interfaces.size(); i++) {
if (interfaces[i] == p_interface) {
@@ -232,7 +231,7 @@ void ARVRServer::add_tracker(ARVRPositionalTracker *p_tracker) {
ERR_FAIL_NULL(p_tracker);
trackers.push_back(p_tracker);
- emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type());
+ emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id());
};
void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) {
@@ -250,7 +249,7 @@ void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) {
ERR_FAIL_COND(idx == -1);
- emit_signal("tracker_removed", p_tracker->get_name());
+ emit_signal("tracker_removed", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id());
trackers.remove(idx);
};
diff --git a/servers/audio/effects/audio_effect_limiter.cpp b/servers/audio/effects/audio_effect_limiter.cpp
index 391e5db639..9787ba8109 100644
--- a/servers/audio/effects/audio_effect_limiter.cpp
+++ b/servers/audio/effects/audio_effect_limiter.cpp
@@ -31,18 +31,13 @@
void AudioEffectLimiterInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
- float thresh = Math::db2linear(base->threshold);
float threshdb = base->threshold;
float ceiling = Math::db2linear(base->ceiling);
float ceildb = base->ceiling;
float makeup = Math::db2linear(ceildb - threshdb);
- float makeupdb = ceildb - threshdb;
float sc = -base->soft_clip;
float scv = Math::db2linear(sc);
- float sccomp = Math::db2linear(-sc);
float peakdb = ceildb + 25;
- float peaklvl = Math::db2linear(peakdb);
- float scratio = base->soft_clip_ratio;
float scmult = Math::abs((ceildb - sc) / (peakdb - sc));
for (int i = 0; i < p_frame_count; i++) {
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 3139c6bb7a..29014a7ced 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -203,8 +203,9 @@ void AudioServer::_mix_step() {
if (!bus_map.has(bus->send)) {
bus = buses[0]; //send to master
} else {
+ int prev_index_cache = bus->index_cache;
bus = bus_map[bus->send];
- if (bus->index_cache >= bus->index_cache) { //invalid, send to master
+ if (prev_index_cache >= bus->index_cache) { //invalid, send to master
bus = buses[0];
}
}
diff --git a/servers/physics/broad_phase_basic.cpp b/servers/physics/broad_phase_basic.cpp
index 959718a252..c6565ac2e9 100644
--- a/servers/physics/broad_phase_basic.cpp
+++ b/servers/physics/broad_phase_basic.cpp
@@ -30,9 +30,10 @@
#include "broad_phase_basic.h"
#include "list.h"
#include "print_string.h"
+
BroadPhaseSW::ID BroadPhaseBasic::create(CollisionObjectSW *p_object, int p_subindex) {
- ERR_FAIL_COND_V(p_object == NULL, NULL);
+ ERR_FAIL_COND_V(p_object == NULL, 0);
current++;
diff --git a/servers/physics/collision_object_sw.cpp b/servers/physics/collision_object_sw.cpp
index ab716a8f6e..3af8b542fa 100644
--- a/servers/physics/collision_object_sw.cpp
+++ b/servers/physics/collision_object_sw.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "collision_object_sw.h"
+#include "servers/physics/physics_server_sw.h"
#include "space_sw.h"
void CollisionObjectSW::add_shape(ShapeSW *p_shape, const Transform &p_transform) {
@@ -39,8 +40,12 @@ void CollisionObjectSW::add_shape(ShapeSW *p_shape, const Transform &p_transform
s.bpid = 0; //needs update
shapes.push_back(s);
p_shape->add_owner(this);
- _update_shapes();
- _shapes_changed();
+
+ if (!pending_shape_update_list.in_list()) {
+ PhysicsServerSW::singleton->pending_shape_update_list.add(&pending_shape_update_list);
+ }
+ //_update_shapes();
+ //_shapes_changed();
}
void CollisionObjectSW::set_shape(int p_index, ShapeSW *p_shape) {
@@ -50,8 +55,11 @@ void CollisionObjectSW::set_shape(int p_index, ShapeSW *p_shape) {
shapes[p_index].shape = p_shape;
p_shape->add_owner(this);
- _update_shapes();
- _shapes_changed();
+ if (!pending_shape_update_list.in_list()) {
+ PhysicsServerSW::singleton->pending_shape_update_list.add(&pending_shape_update_list);
+ }
+ //_update_shapes();
+ //_shapes_changed();
}
void CollisionObjectSW::set_shape_transform(int p_index, const Transform &p_transform) {
@@ -59,8 +67,11 @@ void CollisionObjectSW::set_shape_transform(int p_index, const Transform &p_tran
shapes[p_index].xform = p_transform;
shapes[p_index].xform_inv = p_transform.affine_inverse();
- _update_shapes();
- _shapes_changed();
+ if (!pending_shape_update_list.in_list()) {
+ PhysicsServerSW::singleton->pending_shape_update_list.add(&pending_shape_update_list);
+ }
+ //_update_shapes();
+ //_shapes_changed();
}
void CollisionObjectSW::remove_shape(ShapeSW *p_shape) {
@@ -90,7 +101,11 @@ void CollisionObjectSW::remove_shape(int p_index) {
shapes[p_index].shape->remove_owner(this);
shapes.remove(p_index);
- _shapes_changed();
+ if (!pending_shape_update_list.in_list()) {
+ PhysicsServerSW::singleton->pending_shape_update_list.add(&pending_shape_update_list);
+ }
+ //_update_shapes();
+ //_shapes_changed();
}
void CollisionObjectSW::_set_static(bool p_static) {
@@ -202,7 +217,8 @@ void CollisionObjectSW::_shape_changed() {
_shapes_changed();
}
-CollisionObjectSW::CollisionObjectSW(Type p_type) {
+CollisionObjectSW::CollisionObjectSW(Type p_type)
+ : pending_shape_update_list(this) {
_static = true;
type = p_type;
diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h
index dc988aae86..67a8a44944 100644
--- a/servers/physics/collision_object_sw.h
+++ b/servers/physics/collision_object_sw.h
@@ -75,6 +75,8 @@ private:
Transform inv_transform;
bool _static;
+ SelfList<CollisionObjectSW> pending_shape_update_list;
+
void _update_shapes();
protected:
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index 8d6f7b3fd8..2d46770924 100644
--- a/servers/physics/physics_server_sw.cpp
+++ b/servers/physics/physics_server_sw.cpp
@@ -763,6 +763,8 @@ void PhysicsServerSW::body_apply_impulse(RID p_body, const Vector3 &p_pos, const
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
+ _update_shapes();
+
body->apply_impulse(p_pos, p_impulse);
body->wakeup();
};
@@ -772,6 +774,8 @@ void PhysicsServerSW::body_apply_torque_impulse(RID p_body, const Vector3 &p_imp
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
+ _update_shapes();
+
body->apply_torque_impulse(p_impulse);
body->wakeup();
};
@@ -781,6 +785,8 @@ void PhysicsServerSW::body_set_axis_velocity(RID p_body, const Vector3 &p_axis_v
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
+ _update_shapes();
+
Vector3 v = body->get_linear_velocity();
Vector3 axis = p_axis_velocity.normalized();
v -= axis * axis.dot(v);
@@ -793,6 +799,7 @@ void PhysicsServerSW::body_set_axis_lock(RID p_body, BodyAxisLock p_lock) {
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
+
body->set_axis_lock(p_lock);
body->wakeup();
}
@@ -902,6 +909,8 @@ bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, cons
ERR_FAIL_COND_V(!body->get_space(), false);
ERR_FAIL_COND_V(body->get_space()->is_locked(), false);
+ _update_shapes();
+
return body->get_space()->test_body_motion(body, p_from, p_motion, p_margin, r_result);
}
@@ -1209,6 +1218,8 @@ bool PhysicsServerSW::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_a
void PhysicsServerSW::free(RID p_rid) {
+ _update_shapes(); //just in case
+
if (shape_owner.owns(p_rid)) {
ShapeSW *shape = shape_owner.get(p_rid);
@@ -1312,6 +1323,8 @@ void PhysicsServerSW::step(real_t p_step) {
if (!active)
return;
+ _update_shapes();
+
doing_sync = false;
last_step = p_step;
@@ -1409,6 +1422,14 @@ int PhysicsServerSW::get_process_info(ProcessInfo p_info) {
return 0;
}
+void PhysicsServerSW::_update_shapes() {
+
+ while (pending_shape_update_list.first()) {
+ pending_shape_update_list.first()->self()->_shape_changed();
+ pending_shape_update_list.remove(pending_shape_update_list.first());
+ }
+}
+
void PhysicsServerSW::_shape_col_cbk(const Vector3 &p_point_A, const Vector3 &p_point_B, void *p_userdata) {
CollCbkData *cbk = (CollCbkData *)p_userdata;
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
index 2e1fa7065a..99ba302acd 100644
--- a/servers/physics/physics_server_sw.h
+++ b/servers/physics/physics_server_sw.h
@@ -62,6 +62,10 @@ class PhysicsServerSW : public PhysicsServer {
mutable RID_Owner<JointSW> joint_owner;
//void _clear_query(QuerySW *p_query);
+ friend class CollisionObjectSW;
+ SelfList<CollisionObjectSW>::List pending_shape_update_list;
+ void _update_shapes();
+
public:
static PhysicsServerSW *singleton;
diff --git a/servers/physics/shape_sw.cpp b/servers/physics/shape_sw.cpp
index 1845188089..80eeff93d0 100644
--- a/servers/physics/shape_sw.cpp
+++ b/servers/physics/shape_sw.cpp
@@ -954,6 +954,9 @@ Vector3 ConvexPolygonShapeSW::get_moment_of_inertia(real_t p_mass) const {
void ConvexPolygonShapeSW::_setup(const Vector<Vector3> &p_vertices) {
Error err = QuickHull::build(p_vertices, mesh);
+ if (err != OK)
+ ERR_PRINT("Failed to build QuickHull");
+
Rect3 _aabb;
for (int i = 0; i < mesh.vertices.size(); i++) {
diff --git a/servers/physics/step_sw.cpp b/servers/physics/step_sw.cpp
index 79a55e0af1..76b097dda6 100644
--- a/servers/physics/step_sw.cpp
+++ b/servers/physics/step_sw.cpp
@@ -62,7 +62,7 @@ void StepSW::_setup_island(ConstraintSW *p_island, real_t p_delta) {
ConstraintSW *ci = p_island;
while (ci) {
- bool process = ci->setup(p_delta);
+ ci->setup(p_delta);
//todo remove from island if process fails
ci = ci->get_island_next();
}
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 187a0b180b..344c10089a 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -335,6 +335,8 @@ public:
virtual void light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) = 0;
virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0;
virtual bool light_directional_get_blend_splits(RID p_light) const = 0;
+ virtual void light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) = 0;
+ virtual VS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const = 0;
virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) = 0;
virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) = 0;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 0684cb1701..6ad433268f 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -79,7 +79,11 @@ String ShaderLanguage::get_operator_text(Operator p_op) {
"|",
"^",
"~",
- "++"
+ "++",
+ "--",
+ "?",
+ ":",
+ "++",
"--",
"()",
"construct",
@@ -2996,8 +3000,6 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
if (op->op == OP_CONSTRUCT) {
ERR_FAIL_COND_V(op->arguments[0]->type != Node::TYPE_VARIABLE, p_node);
- VariableNode *vn = static_cast<VariableNode *>(op->arguments[0]);
- //StringName name=vn->name;
DataType base = get_scalar_type(op->get_datatype());
@@ -3116,6 +3118,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
tk = _get_token();
+ VariableDeclarationNode *vardecl = alloc_node<VariableDeclarationNode>();
+ vardecl->datatype = type;
+ vardecl->precision = precision;
+
+ p_block->statements.push_back(vardecl);
+
while (true) {
if (tk.type != TK_IDENTIFIER) {
@@ -3133,8 +3141,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
var.type = type;
var.precision = precision;
var.line = tk_line;
+
p_block->variables[name] = var;
+ VariableDeclarationNode::Declaration decl;
+
+ decl.name = name;
+ decl.initializer = NULL;
+
tk = _get_token();
if (tk.type == TK_OP_ASSIGN) {
@@ -3143,22 +3157,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
if (!n)
return ERR_PARSE_ERROR;
- OperatorNode *assign = alloc_node<OperatorNode>();
- VariableNode *vnode = alloc_node<VariableNode>();
- vnode->name = name;
- vnode->datatype_cache = type;
- assign->arguments.push_back(vnode);
- assign->arguments.push_back(n);
- assign->op = OP_ASSIGN;
- p_block->statements.push_back(assign);
- tk = _get_token();
+ decl.initializer = n;
- if (!_validate_operator(assign)) {
- _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(type) + "'");
+ if (var.type != n->get_datatype()) {
+ _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'");
return ERR_PARSE_ERROR;
}
+ tk = _get_token();
}
+ vardecl->declarations.push_back(decl);
+
if (tk.type == TK_COMMA) {
tk = _get_token();
//another variable
@@ -3221,7 +3230,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
//if () {}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after if");
+ _set_error("Expected '(' after while");
return ERR_PARSE_ERROR;
}
@@ -3243,7 +3252,63 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
cf->blocks.push_back(block);
p_block->statements.push_back(cf);
- Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
+ Error err = _parse_block(block, p_builtin_types, true, true, true);
+ if (err)
+ return err;
+ } else if (tk.type == TK_CF_FOR) {
+ //if () {}
+ tk = _get_token();
+ if (tk.type != TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after for");
+ return ERR_PARSE_ERROR;
+ }
+
+ ControlFlowNode *cf = alloc_node<ControlFlowNode>();
+ cf->flow_op = FLOW_OP_FOR;
+
+ BlockNode *init_block = alloc_node<BlockNode>();
+ init_block->parent_block = p_block;
+ init_block->single_statement = true;
+ cf->blocks.push_back(init_block);
+ if (_parse_block(init_block, p_builtin_types, true, false, false) != OK) {
+ return ERR_PARSE_ERROR;
+ }
+
+ Node *n = _parse_and_reduce_expression(init_block, p_builtin_types);
+ if (!n)
+ return ERR_PARSE_ERROR;
+
+ if (n->get_datatype() != TYPE_BOOL) {
+ _set_error("Middle expression is expected to be boolean.");
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = _get_token();
+ if (tk.type != TK_SEMICOLON) {
+ _set_error("Expected ';' after middle expression");
+ return ERR_PARSE_ERROR;
+ }
+
+ cf->expressions.push_back(n);
+
+ n = _parse_and_reduce_expression(init_block, p_builtin_types);
+ if (!n)
+ return ERR_PARSE_ERROR;
+
+ cf->expressions.push_back(n);
+
+ tk = _get_token();
+ if (tk.type != TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' after third expression");
+ return ERR_PARSE_ERROR;
+ }
+
+ BlockNode *block = alloc_node<BlockNode>();
+ block->parent_block = p_block;
+ cf->blocks.push_back(block);
+ p_block->statements.push_back(cf);
+
+ Error err = _parse_block(block, p_builtin_types, true, true, true);
if (err)
return err;
@@ -3320,6 +3385,42 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
}
p_block->statements.push_back(flow);
+ } else if (tk.type == TK_CF_BREAK) {
+
+ if (!p_can_break) {
+ //all is good
+ _set_error("Breaking is not allowed here");
+ }
+
+ ControlFlowNode *flow = alloc_node<ControlFlowNode>();
+ flow->flow_op = FLOW_OP_BREAK;
+
+ pos = _get_tkpos();
+ tk = _get_token();
+ if (tk.type != TK_SEMICOLON) {
+ //all is good
+ _set_error("Expected ';' after break");
+ }
+
+ p_block->statements.push_back(flow);
+ } else if (tk.type == TK_CF_CONTINUE) {
+
+ if (!p_can_break) {
+ //all is good
+ _set_error("Contiuning is not allowed here");
+ }
+
+ ControlFlowNode *flow = alloc_node<ControlFlowNode>();
+ flow->flow_op = FLOW_OP_CONTINUE;
+
+ pos = _get_tkpos();
+ tk = _get_token();
+ if (tk.type != TK_SEMICOLON) {
+ //all is good
+ _set_error("Expected ';' after continue");
+ }
+
+ p_block->statements.push_back(flow);
} else {
@@ -3872,6 +3973,8 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
shader = alloc_node<ShaderNode>();
Error err = _parse_shader(p_functions, p_render_modes, p_shader_types);
+ if (err != OK)
+ ERR_PRINT("Failed to parse shader");
switch (completion_type) {
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index f00b4c5a97..50f5cebeaa 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -266,6 +266,7 @@ public:
TYPE_FUNCTION,
TYPE_BLOCK,
TYPE_VARIABLE,
+ TYPE_VARIABLE_DECLARATION,
TYPE_CONSTANT,
TYPE_OPERATOR,
TYPE_CONTROL_FLOW,
@@ -315,6 +316,25 @@ public:
}
};
+ struct VariableDeclarationNode : public Node {
+
+ DataPrecision precision;
+ DataType datatype;
+
+ struct Declaration {
+
+ StringName name;
+ Node *initializer;
+ };
+
+ Vector<Declaration> declarations;
+ virtual DataType get_datatype() const { return datatype; }
+
+ VariableDeclarationNode() {
+ type = TYPE_VARIABLE_DECLARATION;
+ }
+ };
+
struct ConstantNode : public Node {
DataType datatype;
@@ -346,10 +366,12 @@ public:
Map<StringName, Variable> variables;
List<Node *> statements;
+ bool single_statement;
BlockNode() {
type = TYPE_BLOCK;
parent_block = NULL;
parent_function = NULL;
+ single_statement=false;
}
};
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index ef0d063f83..91c5d430f5 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -102,6 +102,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["ANISOTROPY"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["ANISOTROPY_FLOW"] = ShaderLanguage::TYPE_VEC2;
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["SSS_STRENGTH"] = ShaderLanguage::TYPE_FLOAT;
+ shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["TRANSMISSION"] = ShaderLanguage::TYPE_VEC3;
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["AO"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3;
shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
@@ -137,7 +138,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].modes.insert("unshaded");
shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert");
- shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_half_lambert");
+ shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_lambert_wrap");
shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_oren_nayar");
shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_burley");
shader_modes[VS::SHADER_SPATIAL].modes.insert("diffuse_toon");
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 3953bc5f48..7c7ce46268 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -795,6 +795,7 @@ public:
BIND2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode)
BIND2(light_directional_set_blend_splits, RID, bool)
+ BIND2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode)
/* PROBE API */
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index 0d70b7fc0e..9fb4dc524d 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -101,7 +101,7 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
SWAP(A, B); //lesser always first
}
- if (B->base_type == VS::INSTANCE_LIGHT && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ if (B->base_type == VS::INSTANCE_LIGHT && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceLightData *light = static_cast<InstanceLightData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -119,7 +119,7 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
geom->lighting_dirty = true;
return E; //this element should make freeing faster
- } else if (B->base_type == VS::INSTANCE_REFLECTION_PROBE && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ } else if (B->base_type == VS::INSTANCE_REFLECTION_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -133,7 +133,7 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
geom->reflection_dirty = true;
return E; //this element should make freeing faster
- } else if (B->base_type == VS::INSTANCE_GI_PROBE && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ } else if (B->base_type == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -151,8 +151,6 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
} else if (B->base_type == VS::INSTANCE_GI_PROBE && A->base_type == VS::INSTANCE_LIGHT) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
- InstanceLightData *light = static_cast<InstanceLightData *>(A->base_data);
-
return gi_probe->lights.insert(A);
}
@@ -169,7 +167,7 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
SWAP(A, B); //lesser always first
}
- if (B->base_type == VS::INSTANCE_LIGHT && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ if (B->base_type == VS::INSTANCE_LIGHT && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceLightData *light = static_cast<InstanceLightData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -184,7 +182,7 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
}
geom->lighting_dirty = true;
- } else if (B->base_type == VS::INSTANCE_REFLECTION_PROBE && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ } else if (B->base_type == VS::INSTANCE_REFLECTION_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -196,7 +194,7 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
geom->reflection_dirty = true;
- } else if (B->base_type == VS::INSTANCE_GI_PROBE && (1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
+ } else if (B->base_type == VS::INSTANCE_GI_PROBE && ((1 << A->base_type) & VS::INSTANCE_GEOMETRY_MASK)) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(A->base_data);
@@ -211,8 +209,6 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
} else if (B->base_type == VS::INSTANCE_GI_PROBE && A->base_type == VS::INSTANCE_LIGHT) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
- InstanceLightData *light = static_cast<InstanceLightData *>(A->base_data);
-
Set<Instance *>::Element *E = reinterpret_cast<Set<Instance *>::Element *>(udata);
gi_probe->lights.erase(E);
@@ -886,12 +882,53 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
float max_distance = p_cam_projection.get_z_far();
float shadow_max = VSG::storage->light_get_param(p_instance->base, VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE);
- if (shadow_max > 0) {
+ if (shadow_max > 0 && !p_cam_orthogonal) { //its impractical (and leads to unwanted behaviors) to set max distance in orthogonal camera
max_distance = MIN(shadow_max, max_distance);
}
max_distance = MAX(max_distance, p_cam_projection.get_z_near() + 0.001);
+ float min_distance = MIN(p_cam_projection.get_z_near(), max_distance);
+
+ VS::LightDirectionalShadowDepthRangeMode depth_range_mode = VSG::storage->light_directional_get_shadow_depth_range_mode(p_instance->base);
+
+ if (depth_range_mode == VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED) {
+ //optimize min/max
+ Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform);
+ int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK);
+ Plane base(p_cam_transform.origin, -p_cam_transform.basis.get_axis(2));
+ //check distance max and min
+
+ bool found_items = false;
+ float z_max = -1e20;
+ float z_min = 1e20;
+
+ for (int i = 0; i < cull_count; i++) {
+
+ Instance *instance = instance_shadow_cull_result[i];
+ if (!instance->visible || !((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData *>(instance->base_data)->can_cast_shadows) {
+ continue;
+ }
+
+ float max, min;
+ instance->transformed_aabb.project_range_in_plane(base, min, max);
+
+ if (max > z_max) {
+ z_max = max;
+ }
+
+ if (min < z_min) {
+ z_min = min;
+ }
- float range = max_distance - p_cam_projection.get_z_near();
+ found_items = true;
+ }
+
+ if (found_items) {
+ min_distance = MAX(min_distance, z_min);
+ max_distance = MIN(max_distance, z_max);
+ }
+ }
+
+ float range = max_distance - min_distance;
int splits = 0;
switch (VSG::storage->light_directional_get_shadow_mode(p_instance->base)) {
@@ -902,9 +939,9 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
float distances[5];
- distances[0] = p_cam_projection.get_z_near();
+ distances[0] = min_distance;
for (int i = 0; i < splits; i++) {
- distances[i + 1] = p_cam_projection.get_z_near() + VSG::storage->light_get_param(p_instance->base, VS::LightParam(VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range;
+ distances[i + 1] = min_distance + VSG::storage->light_get_param(p_instance->base, VS::LightParam(VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range;
};
distances[splits] = max_distance;
@@ -984,8 +1021,6 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
{
//camera viewport stuff
- //this trick here is what stabilizes the shadow (make potential jaggies to not move)
- //at the cost of some wasted resolution. Still the quality increase is very well worth it
Vector3 center;
@@ -1006,7 +1041,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
radius = d;
}
- radius *= texture_size / (texture_size - 2.0); //add a texel by each side, so stepified texture will always fit
+ radius *= texture_size / (texture_size - 2.0); //add a texel by each side
if (i == 0) {
first_radius = radius;
@@ -1021,12 +1056,17 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
z_max_cam = z_vec.dot(center) + radius;
z_min_cam = z_vec.dot(center) - radius;
- float unit = radius * 2.0 / texture_size;
+ if (depth_range_mode == VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE) {
+ //this trick here is what stabilizes the shadow (make potential jaggies to not move)
+ //at the cost of some wasted resolution. Still the quality increase is very well worth it
+
+ float unit = radius * 2.0 / texture_size;
- x_max_cam = Math::stepify(x_max_cam, unit);
- x_min_cam = Math::stepify(x_min_cam, unit);
- y_max_cam = Math::stepify(y_max_cam, unit);
- y_min_cam = Math::stepify(y_min_cam, unit);
+ x_max_cam = Math::stepify(x_max_cam, unit);
+ x_min_cam = Math::stepify(x_min_cam, unit);
+ y_max_cam = Math::stepify(y_max_cam, unit);
+ y_min_cam = Math::stepify(y_min_cam, unit);
+ }
}
//now that we now all ranges, we can proceed to make the light frustum planes, for culling octree
@@ -1069,6 +1109,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
}
{
+
CameraMatrix ortho_camera;
real_t half_x = (x_max_cam - x_min_cam) * 0.5;
real_t half_y = (y_max_cam - y_min_cam) * 0.5;
@@ -1375,7 +1416,7 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
gi_probe_update_list.add(&gi_probe->update_element);
}
- } else if ((1 << ins->base_type) & VS::INSTANCE_GEOMETRY_MASK && ins->visible && ins->cast_shadows != VS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
+ } else if (((1 << ins->base_type) & VS::INSTANCE_GEOMETRY_MASK) && ins->visible && ins->cast_shadows != VS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
keep = true;
@@ -2085,7 +2126,7 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
int success_count = 0;
- uint64_t us = OS::get_singleton()->get_ticks_usec();
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
for (int i = 0; i < p_leaf_count; i++) {
@@ -2138,14 +2179,15 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
success_count++;
}
}
- //print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0));
- //print_line("valid cells: " + itos(success_count));
+
+ // print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0));
+ // print_line("valid cells: " + itos(success_count));
} break;
case VS::LIGHT_OMNI:
case VS::LIGHT_SPOT: {
- uint64_t us = OS::get_singleton()->get_ticks_usec();
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
Vector3 light_pos = light_cache.transform.origin;
Vector3 spot_axis = -light_cache.transform.basis.get_axis(2).normalized();
@@ -2244,8 +2286,7 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
light->energy[2] += int32_t(light_b * att * ((cell->albedo) & 0xFF) / 255.0);
}
}
- //print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0));
-
+ // print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0));
} break;
}
}
@@ -2657,18 +2698,17 @@ void VisualServerScene::render_probes() {
} break;
case GI_UPDATE_STAGE_UPLOADING: {
- uint64_t us = OS::get_singleton()->get_ticks_usec();
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
for (int i = 0; i < (int)probe->dynamic.mipmaps_3d.size(); i++) {
- int mmsize = probe->dynamic.mipmaps_3d[i].size();
PoolVector<uint8_t>::Read r = probe->dynamic.mipmaps_3d[i].read();
VSG::storage->gi_probe_dynamic_data_update(probe->dynamic.probe_data, 0, probe->dynamic.grid_size[2] >> i, i, r.ptr());
}
probe->dynamic.updating_stage = GI_UPDATE_STAGE_CHECK;
- //print_line("UPLOAD TIME: "+rtos((OS::get_singleton()->get_ticks_usec()-us)/1000000.0));
+ // print_line("UPLOAD TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0));
} break;
}
}
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index f24049be92..5cf941b93d 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -233,6 +233,7 @@ public:
FUNC2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode)
FUNC2(light_directional_set_blend_splits, RID, bool)
+ FUNC2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode)
/* PROBE API */
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 67b847d127..47a5f4c7f3 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -1420,6 +1420,29 @@ Array VisualServer::mesh_surface_get_arrays(RID p_mesh, int p_surface) const {
return _get_array_from_surface(format, vertex_data, vertex_len, index_data, index_len);
}
+Array VisualServer::mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const {
+
+ Vector<PoolVector<uint8_t> > blend_shape_data = mesh_surface_get_blend_shapes(p_mesh, p_surface);
+ if (blend_shape_data.size() > 0) {
+ int vertex_len = mesh_surface_get_array_len(p_mesh, p_surface);
+
+ PoolVector<uint8_t> index_data = mesh_surface_get_index_array(p_mesh, p_surface);
+ int index_len = mesh_surface_get_array_index_len(p_mesh, p_surface);
+
+ uint32_t format = mesh_surface_get_format(p_mesh, p_surface);
+
+ Array blend_shape_array;
+ blend_shape_array.resize(blend_shape_data.size());
+ for (int i = 0; i < blend_shape_data.size(); i++) {
+ blend_shape_array.set(i, _get_array_from_surface(format, blend_shape_data[i], vertex_len, index_data, index_len));
+ }
+
+ return blend_shape_array;
+ } else {
+ return Array();
+ }
+}
+
void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_draw"), &VisualServer::draw);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index d516013ee2..72f36f6b65 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -267,6 +267,7 @@ public:
virtual PoolVector<uint8_t> mesh_surface_get_index_array(RID p_mesh, int p_surface) const = 0;
virtual Array mesh_surface_get_arrays(RID p_mesh, int p_surface) const;
+ virtual Array mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const;
virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const = 0;
virtual PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const = 0;
@@ -406,6 +407,14 @@ public:
virtual void light_directional_set_shadow_mode(RID p_light, LightDirectionalShadowMode p_mode) = 0;
virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0;
+ enum LightDirectionalShadowDepthRangeMode {
+ LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE,
+ LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED,
+
+ };
+
+ virtual void light_directional_set_shadow_depth_range_mode(RID p_light, LightDirectionalShadowDepthRangeMode p_range_mode) = 0;
+
/* PROBE API */
virtual RID reflection_probe_create() = 0;
@@ -620,6 +629,7 @@ public:
ENV_BG_CLEAR_COLOR,
ENV_BG_COLOR,
ENV_BG_SKY,
+ ENV_BG_COLOR_SKY,
ENV_BG_CANVAS,
ENV_BG_KEEP,
ENV_BG_MAX