diff options
Diffstat (limited to 'modules')
330 files changed, 4694 insertions, 1038 deletions
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index fc876a81cf..ed05e51e53 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -87,8 +87,8 @@ RID BulletPhysicsServer3D::shape_create(ShapeType p_shape) { ShapeBullet *shape = nullptr; switch (p_shape) { - case SHAPE_PLANE: { - shape = bulletnew(PlaneShapeBullet); + case SHAPE_WORLD_BOUNDARY: { + shape = bulletnew(WorldBoundaryShapeBullet); } break; case SHAPE_SPHERE: { shape = bulletnew(SphereShapeBullet); diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 40e785d699..88ffb9ec67 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -110,7 +110,7 @@ btEmptyShape *ShapeBullet::create_shape_empty() { return bulletnew(btEmptyShape); } -btStaticPlaneShape *ShapeBullet::create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant) { +btStaticPlaneShape *ShapeBullet::create_shape_world_boundary(const btVector3 &planeNormal, btScalar planeConstant) { return bulletnew(btStaticPlaneShape(planeNormal, planeConstant)); } @@ -164,32 +164,32 @@ btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope return r; } -/* PLANE */ +/* World boundary */ -PlaneShapeBullet::PlaneShapeBullet() : +WorldBoundaryShapeBullet::WorldBoundaryShapeBullet() : ShapeBullet() {} -void PlaneShapeBullet::set_data(const Variant &p_data) { +void WorldBoundaryShapeBullet::set_data(const Variant &p_data) { setup(p_data); } -Variant PlaneShapeBullet::get_data() const { +Variant WorldBoundaryShapeBullet::get_data() const { return plane; } -PhysicsServer3D::ShapeType PlaneShapeBullet::get_type() const { - return PhysicsServer3D::SHAPE_PLANE; +PhysicsServer3D::ShapeType WorldBoundaryShapeBullet::get_type() const { + return PhysicsServer3D::SHAPE_WORLD_BOUNDARY; } -void PlaneShapeBullet::setup(const Plane &p_plane) { +void WorldBoundaryShapeBullet::setup(const Plane &p_plane) { plane = p_plane; notifyShapeChanged(); } -btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *WorldBoundaryShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { btVector3 btPlaneNormal; G_TO_B(plane.normal, btPlaneNormal); - return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d)); + return prepare(WorldBoundaryShapeBullet::create_shape_world_boundary(btPlaneNormal, plane.d)); } /* Sphere */ diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 5080d13d99..0822399b5e 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -81,7 +81,7 @@ public: public: static class btEmptyShape *create_shape_empty(); - static class btStaticPlaneShape *create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant); + static class btStaticPlaneShape *create_shape_world_boundary(const btVector3 &planeNormal, btScalar planeConstant); static class btSphereShape *create_shape_sphere(btScalar radius); static class btBoxShape *create_shape_box(const btVector3 &boxHalfExtents); static class btCapsuleShape *create_shape_capsule(btScalar radius, btScalar height); @@ -93,11 +93,11 @@ public: static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope); }; -class PlaneShapeBullet : public ShapeBullet { +class WorldBoundaryShapeBullet : public ShapeBullet { Plane plane; public: - PlaneShapeBullet(); + WorldBoundaryShapeBullet(); virtual void set_data(const Variant &p_data); virtual Variant get_data() const; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index bf11cc7f68..452fb32d9d 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -280,7 +280,7 @@ void CSGShape3D::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const } void CSGShape3D::_update_shape() { - if (parent) { + if (parent || !is_inside_tree()) { return; } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index fc0bef3ba2..ceb6d5a5f0 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -171,7 +171,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me } if (class_exists(p_member_name)) { - push_error(vformat(R"(The class "%s" shadows a native class.)", p_member_name), p_member_node); + push_error(vformat(R"(The member "%s" shadows a native class.)", p_member_name), p_member_node); return ERR_PARSE_ERROR; } @@ -218,6 +218,17 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name); } + if (p_class->identifier) { + StringName class_name = p_class->identifier->name; + if (class_exists(class_name)) { + push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier); + } else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) { + push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier); + } else if (ProjectSettings::get_singleton()->has_autoload(class_name) && ProjectSettings::get_singleton()->get_autoload(class_name).is_singleton) { + push_error(vformat(R"(Class "%s" hides an autoload singleton.)", class_name), p_class->identifier); + } + } + GDScriptParser::DataType result; // Set datatype for class. @@ -242,6 +253,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, int extends_index = 0; if (!p_class->extends_path.is_empty()) { + if (p_class->extends_path.is_relative_path()) { + p_class->extends_path = class_type.script_path.get_base_dir().plus_file(p_class->extends_path).simplify_path(); + } Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path); if (parser.is_null()) { push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class); @@ -681,8 +695,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas specified_type.is_meta_type = false; } - GDScriptParser::DataType datatype = member.constant->get_datatype(); + GDScriptParser::DataType datatype; if (member.constant->initializer) { + datatype = member.constant->initializer->get_datatype(); if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer); const_fold_array(array); @@ -1172,12 +1187,28 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } } - if (!list_resolved) { + GDScriptParser::DataType variable_type; + if (list_resolved) { + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? + p_for->variable->set_datatype(variable_type); + } else { resolve_node(p_for->list); + if (p_for->list->datatype.has_container_element_type()) { + variable_type = p_for->list->datatype.get_container_element_type(); + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + } else if (p_for->list->datatype.is_typed_container_type()) { + variable_type = p_for->list->datatype.get_typed_container_type(); + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + } else { + // Last resort + // TODO: Must other cases be handled? Must we mark as unsafe? + variable_type.type_source = GDScriptParser::DataType::UNDETECTED; + variable_type.kind = GDScriptParser::DataType::VARIANT; + } } - - // TODO: If list is a typed array, the variable should be an element. - // Also applicable for constant range() (so variable is int or float). + p_for->variable->set_datatype(variable_type); resolve_suite(p_for->loop); p_for->set_datatype(p_for->loop->get_datatype()); @@ -1465,8 +1496,7 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame } if (p_parameter->datatype_specifier != nullptr) { - resolve_datatype(p_parameter->datatype_specifier); - result = p_parameter->datatype_specifier->get_datatype(); + result = resolve_datatype(p_parameter->datatype_specifier); result.is_meta_type = false; if (p_parameter->default_value != nullptr) { @@ -1478,6 +1508,10 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame } } + if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) { + push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value); + } + p_parameter->set_datatype(result); } @@ -1748,6 +1782,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig identifier->variable_source->set_datatype(id_type); } } break; + case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: { + GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype(); + if (!id_type.is_hard_type()) { + id_type = assigned_value_type; + id_type.type_source = GDScriptParser::DataType::INFERRED; + id_type.is_constant = false; + identifier->parameter_source->set_datatype(id_type); + } + } break; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: { GDScriptParser::DataType id_type = identifier->variable_source->get_datatype(); if (!id_type.is_hard_type()) { @@ -2409,13 +2452,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->is_constant = true; p_identifier->reduced_value = result; p_identifier->set_datatype(type_from_variant(result, p_identifier)); - } else { + } else if (base.is_hard_type()) { push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); } } else { switch (base.builtin_type) { case Variant::NIL: { - push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + if (base.is_hard_type()) { + push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + } return; } case Variant::DICTIONARY: { @@ -2436,7 +2481,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod return; } } - push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + if (base.is_hard_type()) { + push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + } } } } @@ -2516,14 +2563,29 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod while (outer != nullptr) { if (outer->has_member(name)) { const GDScriptParser::ClassNode::Member &member = outer->get_member(name); - if (member.type == GDScriptParser::ClassNode::Member::CONSTANT) { - // TODO: Make sure loops won't cause problem. And make special error message for those. - // For out-of-order resolution: - reduce_expression(member.constant->initializer); - p_identifier->set_datatype(member.get_datatype()); - p_identifier->is_constant = true; - p_identifier->reduced_value = member.constant->initializer->reduced_value; - return; + switch (member.type) { + case GDScriptParser::ClassNode::Member::CONSTANT: { + // TODO: Make sure loops won't cause problem. And make special error message for those. + // For out-of-order resolution: + reduce_expression(member.constant->initializer); + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = true; + p_identifier->reduced_value = member.constant->initializer->reduced_value; + return; + } break; + case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = true; + p_identifier->reduced_value = member.enum_value.value; + return; + } break; + case GDScriptParser::ClassNode::Member::ENUM: { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = false; + return; + } break; + default: + break; } } outer = outer->outer; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index bed67b55f0..1127488db8 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -864,6 +864,12 @@ void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_ function->default_arguments.push_back(opcodes.size()); } +void GDScriptByteCodeGenerator::write_store_global(const Address &p_dst, int p_global_index) { + append(GDScriptFunction::OPCODE_STORE_GLOBAL, 1); + append(p_dst); + append(p_global_index); +} + void GDScriptByteCodeGenerator::write_store_named_global(const Address &p_dst, const StringName &p_global) { append(GDScriptFunction::OPCODE_STORE_NAMED_GLOBAL, 1); append(p_dst); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index ce1a043b28..dcc11ebdce 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -454,6 +454,7 @@ public: virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override; virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override; + virtual void write_store_global(const Address &p_dst, int p_global_index) override; virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) override; virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override; virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 07f50d14dc..8121053245 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -51,7 +51,9 @@ GDScriptParser *GDScriptParserRef::get_parser() const { Error GDScriptParserRef::raise_status(Status p_new_status) { ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA); - Error result = OK; + if (result != OK) { + return result; + } while (p_new_status > status) { switch (status) { @@ -86,14 +88,6 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { } } if (result != OK) { - if (parser != nullptr) { - memdelete(parser); - parser = nullptr; - } - if (analyzer != nullptr) { - memdelete(analyzer); - analyzer = nullptr; - } return result; } } diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 943638d29f..9fb661d031 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -54,6 +54,7 @@ private: GDScriptParser *parser = nullptr; GDScriptAnalyzer *analyzer = nullptr; Status status = EMPTY; + Error result = OK; String path; friend class GDScriptCache; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 7713d13bc8..e6ecc92d55 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -115,6 +115,7 @@ public: virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0; virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0; + virtual void write_store_global(const Address &p_dst, int p_global_index) = 0; virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) = 0; virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0; virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 736f6eae79..a8aef84db3 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -35,6 +35,8 @@ #include "gdscript_cache.h" #include "gdscript_utility_functions.h" +#include "core/config/project_settings.h" + bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { if (codegen.function_node && codegen.function_node->is_static) { return false; @@ -316,10 +318,21 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } + // Try globals. if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { - int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; - Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx]; - return codegen.add_constant(global); // TODO: Get type. + // If it's an autoload singleton, we postpone to load it at runtime. + // This is so one autoload doesn't try to load another before it's compiled. + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + if (autoloads.has(identifier) && autoloads[identifier].is_singleton) { + GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype())); + int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; + gen->write_store_global(global, idx); + return global; + } else { + int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; + Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + return codegen.add_constant(global); + } } // Try global classes. @@ -427,7 +440,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code break; case GDScriptParser::DictionaryNode::LUA_TABLE: // Lua-style: key is an identifier interpreted as StringName. - StringName key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name; + StringName key = dn->elements[i].key->reduced_value.operator StringName(); element = codegen.add_constant(key); break; } @@ -951,7 +964,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Perform operator if any. if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { - GDScriptCodeGenerator::Address value = codegen.add_temporary(); + GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype())); if (subscript->is_attribute) { gen->write_get_named(value, name, prev_base); } else { diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 1acb9ceddc..9287df2ea0 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -914,6 +914,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 5; } break; DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE); + case OPCODE_STORE_GLOBAL: { + text += "store global "; + text += DADDR(1); + text += " = "; + text += String::num_int64(_code_ptr[ip + 2]); + + incr += 3; + } break; case OPCODE_STORE_NAMED_GLOBAL: { text += "store named global "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index f809a4dab8..f79e5726ce 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1733,7 +1733,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, } } - if (is_function_parameter && p_context.current_function && p_context.current_class) { + if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) { // Check if it's override of native function, then we can assume the type from the signature. GDScriptParser::DataType base_type = p_context.current_class->base_type; while (base_type.is_set()) { diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index b21cb47910..9d076a8e4c 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -348,6 +348,7 @@ public: OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, OPCODE_ITERATE_PACKED_COLOR_ARRAY, OPCODE_ITERATE_OBJECT, + OPCODE_STORE_GLOBAL, OPCODE_STORE_NAMED_GLOBAL, OPCODE_TYPE_ADJUST_BOOL, OPCODE_TYPE_ADJUST_INT, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6c3d4367e4..c901d9f68f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -337,12 +337,29 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ tokenizer.set_cursor_position(cursor_line, cursor_column); script_path = p_script_path; current = tokenizer.scan(); - // Avoid error as the first token. - while (current.type == GDScriptTokenizer::Token::ERROR) { - push_error(current.literal); + // Avoid error or newline as the first token. + // The latter can mess with the parser when opening files filled exclusively with comments and newlines. + while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) { + if (current.type == GDScriptTokenizer::Token::ERROR) { + push_error(current.literal); + } current = tokenizer.scan(); } +#ifdef DEBUG_ENABLED + // Warn about parsing an empty script file: + if (current.type == GDScriptTokenizer::Token::TK_EOF) { + // Create a dummy Node for the warning, pointing to the very beginning of the file + Node *nd = alloc_node<PassNode>(); + nd->start_line = 1; + nd->start_column = 0; + nd->end_line = 1; + nd->leftmost_column = 0; + nd->rightmost_column = 0; + push_warning(nd, GDScriptWarning::EMPTY_FILE); + } +#endif + push_multiline(false); // Keep one for the whole parsing. parse_program(); pop_multiline(); @@ -2106,22 +2123,34 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN operation->operation = UnaryOpNode::OP_NEGATIVE; operation->variant_op = Variant::OP_NEGATE; operation->operand = parse_precedence(PREC_SIGN, false); + if (operation->operand == nullptr) { + push_error(R"(Expected expression after "-" operator.)"); + } break; case GDScriptTokenizer::Token::PLUS: operation->operation = UnaryOpNode::OP_POSITIVE; operation->variant_op = Variant::OP_POSITIVE; operation->operand = parse_precedence(PREC_SIGN, false); + if (operation->operand == nullptr) { + push_error(R"(Expected expression after "+" operator.)"); + } break; case GDScriptTokenizer::Token::TILDE: operation->operation = UnaryOpNode::OP_COMPLEMENT; operation->variant_op = Variant::OP_BIT_NEGATE; operation->operand = parse_precedence(PREC_BIT_NOT, false); + if (operation->operand == nullptr) { + push_error(R"(Expected expression after "~" operator.)"); + } break; case GDScriptTokenizer::Token::NOT: case GDScriptTokenizer::Token::BANG: operation->operation = UnaryOpNode::OP_LOGIC_NOT; operation->variant_op = Variant::OP_NOT; operation->operand = parse_precedence(PREC_LOGIC_NOT, false); + if (operation->operand == nullptr) { + push_error(vformat(R"(Expected expression after "%s" operator.)", op_type == GDScriptTokenizer::Token::NOT ? "not" : "!")); + } break; default: return nullptr; // Unreachable. @@ -2258,6 +2287,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio operation->false_expr = parse_precedence(PREC_TERNARY, false); + if (operation->false_expr == nullptr) { + push_error(R"(Expected expression after "else".)"); + } + return operation; } @@ -2369,8 +2402,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode } #ifdef DEBUG_ENABLED - if (has_operator && source_variable != nullptr && source_variable->assignments == 0) { - push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name); + if (source_variable != nullptr) { + if (has_operator && source_variable->assignments == 0) { + push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name); + } + + source_variable->assignments += 1; } #endif @@ -2451,8 +2488,13 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode switch (dictionary->style) { case DictionaryNode::LUA_TABLE: - if (key != nullptr && key->type != Node::IDENTIFIER) { - push_error("Expected identifier as LUA-style dictionary key."); + if (key != nullptr && key->type != Node::IDENTIFIER && key->type != Node::LITERAL) { + push_error("Expected identifier or string as LUA-style dictionary key."); + advance(); + break; + } + if (key != nullptr && key->type == Node::LITERAL && static_cast<LiteralNode *>(key)->value.get_type() != Variant::STRING) { + push_error("Expected identifier or string as LUA-style dictionary key."); advance(); break; } @@ -2466,7 +2508,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode } if (key != nullptr) { key->is_constant = true; - key->reduced_value = static_cast<IdentifierNode *>(key)->name; + if (key->type == Node::IDENTIFIER) { + key->reduced_value = static_cast<IdentifierNode *>(key)->name; + } else if (key->type == Node::LITERAL) { + key->reduced_value = StringName(static_cast<LiteralNode *>(key)->value.operator String()); + } } break; case DictionaryNode::PYTHON_DICT: @@ -2546,6 +2592,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode * subscript->base = p_previous_operand; subscript->index = parse_expression(false); + if (subscript->index == nullptr) { + push_error(R"(Expected expression after "[".)"); + } + pop_multiline(); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)"); @@ -3541,6 +3591,39 @@ String GDScriptParser::DataType::to_string() const { ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range."); } +static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) { + switch (p_type) { + case Variant::PACKED_BYTE_ARRAY: + case Variant::PACKED_INT32_ARRAY: + case Variant::PACKED_INT64_ARRAY: + return Variant::INT; + case Variant::PACKED_FLOAT32_ARRAY: + case Variant::PACKED_FLOAT64_ARRAY: + return Variant::FLOAT; + case Variant::PACKED_STRING_ARRAY: + return Variant::STRING; + case Variant::PACKED_VECTOR2_ARRAY: + return Variant::VECTOR2; + case Variant::PACKED_VECTOR3_ARRAY: + return Variant::VECTOR3; + case Variant::PACKED_COLOR_ARRAY: + return Variant::COLOR; + default: + return Variant::NIL; + } +} + +bool GDScriptParser::DataType::is_typed_container_type() const { + return kind == GDScriptParser::DataType::BUILTIN && _variant_type_to_typed_array_element_type(builtin_type) != Variant::NIL; +} + +GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() const { + GDScriptParser::DataType type; + type.kind = GDScriptParser::DataType::BUILTIN; + type.builtin_type = _variant_type_to_typed_array_element_type(builtin_type); + return type; +} + /*---------- PRETTY PRINT FOR DEBUG ----------*/ #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 4902f0d4a6..a641c1052d 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -161,6 +161,10 @@ public: container_element_type = nullptr; } + bool is_typed_container_type() const; + + GDScriptParser::DataType get_typed_container_type() const; + bool operator==(const DataType &p_other) const { if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) { return true; // Can be consireded equal for parsing purposes. diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 882256b7e3..bf21c8510a 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -322,6 +322,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \ &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \ &&OPCODE_ITERATE_OBJECT, \ + &&OPCODE_STORE_GLOBAL, \ &&OPCODE_STORE_NAMED_GLOBAL, \ &&OPCODE_TYPE_ADJUST_BOOL, \ &&OPCODE_TYPE_ADJUST_INT, \ @@ -1231,6 +1232,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX); +#ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + err_text = "Trying to cast a freed object."; + OPCODE_BREAK; + } +#endif + Callable::CallError err; Variant::construct(to_type, *dst, (const Variant **)&src, 1, err); @@ -1255,6 +1263,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); #ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + err_text = "Trying to cast a freed object."; + OPCODE_BREAK; + } if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Invalid cast: can't convert a non-object value to an object type."; OPCODE_BREAK; @@ -1283,6 +1295,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!base_type); #ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + err_text = "Trying to cast a freed object."; + OPCODE_BREAK; + } if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'."; OPCODE_BREAK; @@ -3116,6 +3132,18 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_STORE_GLOBAL) { + CHECK_SPACE(3); + int global_idx = _code_ptr[ip + 2]; + GD_ERR_BREAK(global_idx < 0 || global_idx >= GDScriptLanguage::get_singleton()->get_global_array_size()); + + GET_INSTRUCTION_ARG(dst, 0); + *dst = GDScriptLanguage::get_singleton()->get_global_array()[global_idx]; + + ip += 3; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_STORE_NAMED_GLOBAL) { CHECK_SPACE(3); int globalname_idx = _code_ptr[ip + 2]; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index ad41b60a4e..7a483a16ba 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -145,6 +145,9 @@ String GDScriptWarning::get_message() const { case REDUNDANT_AWAIT: { return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)"; } + case EMPTY_FILE: { + return "Empty script file."; + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -190,6 +193,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "ASSERT_ALWAYS_TRUE", "ASSERT_ALWAYS_FALSE", "REDUNDANT_AWAIT", + "EMPTY_FILE", }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 4b295b5eb8..8de46b08c1 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -68,6 +68,7 @@ public: ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false. REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine). + EMPTY_FILE, // A script file is empty. WARNING_MAX, }; diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index 6225e5d1eb..c383830c82 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -415,6 +415,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { TestResult result; result.status = GDTEST_OK; result.output = String(); + result.passed = false; Error err = OK; @@ -496,7 +497,12 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { } return result; } - + // Script files matching this pattern are allowed to not contain a test() function. + if (source_file.match("*.notest.gd")) { + enable_stdout(); + result.passed = check_output(result.output); + return result; + } // Test running. const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name); if (test_function_element == nullptr) { diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd new file mode 100644 index 0000000000..9b722ea50a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd @@ -0,0 +1,3 @@ +func test(): + # Error here. + print(2.2 << 4) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out new file mode 100644 index 0000000000..7dee854d1a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid operands to operator <<, float and int. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd new file mode 100644 index 0000000000..4502960105 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd @@ -0,0 +1,3 @@ +func test(): + # Error here. + print(2 << 4.4) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out new file mode 100644 index 0000000000..1879fc1adf --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid operands to operator <<, int and float. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd new file mode 100644 index 0000000000..0ad2337c15 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd @@ -0,0 +1,5 @@ +const CONSTANT = 25 + + +func test(): + CONSTANT(123) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out new file mode 100644 index 0000000000..f4051cd02c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Member "CONSTANT" is not a function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd new file mode 100644 index 0000000000..7a922cd73e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd @@ -0,0 +1,6 @@ +func test(): + var lua_dict = { + a = 1, + b = 2, + a = 3, # Duplicate isn't allowed. + } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out new file mode 100644 index 0000000000..ffdfa56645 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Key "a" was already used in this dictionary (at line 3). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd new file mode 100644 index 0000000000..933e737ac7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd @@ -0,0 +1,6 @@ +func test(): + var lua_dict_with_string = { + a = 1, + b = 2, + "a" = 3, # Duplicate isn't allowed. + } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out new file mode 100644 index 0000000000..ffdfa56645 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Key "a" was already used in this dictionary (at line 3). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd new file mode 100644 index 0000000000..3b8c83e9cb --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd @@ -0,0 +1,6 @@ +func test(): + var python_dict = { + "a": 1, + "b": 2, + "a": 3, # Duplicate isn't allowed. + } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out new file mode 100644 index 0000000000..ffdfa56645 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Key "a" was already used in this dictionary (at line 3). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd new file mode 100644 index 0000000000..cf9a0ce038 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd @@ -0,0 +1,7 @@ +enum Size { + # Error here. Enum values must be integers. + S = 0.0, +} + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out new file mode 100644 index 0000000000..b315d20508 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Enum values must be integers. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd new file mode 100644 index 0000000000..cd9b8fabc4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd @@ -0,0 +1,7 @@ +enum Size { + # Error here. Enum values must be integers. + S = "hello", +} + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out new file mode 100644 index 0000000000..b315d20508 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Enum values must be integers. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd new file mode 100644 index 0000000000..4346503fc2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd @@ -0,0 +1,6 @@ +func function(): + pass + + +func test(): + function = 25 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd new file mode 100644 index 0000000000..b8c0b7a8d3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd @@ -0,0 +1,3 @@ +func test(): + # Error here. Array indices must be integers. + print([0, 1][true]) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out new file mode 100644 index 0000000000..015ad756f8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid index type "bool" for a base of type "Array". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd new file mode 100644 index 0000000000..c159e03140 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd @@ -0,0 +1,2 @@ +func test(): + print(true + true) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out new file mode 100644 index 0000000000..c1dc7c7d08 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid operands to operator +, bool and bool. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd new file mode 100644 index 0000000000..6aec2e0796 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd @@ -0,0 +1,2 @@ +func test(): + print({"hello": "world"} + {"godot": "engine"}) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out new file mode 100644 index 0000000000..1b4451edbe --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid operands "Dictionary" and "Dictionary" for "+" operator. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd new file mode 100644 index 0000000000..eb2a6a0ce7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd @@ -0,0 +1,2 @@ +func test(): + print("hello" + ["world"]) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out new file mode 100644 index 0000000000..6d44c6c1bd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid operands "String" and "Array" for "+" operator. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd new file mode 100644 index 0000000000..a7426e88da --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd @@ -0,0 +1,5 @@ +func test(): + var i = 12 + # Constants must be made of a constant, deterministic expression. + # A constant that depends on a variable's value is not a constant expression. + const TEST = 13 + i diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out new file mode 100644 index 0000000000..c40830f123 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Assigned value for constant "TEST" isn't a constant expression. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd new file mode 100644 index 0000000000..d88c02d6ee --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd @@ -0,0 +1,3 @@ +func test(): + # Number separators may not be placed at the beginning of a number. + var __ = _123 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out new file mode 100644 index 0000000000..cfb558bf45 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Identifier "_123" not declared in the current scope. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd new file mode 100644 index 0000000000..70bdadf291 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd @@ -0,0 +1,6 @@ +func args(a, b): + print(a) + print(b) + +func test(): + args(1,) diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_argument.out b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out index fc2a891109..fc2a891109 100644 --- a/modules/gdscript/tests/scripts/parser/errors/missing_argument.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd new file mode 100644 index 0000000000..059d774927 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd @@ -0,0 +1,4 @@ +var property = 25 + +func test(): + property() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out new file mode 100644 index 0000000000..94d6c26a1a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Member "property" is not a function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd new file mode 100644 index 0000000000..91401d32fc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd @@ -0,0 +1,7 @@ +# See also `parser-warnings/shadowed-constant.gd`. +const TEST = 25 + + +func test(): + # Error here (trying to set a new value to a constant). + TEST = 50 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd new file mode 100644 index 0000000000..97f3e55e81 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd @@ -0,0 +1,5 @@ +func test(): + const TEST = 25 + + # Error here (can't assign a new value to a constant). + TEST = 50 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd new file mode 100644 index 0000000000..722a8fcdb7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd @@ -0,0 +1,11 @@ +# `class` extends RefCounted by default. +class Say: + func say(): + super() + print("say something") + + +func test(): + # RefCounted doesn't have a `say()` method, so the `super()` call in the method + # definition will cause a run-time error. + Say.new().say() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out new file mode 100644 index 0000000000..e3dbf81850 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Function "say()" not found in base RefCounted. diff --git a/modules/gdscript/tests/scripts/analyzer/features/as.gd b/modules/gdscript/tests/scripts/analyzer/features/as.gd new file mode 100644 index 0000000000..13a36147c0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/as.gd @@ -0,0 +1,16 @@ +func test(): + var some_bool = 5 as bool + var some_int = 5 as int + var some_float = 5 as float + print(typeof(some_bool)) + print(typeof(some_int)) + print(typeof(some_float)) + + print() + + var some_bool_typed := 5 as bool + var some_int_typed := 5 as int + var some_float_typed := 5 as float + print(typeof(some_bool_typed)) + print(typeof(some_int_typed)) + print(typeof(some_float_typed)) diff --git a/modules/gdscript/tests/scripts/analyzer/features/as.out b/modules/gdscript/tests/scripts/analyzer/features/as.out new file mode 100644 index 0000000000..bcf84aa6f6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/as.out @@ -0,0 +1,8 @@ +GDTEST_OK +1 +2 +3 + +1 +2 +3 diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd new file mode 100644 index 0000000000..f64dce26c9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd @@ -0,0 +1,9 @@ +func inferred_parameter(param = null): + if param == null: + param = Node.new() + param.name = "Ok" + print(param.name) + param.free() + +func test(): + inferred_parameter() diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out new file mode 100644 index 0000000000..0e9f482af4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out @@ -0,0 +1,2 @@ +GDTEST_OK +Ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd new file mode 100644 index 0000000000..135b6c3d85 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd @@ -0,0 +1,16 @@ +extends Node + +const NO_TYPE_CONST = 0 +const TYPE_CONST: int = 1 +const GUESS_TYPE_CONST := 2 + +class Test: + var a = NO_TYPE_CONST + var b = TYPE_CONST + var c = GUESS_TYPE_CONST + +func test(): + var test_instance = Test.new() + prints("a", test_instance.a, test_instance.a == NO_TYPE_CONST) + prints("b", test_instance.b, test_instance.b == TYPE_CONST) + prints("c", test_instance.c, test_instance.c == GUESS_TYPE_CONST) diff --git a/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out new file mode 100644 index 0000000000..a96bb84246 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out @@ -0,0 +1,4 @@ +GDTEST_OK +a 0 true +b 1 true +c 2 true diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd new file mode 100644 index 0000000000..5f57c5b8c2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd @@ -0,0 +1,14 @@ +extends Node + +enum Named { VALUE_A, VALUE_B, VALUE_C = 42 } + +class Test: + var a = Named.VALUE_A + var b = Named.VALUE_B + var c = Named.VALUE_C + +func test(): + var test_instance = Test.new() + prints("a", test_instance.a, test_instance.a == Named.VALUE_A) + prints("b", test_instance.b, test_instance.b == Named.VALUE_B) + prints("c", test_instance.c, test_instance.c == Named.VALUE_C) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out new file mode 100644 index 0000000000..c160839da3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out @@ -0,0 +1,4 @@ +GDTEST_OK +a 0 true +b 1 true +c 42 true diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd new file mode 100644 index 0000000000..26edce353d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd @@ -0,0 +1,14 @@ +extends Node + +enum { VALUE_A, VALUE_B, VALUE_C = 42 } + +class Test: + var a = VALUE_A + var b = VALUE_B + var c = VALUE_C + +func test(): + var test_instance = Test.new() + prints("a", test_instance.a, test_instance.a == VALUE_A) + prints("b", test_instance.b, test_instance.b == VALUE_B) + prints("c", test_instance.c, test_instance.c == VALUE_C) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out new file mode 100644 index 0000000000..c160839da3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out @@ -0,0 +1,4 @@ +GDTEST_OK +a 0 true +b 1 true +c 42 true diff --git a/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd new file mode 100644 index 0000000000..b45f99fdd0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd @@ -0,0 +1,3 @@ +func test(): + # Arrays with consecutive commas are not allowed. + var array = ["arrays",,,,] diff --git a/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out new file mode 100644 index 0000000000..4ef8526065 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression as array element. diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd new file mode 100644 index 0000000000..17d5e078e5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd @@ -0,0 +1,2 @@ +func test(): + var hello == "world" diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out new file mode 100644 index 0000000000..b150fc0d16 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected end of statement after variable declaration, found "==" instead. diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd new file mode 100644 index 0000000000..8b5f620889 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd @@ -0,0 +1,2 @@ +func test(): + var hello === "world" diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out new file mode 100644 index 0000000000..b150fc0d16 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected end of statement after variable declaration, found "==" instead. diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd new file mode 100644 index 0000000000..8c3a908532 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd @@ -0,0 +1,4 @@ +func test(): + # Error here. + if foo = 25: + print(foo) diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out new file mode 100644 index 0000000000..e8f9130706 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Assignment is not allowed inside an expression. diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd new file mode 100644 index 0000000000..126a3227ea --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd @@ -0,0 +1,2 @@ +func test(): + var hello = "world" = "test" diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out new file mode 100644 index 0000000000..e8f9130706 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Assignment is not allowed inside an expression. diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd new file mode 100644 index 0000000000..a99557fa3c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd @@ -0,0 +1,4 @@ +func test(): + # Error here. + if var foo = 25: + print(foo) diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out new file mode 100644 index 0000000000..e84f4652ac --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected conditional expression after "if". diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd new file mode 100644 index 0000000000..031ea523c8 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd @@ -0,0 +1,2 @@ +func test(): + var = "world" diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out new file mode 100644 index 0000000000..a4bd8beef1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected variable name after "var". diff --git a/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd new file mode 100644 index 0000000000..b52a6defcb --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd @@ -0,0 +1,2 @@ +func test(): + print(~) diff --git a/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out new file mode 100644 index 0000000000..ceabe42d3c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "~" operator. diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd new file mode 100644 index 0000000000..b3ea1ba1f6 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd @@ -0,0 +1,2 @@ +func test(): + print(not) diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out new file mode 100644 index 0000000000..6cf191ea98 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "not" operator. diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd new file mode 100644 index 0000000000..8a33079193 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd @@ -0,0 +1,2 @@ +func test(): + print(!) diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out new file mode 100644 index 0000000000..87fcc5e2b0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "!" operator. diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd new file mode 100644 index 0000000000..d13d713454 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -0,0 +1,6 @@ +# Error here. `class_name` should be used *before* annotations, not after. +@icon("res://path/to/optional/icon.svg") +class_name HelloWorld + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out new file mode 100644 index 0000000000..0bcc8acc55 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +"class_name" should be used before annotations. diff --git a/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd new file mode 100644 index 0000000000..49fb4ffedf --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd @@ -0,0 +1,3 @@ +func test(): + var TEST = 50 + const TEST = 25 diff --git a/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out new file mode 100644 index 0000000000..407f094ca0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +There is already a variable named "TEST" declared in this scope. diff --git a/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd new file mode 100644 index 0000000000..2581d873dd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd @@ -0,0 +1,7 @@ +func hello(arg1): + print(arg1) + + +func test(): + # Error here. + hello(arg1 = 25) diff --git a/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out new file mode 100644 index 0000000000..e8f9130706 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Assignment is not allowed inside an expression. diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd new file mode 100644 index 0000000000..a8f7cf1810 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd @@ -0,0 +1,5 @@ +const test = 25 + + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out new file mode 100644 index 0000000000..c614acd094 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Function "test" has the same name as a previously declared constant. diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd new file mode 100644 index 0000000000..5c86710a40 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd @@ -0,0 +1,7 @@ +func test(): + pass + + +# Error here. The difference with `variable-conflicts-function.gd` is that here, +# the function is defined *before* the variable. +var test = 25 diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out new file mode 100644 index 0000000000..551db61531 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Variable "test" has the same name as a previously declared function. diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd new file mode 100644 index 0000000000..081b9faf4b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd @@ -0,0 +1,3 @@ +func test(): + # Error here. + var 23test = "is not a valid identifier" diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out new file mode 100644 index 0000000000..a4bd8beef1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected variable name after "var". diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd new file mode 100644 index 0000000000..fa4d6b5cac --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd @@ -0,0 +1,3 @@ +func test(): + # Error here. + var "yes" = "is not a valid identifier" diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out new file mode 100644 index 0000000000..a4bd8beef1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected variable name after "var". diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd b/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd deleted file mode 100644 index c56ad94095..0000000000 --- a/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd +++ /dev/null @@ -1,6 +0,0 @@ -func args(a, b): - print(a) - print(b) - -func test(): - args(1,) diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd index a1077e1985..8af5f123cc 100644 --- a/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd +++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd @@ -1,2 +1,2 @@ func test(): - var a = ("missing paren ->" + var a = ("missing paren ->" diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd index 62cb633e9e..0e5e5ce060 100644 --- a/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd +++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd @@ -1,3 +1,3 @@ func test(): - if true # Missing colon here. - print("true") + if true # Missing colon here. + print("true") diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd new file mode 100644 index 0000000000..1f66935329 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd @@ -0,0 +1,4 @@ +func test(): + var x = 1 if false else + print("oops") + print(x) diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out new file mode 100644 index 0000000000..dab6b0a1ad --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "else". diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd index 116b0151da..7a35bf688c 100644 --- a/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd +++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd @@ -1,6 +1,6 @@ func args(a, b): - print(a) - print(b) + print(a) + print(b) func test(): - args(1,2 + args(1,2 diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd new file mode 100644 index 0000000000..193f824702 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd @@ -0,0 +1,3 @@ +func test(): + var a = 0 + print(a--) diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out new file mode 100644 index 0000000000..b6b577a277 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "-" operator. diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd new file mode 100644 index 0000000000..035d27638c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd @@ -0,0 +1,3 @@ +func test(): + var a = 0 + print(a++) diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out new file mode 100644 index 0000000000..24eb76593a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "+" operator. diff --git a/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd new file mode 100644 index 0000000000..71a03fbc0d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd @@ -0,0 +1,3 @@ +func test(): + # Number separators may not be placed right next to each other. + var __ = 1__23 diff --git a/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out new file mode 100644 index 0000000000..71a3c2fd6a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Only one underscore can be used as a numeric separator. diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd index 3875ce3936..df388a21de 100644 --- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd +++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd @@ -1,3 +1,5 @@ extends Node + + func test(): - var a = $ # Expected some node path. + var a = $ # Expected some node path. diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd new file mode 100644 index 0000000000..c289c9d976 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd @@ -0,0 +1,2 @@ +func test(): + var while = "it's been a while" diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out new file mode 100644 index 0000000000..a4bd8beef1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected variable name after "var". diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd new file mode 100644 index 0000000000..204259f981 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd @@ -0,0 +1,5 @@ +func test(): + const TEST = 25 + + # Error here (can't redeclare a constant on the same scope). + const TEST = 50 diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out new file mode 100644 index 0000000000..d67cc92953 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +There is already a constant named "TEST" declared in this scope. diff --git a/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd new file mode 100644 index 0000000000..c30c05e4da --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd @@ -0,0 +1,3 @@ +func test(): + var array = [1, 2, 3] + array[] = 4 diff --git a/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out new file mode 100644 index 0000000000..7017c7b4aa --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression after "[". diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd new file mode 100644 index 0000000000..0d8843df20 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd @@ -0,0 +1,3 @@ +func test(): + const TEST = 25 + var TEST = 50 diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out new file mode 100644 index 0000000000..d67cc92953 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +There is already a constant named "TEST" declared in this scope. diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd new file mode 100644 index 0000000000..ce2c8784d6 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd @@ -0,0 +1,6 @@ +var test = 25 + +# Error here. The difference with `variable-conflicts-function.gd` is that here, +# the function is defined *before* the variable. +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out new file mode 100644 index 0000000000..daeaca40ec --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Function "test" has the same name as a previously declared variable. diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd index 6fd2692d47..babe39068c 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd @@ -1,3 +1,5 @@ extends Node + + func test(): - $23 # Can't use number here. + $23 # Can't use number here. diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd index 1836d42226..b6b1cf3e52 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd @@ -1,3 +1,5 @@ extends Node + + func test(): - $MyNode/23 # Can't use number here. + $MyNode/23 # Can't use number here. diff --git a/modules/gdscript/tests/scripts/parser/features/array.gd b/modules/gdscript/tests/scripts/parser/features/array.gd new file mode 100644 index 0000000000..828ce8d134 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/array.gd @@ -0,0 +1,16 @@ +func test(): + # Indexing from the beginning: + print([1, 2, 3][0]) + print([1, 2, 3][1]) + print([1, 2, 3][2]) + + # Indexing from the end: + print([1, 2, 3][-1]) + print([1, 2, 3][-2]) + print([1, 2, 3][-3]) + + # Float indices are currently allowed, but should probably be an error? + print([1, 2, 3][0.4]) + print([1, 2, 3][0.8]) + print([1, 2, 3][1.0]) + print([1, 2, 3][-1.0]) diff --git a/modules/gdscript/tests/scripts/parser/features/array.out b/modules/gdscript/tests/scripts/parser/features/array.out new file mode 100644 index 0000000000..cf576c59e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/array.out @@ -0,0 +1,11 @@ +GDTEST_OK +1 +2 +3 +3 +2 +1 +1 +1 +2 +3 diff --git a/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd new file mode 100644 index 0000000000..de502c6ed1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd @@ -0,0 +1,50 @@ +enum Flags { + FIRE = 1 << 1, + ICE = 1 << 2, + SLIPPERY = 1 << 3, + STICKY = 1 << 4, + NONSOLID = 1 << 5, + + ALL = FIRE | ICE | SLIPPERY | STICKY | NONSOLID, +} + + +func test(): + var flags = Flags.FIRE | Flags.SLIPPERY + print(flags) + + flags = Flags.FIRE & Flags.SLIPPERY + print(flags) + + flags = Flags.FIRE ^ Flags.SLIPPERY + print(flags) + + flags = Flags.ALL & (Flags.FIRE | Flags.ICE) + print(flags) + + flags = (Flags.ALL & Flags.FIRE) | Flags.ICE + print(flags) + + flags = Flags.ALL & Flags.FIRE | Flags.ICE + print(flags) + + # Enum value must be casted to an integer. Otherwise, a parser error is emitted. + flags &= int(Flags.ICE) + print(flags) + + flags ^= int(Flags.ICE) + print(flags) + + flags |= int(Flags.STICKY | Flags.SLIPPERY) + print(flags) + + print() + + var num = 2 << 4 + print(num) + + num <<= 2 + print(num) + + num >>= 2 + print(num) diff --git a/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out new file mode 100644 index 0000000000..410e358a05 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out @@ -0,0 +1,14 @@ +GDTEST_OK +10 +0 +10 +6 +6 +6 +4 +0 +24 + +32 +128 +32 diff --git a/modules/gdscript/tests/scripts/parser/features/class.gd b/modules/gdscript/tests/scripts/parser/features/class.gd new file mode 100644 index 0000000000..6652f85ad9 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class.gd @@ -0,0 +1,25 @@ +# Test non-nested/slightly nested class architecture. +class Test: + var number = 25 + var string = "hello" + + +class TestSub extends Test: + var other_string = "bye" + + +class TestConstructor: + func _init(argument = 10): + print(str("constructor with argument ", argument)) + + +func test(): + var test_instance = Test.new() + test_instance.number = 42 + + var test_sub = TestSub.new() + assert(test_sub.number == 25) # From Test. + assert(test_sub.other_string == "bye") # From TestSub. + + TestConstructor.new() + TestConstructor.new(500) diff --git a/modules/gdscript/tests/scripts/parser/features/class.out b/modules/gdscript/tests/scripts/parser/features/class.out new file mode 100644 index 0000000000..94dc2d6003 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class.out @@ -0,0 +1,3 @@ +GDTEST_OK +constructor with argument 10 +constructor with argument 500 diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd b/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd new file mode 100644 index 0000000000..3f9b4ea86e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd @@ -0,0 +1,33 @@ +# Test deeply nested class architectures. +class Test: + var depth = 1 + + class Nested: + var depth_nested = 10 + + +class Test2 extends Test: + var depth2 = 2 + + +class Test3 extends Test2: + var depth3 = 3 + + +class Test4 extends Test3: + var depth4 = 4 + + class Nested2: + var depth4_nested = 100 + + +func test(): + print(Test.new().depth) + print(Test2.new().depth) + print(Test2.new().depth2) + print(Test3.new().depth) + print(Test3.new().depth3) + print(Test4.new().depth) + print(Test4.new().depth4) + print(Test.Nested.new().depth_nested) + print(Test4.Nested2.new().depth4_nested) diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance.out b/modules/gdscript/tests/scripts/parser/features/class_inheritance.out new file mode 100644 index 0000000000..75bdde3d94 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance.out @@ -0,0 +1,10 @@ +GDTEST_OK +1 +1 +2 +1 +3 +1 +4 +10 +100 diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd new file mode 100644 index 0000000000..8bd188e247 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd @@ -0,0 +1,5 @@ +class_name HelloWorld +@icon("res://path/to/optional/icon.svg") + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.out b/modules/gdscript/tests/scripts/parser/features/class_name.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_name.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/concatenation.gd b/modules/gdscript/tests/scripts/parser/features/concatenation.gd new file mode 100644 index 0000000000..e8335c9823 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/concatenation.gd @@ -0,0 +1,4 @@ +func test(): + print(20 + 20) + print("hello" + "world") + print([1, 2] + [3, 4]) diff --git a/modules/gdscript/tests/scripts/parser/features/concatenation.out b/modules/gdscript/tests/scripts/parser/features/concatenation.out new file mode 100644 index 0000000000..23bff08f49 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/concatenation.out @@ -0,0 +1,4 @@ +GDTEST_OK +40 +helloworld +[1, 2, 3, 4] diff --git a/modules/gdscript/tests/scripts/parser/features/constants.gd b/modules/gdscript/tests/scripts/parser/features/constants.gd new file mode 100644 index 0000000000..013c9c074f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/constants.gd @@ -0,0 +1,11 @@ +func test(): + const _TEST = 12 + 34 - 56 * 78 + const _STRING = "yes" + const _VECTOR = Vector2(5, 6) + const _ARRAY = [] + const _DICTIONARY = {"this": "dictionary"} + + # Create user constants from built-in constants. + const _HELLO = PI + TAU + const _INFINITY = INF + const _NOT_A_NUMBER = NAN diff --git a/modules/gdscript/tests/scripts/parser/features/constants.out b/modules/gdscript/tests/scripts/parser/features/constants.out new file mode 100644 index 0000000000..6093e4a6ca --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/constants.out @@ -0,0 +1,33 @@ +GDTEST_OK +>> WARNING +>> Line: 2 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '__TEST' +>> WARNING +>> Line: 3 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_STRING' is declared but never used in the block. If this is intended, prefix it with an underscore: '__STRING' +>> WARNING +>> Line: 4 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_VECTOR' is declared but never used in the block. If this is intended, prefix it with an underscore: '__VECTOR' +>> WARNING +>> Line: 5 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_ARRAY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__ARRAY' +>> WARNING +>> Line: 6 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_DICTIONARY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__DICTIONARY' +>> WARNING +>> Line: 9 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_HELLO' is declared but never used in the block. If this is intended, prefix it with an underscore: '__HELLO' +>> WARNING +>> Line: 10 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INFINITY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INFINITY' +>> WARNING +>> Line: 11 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_NOT_A_NUMBER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__NOT_A_NUMBER' diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.gd b/modules/gdscript/tests/scripts/parser/features/dictionary.gd new file mode 100644 index 0000000000..99afe166c7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary.gd @@ -0,0 +1,37 @@ +func test(): + # Non-string keys are valid. + print({ 12: "world" }[12]) + + var contents = { + 0: "zero", + 0.0: "zero point zero", + null: "null", + false: "false", + []: "empty array", + Vector2i(): "zero Vector2i", + 15: { + 22: { + 4: ["nesting", "arrays"], + }, + }, + } + + print(contents[0.0]) + # Making sure declaration order doesn't affect things... + print({ 0.0: "zero point zero", 0: "zero", null: "null", false: "false", []: "empty array" }[0]) + print({ 0.0: "zero point zero", 0: "zero", null: "null", false: "false", []: "empty array" }[0.0]) + + print(contents[null]) + print(contents[false]) + print(contents[[]]) + print(contents[Vector2i()]) + print(contents[15]) + print(contents[15][22]) + print(contents[15][22][4]) + print(contents[15][22][4][0]) + print(contents[15][22][4][1]) + + # Currently fails with "invalid get index 'hello' on base Dictionary". + # Both syntaxes are valid however. + #print({ "hello": "world" }["hello"]) + #print({ "hello": "world" }.hello) diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out new file mode 100644 index 0000000000..54083c1afc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary.out @@ -0,0 +1,14 @@ +GDTEST_OK +world +zero point zero +zero +zero point zero +null +false +empty array +zero Vector2i +{22:{4:[nesting, arrays]}} +{4:[nesting, arrays]} +[nesting, arrays] +nesting +arrays diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd new file mode 100644 index 0000000000..fdd6de2348 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd @@ -0,0 +1,9 @@ +func test(): + var lua_dict = { + a = 1, + "b" = 2, # Using strings are allowed too. + "with spaces" = 3, # Especially useful when key has spaces... + "2" = 4, # ... or invalid identifiers. + } + + print(lua_dict) diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out new file mode 100644 index 0000000000..447d7e223c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out @@ -0,0 +1,2 @@ +GDTEST_OK +{2:4, a:1, b:2, with spaces:3} diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd new file mode 100644 index 0000000000..cce8538ddd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd @@ -0,0 +1,12 @@ +func test(): + # Mixing Python-style and Lua-style syntax in the same dictionary declaration + # is allowed. + var dict = { + "hello": { + world = { + "is": "beautiful", + }, + }, + } + + print(dict) diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out new file mode 100644 index 0000000000..62be807a1f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out @@ -0,0 +1,2 @@ +GDTEST_OK +{hello:{world:{is:beautiful}}} diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd new file mode 100644 index 0000000000..8ba558e91d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd @@ -0,0 +1,20 @@ +extends Node + + +func test(): + # Create the required node structure. + var hello = Node.new() + hello.name = "Hello" + add_child(hello) + var world = Node.new() + world.name = "World" + hello.add_child(world) + + # All the ways of writing node paths below with the `$` operator are valid. + # Results are assigned to variables to avoid warnings. + var __ = $Hello + __ = $"Hello" + __ = $Hello/World + __ = $"Hello/World" + __ = $"Hello/.." + __ = $"Hello/../Hello/World" diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/enum.gd b/modules/gdscript/tests/scripts/parser/features/enum.gd new file mode 100644 index 0000000000..bbc66f6f3d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/enum.gd @@ -0,0 +1,14 @@ +enum Size { + S = -10, + M, + L = 0, + XL = 10, + XXL, +} + +func test(): + print(Size.S) + print(Size.M) + print(Size.L) + print(Size.XL) + print(Size.XXL) diff --git a/modules/gdscript/tests/scripts/parser/features/enum.out b/modules/gdscript/tests/scripts/parser/features/enum.out new file mode 100644 index 0000000000..6f3a4a3e49 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/enum.out @@ -0,0 +1,6 @@ +GDTEST_OK +-10 +-9 +0 +10 +11 diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd new file mode 100644 index 0000000000..51e7d4a8ed --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd @@ -0,0 +1,11 @@ +@export var example = 99 +@export_range(0, 100) var example_range = 100 +@export_range(0, 100, 1) var example_range_step = 101 +@export_range(0, 100, 1, "or_greater") var example_range_step_or_greater = 102 + + +func test(): + print(example) + print(example_range) + print(example_range_step) + print(example_range_step_or_greater) diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.out b/modules/gdscript/tests/scripts/parser/features/export_variable.out new file mode 100644 index 0000000000..b455196359 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.out @@ -0,0 +1,5 @@ +GDTEST_OK +99 +100 +101 +102 diff --git a/modules/gdscript/tests/scripts/parser/features/float_notation.gd b/modules/gdscript/tests/scripts/parser/features/float_notation.gd new file mode 100644 index 0000000000..b207b88820 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/float_notation.gd @@ -0,0 +1,24 @@ +func test(): + # The following floating-point notations are all valid: + print(is_equal_approx(123., 123)) + print(is_equal_approx(.123, 0.123)) + print(is_equal_approx(.123e4, 1230)) + print(is_equal_approx(123.e4, 1.23e6)) + print(is_equal_approx(.123e-1, 0.0123)) + print(is_equal_approx(123.e-1, 12.3)) + + # Same as above, but with negative numbers. + print(is_equal_approx(-123., -123)) + print(is_equal_approx(-.123, -0.123)) + print(is_equal_approx(-.123e4, -1230)) + print(is_equal_approx(-123.e4, -1.23e6)) + print(is_equal_approx(-.123e-1, -0.0123)) + print(is_equal_approx(-123.e-1, -12.3)) + + # Same as above, but with explicit positive numbers (which is redundant). + print(is_equal_approx(+123., +123)) + print(is_equal_approx(+.123, +0.123)) + print(is_equal_approx(+.123e4, +1230)) + print(is_equal_approx(+123.e4, +1.23e6)) + print(is_equal_approx(+.123e-1, +0.0123)) + print(is_equal_approx(+123.e-1, +12.3)) diff --git a/modules/gdscript/tests/scripts/parser/features/float_notation.out b/modules/gdscript/tests/scripts/parser/features/float_notation.out new file mode 100644 index 0000000000..041c4439b0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/float_notation.out @@ -0,0 +1,19 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/for_range.gd b/modules/gdscript/tests/scripts/parser/features/for_range.gd new file mode 100644 index 0000000000..fd1d002b82 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/for_range.gd @@ -0,0 +1,39 @@ +func test(): + for i in range(5): + print(i) + + print() + + # Equivalent to the above `for` loop: + for i in 5: + print(i) + + print() + + for i in range(1, 5): + print(i) + + print() + + for i in range(1, -5, -1): + print(i) + + print() + + for i in [2, 4, 6, -8]: + print(i) + + print() + + for i in [true, false]: + print(i) + + print() + + for i in [Vector2i(10, 20), Vector2i(-30, -40)]: + print(i) + + print() + + for i in "Hello_Unicôde_world!_🦄": + print(i) diff --git a/modules/gdscript/tests/scripts/parser/features/for_range.out b/modules/gdscript/tests/scripts/parser/features/for_range.out new file mode 100644 index 0000000000..50b2c856c5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/for_range.out @@ -0,0 +1,58 @@ +GDTEST_OK +0 +1 +2 +3 +4 + +0 +1 +2 +3 +4 + +1 +2 +3 +4 + +1 +0 +-1 +-2 +-3 +-4 + +2 +4 +6 +-8 + +true +false + +(10, 20) +(-30, -40) + +H +e +l +l +o +_ +U +n +i +c +ô +d +e +_ +w +o +r +l +d +! +_ +🦄 diff --git a/modules/gdscript/tests/scripts/parser/features/in.gd b/modules/gdscript/tests/scripts/parser/features/in.gd new file mode 100644 index 0000000000..f7296017c5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/in.gd @@ -0,0 +1,14 @@ +func test(): + print("dot" in "Godot") + print(not "i" in "team") + + print(true in [true, false]) + print(not null in [true, false]) + print(null in [null]) + + print(26 in [8, 26, 64, 100]) + print(not Vector2i(10, 20) in [Vector2i(20, 10)]) + + print("apple" in { "apple": "fruit" }) + print("apple" in { "apple": null }) + print(not "apple" in { "fruit": "apple" }) diff --git a/modules/gdscript/tests/scripts/parser/features/in.out b/modules/gdscript/tests/scripts/parser/features/in.out new file mode 100644 index 0000000000..7533f6ff54 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/in.out @@ -0,0 +1,11 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/match.gd b/modules/gdscript/tests/scripts/parser/features/match.gd new file mode 100644 index 0000000000..4d05490aa5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match.gd @@ -0,0 +1,18 @@ +func test(): + var i = "Hello" + match i: + "Hello": + print("hello") + # This will fall through to the default case below. + continue + "Good bye": + print("bye") + _: + print("default") + + var j = 25 + match j: + 26: + print("This won't match") + _: + print("This will match") diff --git a/modules/gdscript/tests/scripts/parser/features/match.out b/modules/gdscript/tests/scripts/parser/features/match.out new file mode 100644 index 0000000000..732885c7a2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match.out @@ -0,0 +1,4 @@ +GDTEST_OK +hello +default +This will match diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd new file mode 100644 index 0000000000..3b30998853 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd @@ -0,0 +1,7 @@ +func test(): + var __ = [ + "this", + "is", "a","multiline", + + "array", "with mixed indentation and trailing comma", + ] diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd new file mode 100644 index 0000000000..e108cd23d4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd @@ -0,0 +1,10 @@ +func test(): + var __ = { + "multiline": "dictionary","should": "work", + "even with": "a trailing comma", + } + + __ = { + this_also_applies = "to the", + lua_style_syntax = null, foo = null, + } diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_if.gd b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd new file mode 100644 index 0000000000..86152f4543 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd @@ -0,0 +1,14 @@ +func test(): + # Line breaks are allowed within parentheses. + if ( + 1 == 1 + and 2 == 2 and + 3 == 3 + ): + pass + + # Alternatively, backslashes can be used. + if 1 == 1 \ + and 2 == 2 and \ + 3 == 3: + pass diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_if.out b/modules/gdscript/tests/scripts/parser/features/multiline_if.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_if.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd b/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd new file mode 100644 index 0000000000..7f5bba85e7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd @@ -0,0 +1,15 @@ +func test(): + var __ = """ + This is a standalone string, not a multiline comment. + Writing both "double" quotes and 'simple' quotes is fine as + long as there is only ""one"" or ''two'' of those in a row, not more. + + If you have more quotes, they need to be escaped like this: \"\"\" + """ + __ = ''' + Another standalone string, this time with single quotes. + Writing both "double" quotes and 'simple' quotes is fine as + long as there is only ""one"" or ''two'' of those in a row, not more. + + If you have more quotes, they need to be escaped like this: \'\'\' + ''' diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_strings.out b/modules/gdscript/tests/scripts/parser/features/multiline_strings.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_strings.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd b/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd new file mode 100644 index 0000000000..11a40fc00e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd @@ -0,0 +1,17 @@ +func test(): + Vector2( + 1, + 2 + ) + + Vector3( + 3, + 3.5, + 4, # Trailing comma should work. + ) + + Vector2i(1, 2,) # Trailing comma should work. + + Vector3i(6, + 9, + 12) diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_vector.out b/modules/gdscript/tests/scripts/parser/features/multiline_vector.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/multiline_vector.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd new file mode 100644 index 0000000000..b9bd19c9c5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd @@ -0,0 +1,22 @@ +func test(): + print(+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++2.718) + + print() + + print(------------------------------------------------------------------2.718) + print(-------------------------------------------------------------------2.718) + + print() + + print(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~999) + + print() + + print(+-+-+-----+------------+++++++---++--++--+--+---+--++2.718) + + print() + + print(2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 0.444) + print(153023902390239 % 550 % 29 % 27 % 23 % 17) + print(2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2) + print(8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ -8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8) diff --git a/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out new file mode 100644 index 0000000000..048cfbdfae --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out @@ -0,0 +1,82 @@ +GDTEST_OK +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +>> WARNING +>> Line: 19 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. +2.718 + +2.718 +-2.718 + +-1000 + +-2.718 + +36900.9009009009 +2 +8192 +-8 diff --git a/modules/gdscript/tests/scripts/parser/features/nested_array.gd b/modules/gdscript/tests/scripts/parser/features/nested_array.gd new file mode 100644 index 0000000000..3caef96391 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_array.gd @@ -0,0 +1,5 @@ +func test(): + var array = [[[[[[[[[[15]]]]]]]]]] + print(array[0][0][0][0][0][0][0][0]) + print(array[0][0][0][0][0][0][0][0][0]) + print(array[0][0][0][0][0][0][0][0][0][0]) diff --git a/modules/gdscript/tests/scripts/parser/features/nested_array.out b/modules/gdscript/tests/scripts/parser/features/nested_array.out new file mode 100644 index 0000000000..46c6ce3874 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_array.out @@ -0,0 +1,4 @@ +GDTEST_OK +[[15]] +[15] +15 diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd new file mode 100644 index 0000000000..d67e142156 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd @@ -0,0 +1,6 @@ +func test(): + var dictionary = {1: {2: {3: {4: {5: {6: {7: {8: {"key": "value"}}}}}}}}} + print(dictionary[1][2][3][4][5][6][7]) + print(dictionary[1][2][3][4][5][6][7][8]) + print(dictionary[1][2][3][4][5][6][7][8].key) + print(dictionary[1][2][3][4][5][6][7][8]["key"]) diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out new file mode 100644 index 0000000000..4009160439 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out @@ -0,0 +1,5 @@ +GDTEST_OK +{8:{key:value}} +{key:value} +value +value diff --git a/modules/gdscript/tests/scripts/parser/features/nested_if.gd b/modules/gdscript/tests/scripts/parser/features/nested_if.gd new file mode 100644 index 0000000000..7282d08497 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_if.gd @@ -0,0 +1,57 @@ +func test(): + # 20 levels of nesting (and then some). + if true: + print("1") + if true: + print("2") + if true: + print("3") + if true: + print("4") + if true: + print("5") + if true: + print("6") + if true: + print("7") + if true: + print("8") + if true: + print("9") + if true: + print("10") + if true: + print("11") + if true: + print("12") + if true: + print("13") + if true: + print("14") + if true: + print("15") + if true: + print("16") + if true: + print("17") + if true: + print("18") + if true: + print("19") + if true: + print("20") + if false: + print("End") + if true: + if true: + if true: + if true: + if true: + if true: + if true: + if true: + if true: + if true: + if true: + if true: + print("This won't be printed") diff --git a/modules/gdscript/tests/scripts/parser/features/nested_if.out b/modules/gdscript/tests/scripts/parser/features/nested_if.out new file mode 100644 index 0000000000..c2d2e29a06 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_if.out @@ -0,0 +1,21 @@ +GDTEST_OK +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.gd b/modules/gdscript/tests/scripts/parser/features/nested_match.gd new file mode 100644 index 0000000000..aaddcc7e83 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_match.gd @@ -0,0 +1,79 @@ +func test(): + # 20 levels of nesting (and then some). + var number = 1234 + match number: + 1234: + print("1") + match number: + 1234: + print("2") + match number: + 1234: + print("3") + continue + _: + print("Should also be printed") + match number: + 1234: + print("4") + match number: + _: + print("5") + match number: + false: + print("Should not be printed") + true: + print("Should not be printed") + "hello": + print("Should not be printed") + 1234: + print("6") + match number: + _: + print("7") + match number: + 1234: + print("8") + match number: + _: + print("9") + match number: + 1234: + print("10") + match number: + _: + print("11") + match number: + 1234: + print("12") + match number: + _: + print("13") + match number: + 1234: + print("14") + match number: + _: + print("15") + match number: + _: + print("16") + match number: + 1234: + print("17") + match number: + _: + print("18") + match number: + 1234: + print("19") + match number: + _: + print("20") + match number: + []: + print("Should not be printed") + _: + print("Should not be printed") + 5678: + print("Should not be printed either") diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.out b/modules/gdscript/tests/scripts/parser/features/nested_match.out new file mode 100644 index 0000000000..651d76cc59 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_match.out @@ -0,0 +1,22 @@ +GDTEST_OK +1 +2 +3 +Should also be printed +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 diff --git a/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd new file mode 100644 index 0000000000..3fef73b9be --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd @@ -0,0 +1,65 @@ +func test(): + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((print("Hello world!")))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + print(((((((((((((((((((((((((((((((((((((((((((((((((((((((((("Hello world 2!")))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + print( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + ( + (2)) + ((4)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) diff --git a/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out new file mode 100644 index 0000000000..27221a56bb --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out @@ -0,0 +1,4 @@ +GDTEST_OK +Hello world! +Hello world 2! +6 diff --git a/modules/gdscript/tests/scripts/parser/features/number_separators.gd b/modules/gdscript/tests/scripts/parser/features/number_separators.gd new file mode 100644 index 0000000000..f5f5661cae --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/number_separators.gd @@ -0,0 +1,12 @@ +func test(): + # `_` can be used as a separator for numbers in GDScript. + # It can be placed anywhere in the number, except at the beginning. + # Currently, GDScript in the `master` branch only allows using one separator + # per number. + # Results are assigned to variables to avoid warnings. + var __ = 1_23 + __ = 123_ # Trailing number separators are OK. + __ = 12_3 + __ = 123_456 + __ = 0x1234_5678 + __ = 0b1001_0101 diff --git a/modules/gdscript/tests/scripts/parser/features/number_separators.out b/modules/gdscript/tests/scripts/parser/features/number_separators.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/number_separators.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/operator_assign.gd b/modules/gdscript/tests/scripts/parser/features/operator_assign.gd new file mode 100644 index 0000000000..b5f07675ca --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/operator_assign.gd @@ -0,0 +1,8 @@ +func test(): + var i = 0 + i += 5 + i -= 4 + i *= 10 + i %= 8 + i /= 0.25 + print(round(i)) diff --git a/modules/gdscript/tests/scripts/parser/features/operator_assign.out b/modules/gdscript/tests/scripts/parser/features/operator_assign.out new file mode 100644 index 0000000000..b0cb63ef59 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/operator_assign.out @@ -0,0 +1,2 @@ +GDTEST_OK +8 diff --git a/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd new file mode 100644 index 0000000000..9e4b360fb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd @@ -0,0 +1,37 @@ +# 4.0+ replacement for `setget`: +var _backing: int = 0 +var property: + get: + return _backing + 1000 + set(value): + _backing = value - 1000 + + +func test(): + print("Not using self:") + print(property) + print(_backing) + property = 5000 + print(property) + print(_backing) + _backing = -50 + print(property) + print(_backing) + property = 5000 + print(property) + print(_backing) + + # In Godot 4.0 and later, using `self` no longer makes a difference for + # getter/setter execution in GDScript. + print("Using self:") + print(self.property) + print(self._backing) + self.property = 5000 + print(self.property) + print(self._backing) + self._backing = -50 + print(self.property) + print(self._backing) + self.property = 5000 + print(self.property) + print(self._backing) diff --git a/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out new file mode 100644 index 0000000000..560e0c3bd7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out @@ -0,0 +1,19 @@ +GDTEST_OK +Not using self: +1000 +0 +5000 +4000 +950 +-50 +5000 +4000 +Using self: +5000 +4000 +5000 +4000 +950 +-50 +5000 +4000 diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd index 08f2eedb2d..d50776c25c 100644 --- a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd +++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd @@ -1,2 +1,5 @@ func test(): - print("A"); print("B") + print("A"); print("B") + + # Multiple semicolons and whitespace between them is also valid. + print("A"); ;;;;; ; print("B");; diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out index fc03f3efe8..bd7f38f516 100644 --- a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out +++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out @@ -1,3 +1,5 @@ GDTEST_OK A B +A +B diff --git a/modules/gdscript/tests/scripts/parser/features/space_indentation.gd b/modules/gdscript/tests/scripts/parser/features/space_indentation.gd new file mode 100644 index 0000000000..0a4887c199 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/space_indentation.gd @@ -0,0 +1,4 @@ +func test(): + # 2-space indentation should work, even though the GDScript style guide recommends tabs. + if true: + pass diff --git a/modules/gdscript/tests/scripts/parser/features/space_indentation.out b/modules/gdscript/tests/scripts/parser/features/space_indentation.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/space_indentation.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.gd b/modules/gdscript/tests/scripts/parser/features/static_typing.gd new file mode 100644 index 0000000000..d42632c82d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/static_typing.gd @@ -0,0 +1,13 @@ +func test(): + # The following lines are equivalent: + var _integer: int = 1 + var _integer2 : int = 1 + var _inferred := 1 + var _inferred2 : = 1 + + # Type inference is automatic for constants. + const _INTEGER = 1 + const _INTEGER_REDUNDANT_TYPED : int = 1 + const _INTEGER_REDUNDANT_TYPED2 : int = 1 + const _INTEGER_REDUNDANT_INFERRED := 1 + const _INTEGER_REDUNDANT_INFERRED2 : = 1 diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.out b/modules/gdscript/tests/scripts/parser/features/static_typing.out new file mode 100644 index 0000000000..92ce7bc0e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/static_typing.out @@ -0,0 +1,21 @@ +GDTEST_OK +>> WARNING +>> Line: 9 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INTEGER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER' +>> WARNING +>> Line: 10 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INTEGER_REDUNDANT_TYPED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED' +>> WARNING +>> Line: 11 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INTEGER_REDUNDANT_TYPED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED2' +>> WARNING +>> Line: 12 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INTEGER_REDUNDANT_INFERRED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED' +>> WARNING +>> Line: 13 +>> UNUSED_LOCAL_CONSTANT +>> The local constant '_INTEGER_REDUNDANT_INFERRED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED2' diff --git a/modules/gdscript/tests/scripts/parser/features/string_formatting.gd b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd new file mode 100644 index 0000000000..a91837145d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd @@ -0,0 +1,18 @@ +func test(): + print("hello %s" % "world" == "hello world") + print("hello %s" % true == "hello true") + print("hello %s" % false == "hello false") + + print("hello %d" % 25 == "hello 25") + print("hello %d %d" % [25, 42] == "hello 25 42") + # Pad with spaces. + print("hello %3d" % 25 == "hello 25") + # Pad with zeroes. + print("hello %03d" % 25 == "hello 025") + + print("hello %.02f" % 0.123456 == "hello 0.12") + + # Dynamic padding: + # <https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_format_string.html#dynamic-padding> + print("hello %*.*f" % [7, 3, 0.123456] == "hello 0.123") + print("hello %0*.*f" % [7, 3, 0.123456] == "hello 000.123") diff --git a/modules/gdscript/tests/scripts/parser/features/string_formatting.out b/modules/gdscript/tests/scripts/parser/features/string_formatting.out new file mode 100644 index 0000000000..7533f6ff54 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/string_formatting.out @@ -0,0 +1,11 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/super.gd b/modules/gdscript/tests/scripts/parser/features/super.gd new file mode 100644 index 0000000000..f5ae2a74a7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/super.gd @@ -0,0 +1,60 @@ +class Say: + var prefix = "S" + + func greet(): + prefix = "S Greeted" + print("hello") + + func say(name): + print(prefix, " say something ", name) + + +class SayAnotherThing extends Say: + # This currently crashes the engine. + #var prefix = "SAT" + + func greet(): + prefix = "SAT Greeted" + print("hi") + + func say(name): + print(prefix, " say another thing ", name) + + +class SayNothing extends Say: + # This currently crashes the engine. + #var prefix = "SN" + + func greet(): + super() + prefix = "SN Greeted" + print("howdy, see above") + + func greet_prefix_before_super(): + prefix = "SN Greeted" + super.greet() + print("howdy, see above") + + func say(name): + super(name + " super'd") + print(prefix, " say nothing... or not? ", name) + + +func test(): + var say = Say.new() + say.greet() + say.say("foo") + print() + + var say_another_thing = SayAnotherThing.new() + say_another_thing.greet() + say_another_thing.say("bar") + print() + + var say_nothing = SayNothing.new() + say_nothing.greet() + print(say_nothing.prefix) + say_nothing.greet_prefix_before_super() + print(say_nothing.prefix) + # This currently triggers a compiler bug: "compiler bug, function name not found" + #say_nothing.say("baz") diff --git a/modules/gdscript/tests/scripts/parser/features/super.out b/modules/gdscript/tests/scripts/parser/features/super.out new file mode 100644 index 0000000000..e0d4f4f098 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/super.out @@ -0,0 +1,13 @@ +GDTEST_OK +hello +S Greeted say something foo + +hi +SAT Greeted say another thing bar + +hello +howdy, see above +SN Greeted +hello +howdy, see above +S Greeted diff --git a/modules/gdscript/tests/scripts/parser/features/truthiness.gd b/modules/gdscript/tests/scripts/parser/features/truthiness.gd new file mode 100644 index 0000000000..9c67a152f5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/truthiness.gd @@ -0,0 +1,30 @@ +func test(): + # The assertions below should all evaluate to `true` for this test to pass. + assert(true) + assert(not false) + assert(500) + assert(not 0) + assert(500.5) + assert(not 0.0) + assert("non-empty string") + assert(["non-empty array"]) + assert({"non-empty": "dictionary"}) + assert(Vector2(1, 0)) + assert(Vector2i(-1, -1)) + assert(Vector3(0, 0, 0.0001)) + assert(Vector3i(0, 0, 10000)) + + # Zero position is `true` only if the Rect2's size is non-zero. + assert(Rect2(0, 0, 0, 1)) + + # Zero size is `true` only if the position is non-zero. + assert(Rect2(1, 1, 0, 0)) + + # Zero position is `true` only if the Rect2's size is non-zero. + assert(Rect2i(0, 0, 0, 1)) + + # Zero size is `true` only if the position is non-zero. + assert(Rect2i(1, 1, 0, 0)) + + # A fully black color is only truthy if its alpha component is not equal to `1`. + assert(Color(0, 0, 0, 0.5)) diff --git a/modules/gdscript/tests/scripts/parser/features/truthiness.out b/modules/gdscript/tests/scripts/parser/features/truthiness.out new file mode 100644 index 0000000000..705524857b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/truthiness.out @@ -0,0 +1,65 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 4 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 5 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 6 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 7 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 8 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 9 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 12 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 13 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 14 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 15 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 18 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 21 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 24 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 27 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 30 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. diff --git a/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd index 3b48f10ca7..65013c4301 100644 --- a/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd +++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd @@ -1,12 +1,20 @@ -var a # No init. -var b = 42 # Init. +var m1 # No init. +var m2 = 22 # Init. +var m3: String # No init, typed. +var m4: String = "44" # Init, typed. + func test(): - var c # No init, local. - var d = 23 # Init, local. + var loc5 # No init, local. + var loc6 = 66 # Init, local. + var loc7: String # No init, typed. + var loc8: String = "88" # Init, typed. + + m1 = 11 + m3 = "33" - a = 1 - c = 2 + loc5 = 55 + loc7 = "77" - prints(a, b, c, d) + prints(m1, m2, m3, m4, loc5, loc6, loc7, loc8) print("OK") diff --git a/modules/gdscript/tests/scripts/parser/features/variable_declaration.out b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out index 2e0a63c024..7817dd3169 100644 --- a/modules/gdscript/tests/scripts/parser/features/variable_declaration.out +++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out @@ -1,7 +1,3 @@ GDTEST_OK ->> WARNING ->> Line: 5 ->> UNASSIGNED_VARIABLE ->> The variable 'c' was used but never assigned a value. -1 42 2 23 +11 22 33 44 55 66 77 88 OK diff --git a/modules/gdscript/tests/scripts/parser/features/while.gd b/modules/gdscript/tests/scripts/parser/features/while.gd new file mode 100644 index 0000000000..17dd4fbad2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/while.gd @@ -0,0 +1,5 @@ +func test(): + var i = 0 + while i < 5: + print(i) + i += 1 diff --git a/modules/gdscript/tests/scripts/parser/features/while.out b/modules/gdscript/tests/scripts/parser/features/while.out new file mode 100644 index 0000000000..b4a50885c7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/while.out @@ -0,0 +1,6 @@ +GDTEST_OK +0 +1 +2 +3 +4 diff --git a/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd new file mode 100644 index 0000000000..8feaed899f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd @@ -0,0 +1,5 @@ +func test(): + # These statements always evaluate to `true`, and therefore emit a warning. + assert(true) + assert(-1.234) + assert(2 + 3 == 5) diff --git a/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out new file mode 100644 index 0000000000..5132792cb7 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out @@ -0,0 +1,13 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 4 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. +>> WARNING +>> Line: 5 +>> ASSERT_ALWAYS_TRUE +>> Assert statement is redundant because the expression is always true. diff --git a/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd new file mode 100644 index 0000000000..f72b10213f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd @@ -0,0 +1,8 @@ +func test(): + # `and` should be used instead. + if true && true: + pass + + # `or` should be used instead. + if false || true: + pass diff --git a/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd @@ -0,0 +1 @@ + diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out new file mode 100644 index 0000000000..20eec212ba --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out @@ -0,0 +1,4 @@ +>> WARNING +>> Line: 1 +>> EMPTY_FILE +>> Empty script file. diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd new file mode 100644 index 0000000000..15cd95ff2b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd @@ -0,0 +1 @@ +#a comment diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out new file mode 100644 index 0000000000..20eec212ba --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out @@ -0,0 +1,4 @@ +>> WARNING +>> Line: 1 +>> EMPTY_FILE +>> Empty script file. diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd new file mode 100644 index 0000000000..b28b04f643 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd @@ -0,0 +1,3 @@ + + + diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out new file mode 100644 index 0000000000..20eec212ba --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out @@ -0,0 +1,4 @@ +>> WARNING +>> Line: 1 +>> EMPTY_FILE +>> Empty script file. diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd new file mode 100644 index 0000000000..ecdba44d21 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd @@ -0,0 +1,4 @@ +#a comment, followed by a bunch of newlines + + + diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out new file mode 100644 index 0000000000..20eec212ba --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out @@ -0,0 +1,4 @@ +>> WARNING +>> Line: 1 +>> EMPTY_FILE +>> Empty script file. diff --git a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd new file mode 100644 index 0000000000..a93ecb66b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd @@ -0,0 +1,8 @@ +func test(): + # The ternary operator below returns values of different types and the + # result is assigned to a typed variable. This will cause a run-time error + # if the branch with the incompatible type is picked. Here, it won't happen + # since the `false` condition never evaluates to `true`. Instead, a warning + # will be emitted. + var __: int = 25 + __ = "hello" if false else -2 diff --git a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out new file mode 100644 index 0000000000..7d1558c6fc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 8 +>> INCOMPATIBLE_TERNARY +>> Values of the ternary conditional are not mutually compatible. diff --git a/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd b/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd new file mode 100644 index 0000000000..6117425528 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd @@ -0,0 +1,10 @@ +func test(): + # This should emit a warning. + var __ = 5 / 2 + + # These should not emit warnings. + __ = float(5) / 2 + __ = 5 / float(2) + __ = 5.0 / 2 + __ = 5 / 2.0 + __ = 5.0 / 2.0 diff --git a/modules/gdscript/tests/scripts/parser/warnings/integer_division.out b/modules/gdscript/tests/scripts/parser/warnings/integer_division.out new file mode 100644 index 0000000000..40eb63ffcb --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/integer_division.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> INTEGER_DIVISION +>> Integer division, decimal part will be discarded. diff --git a/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd new file mode 100644 index 0000000000..1eb54059dd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd @@ -0,0 +1,9 @@ +func test(): + var i = 25 + # The default branch (`_`) should be at the end of the `match` statement. + # Otherwise, a warning will be emitted + match i: + _: + print("default") + 25: + print("is 25") diff --git a/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out new file mode 100644 index 0000000000..8630fab420 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 8 +>> UNREACHABLE_PATTERN +>> Unreachable pattern (pattern after wildcard or bind). +default diff --git a/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd new file mode 100644 index 0000000000..954e697145 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd @@ -0,0 +1,5 @@ +func i_accept_ints_only(_i: int): + pass + +func test(): + i_accept_ints_only(12.345) diff --git a/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out new file mode 100644 index 0000000000..6fb592117b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 5 +>> NARROWING_CONVERSION +>> Narrowing conversion (float is converted to int and loses precision). diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd new file mode 100644 index 0000000000..00598e4d50 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd @@ -0,0 +1,6 @@ +func i_return_int() -> int: + return 4 + + +func test(): + i_return_int() diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd new file mode 100644 index 0000000000..d565d38365 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd @@ -0,0 +1,8 @@ +# See also `parser-errors/redefine-class-constant.gd`. +const TEST = 25 + + +func test(): + # Warning here. This is not an error because a new constant is created, + # rather than attempting to set the value of an existing constant. + const TEST = 50 diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out new file mode 100644 index 0000000000..9c9417e11d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out @@ -0,0 +1,9 @@ +GDTEST_OK +>> WARNING +>> Line: 8 +>> UNUSED_LOCAL_CONSTANT +>> The local constant 'TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '_TEST' +>> WARNING +>> Line: 8 +>> SHADOWED_VARIABLE +>> The local constant "TEST" is shadowing an already-declared constant at line 2. diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd new file mode 100644 index 0000000000..66dcf309e8 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd @@ -0,0 +1,8 @@ +var foo = 123 + + +func test(): + # Notice the `var` keyword. Without this keyword, no warning would be emitted + # because no new variable would be created. Instead, the class variable's value + # would be overwritten. + var foo = 456 diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out new file mode 100644 index 0000000000..82e467b368 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out @@ -0,0 +1,9 @@ +GDTEST_OK +>> WARNING +>> Line: 8 +>> UNUSED_VARIABLE +>> The local variable 'foo' is declared but never used in the block. If this is intended, prefix it with an underscore: '_foo' +>> WARNING +>> Line: 8 +>> SHADOWED_VARIABLE +>> The local variable "foo" is shadowing an already-declared variable at line 1. diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd new file mode 100644 index 0000000000..2c55d68be8 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd @@ -0,0 +1,2 @@ +func test(): + var test = "This variable has the same name as the test() function." diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out new file mode 100644 index 0000000000..26ce0465b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out @@ -0,0 +1,9 @@ +GDTEST_OK +>> WARNING +>> Line: 2 +>> UNUSED_VARIABLE +>> The local variable 'test' is declared but never used in the block. If this is intended, prefix it with an underscore: '_test' +>> WARNING +>> Line: 2 +>> SHADOWED_VARIABLE +>> The local variable "test" is shadowing an already-declared function at line 1. diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd new file mode 100644 index 0000000000..18ea260fa2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd @@ -0,0 +1,9 @@ +func test(): + # The following statements should all be reported as standalone expressions: + "This is a standalone expression" + 1234 + 0.0 + 0.0 + Color(1, 1, 1) + Vector3.ZERO + [true, false] + float(125) diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out new file mode 100644 index 0000000000..99ec87438e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out @@ -0,0 +1,21 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> STANDALONE_EXPRESSION +>> Standalone expression (the line has no effect). +>> WARNING +>> Line: 4 +>> STANDALONE_EXPRESSION +>> Standalone expression (the line has no effect). +>> WARNING +>> Line: 5 +>> STANDALONE_EXPRESSION +>> Standalone expression (the line has no effect). +>> WARNING +>> Line: 7 +>> STANDALONE_EXPRESSION +>> Standalone expression (the line has no effect). +>> WARNING +>> Line: 8 +>> STANDALONE_EXPRESSION +>> Standalone expression (the line has no effect). diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd new file mode 100644 index 0000000000..afb5059eea --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd @@ -0,0 +1,2 @@ +func test(): + var __ diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out new file mode 100644 index 0000000000..cf14502e9a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 2 +>> UNASSIGNED_VARIABLE +>> The variable '__' was used but never assigned a value. diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd new file mode 100644 index 0000000000..d77791f4c5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd @@ -0,0 +1,4 @@ +func test(): + var __: int + # Variable has no set value at this point (even though it's implicitly `0` here). + __ += 15 diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out new file mode 100644 index 0000000000..ba55a4e0f8 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 4 +>> UNASSIGNED_VARIABLE_OP_ASSIGN +>> Using assignment with operation but the variable '__' was not previously assigned a value. diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd new file mode 100644 index 0000000000..3311f342ab --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd @@ -0,0 +1,7 @@ +func test(): + var i = 25 + + return + + # This will never be run due to the `return` statement above. + print(i) diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out new file mode 100644 index 0000000000..9316abd5eb --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 7 +>> UNREACHABLE_CODE +>> Unreachable code (statement after return) in function 'test()'. diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd new file mode 100644 index 0000000000..e6e24dc6f2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd @@ -0,0 +1,12 @@ +# This should emit a warning since the unused argument is not prefixed with an underscore. +func function_with_unused_argument(p_arg1, p_arg2): + print(p_arg1) + + +# This shouldn't emit a warning since the unused argument is prefixed with an underscore. +func function_with_ignored_unused_argument(p_arg1, _p_arg2): + print(p_arg1) + + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out new file mode 100644 index 0000000000..92f3308f85 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 2 +>> UNUSED_PARAMETER +>> The parameter 'p_arg2' is never used in the function 'function_with_unused_argument'. If this is intended, prefix it with an underscore: '_p_arg2' diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd index 68e3bd424f..013a2e4beb 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd +++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd @@ -1,2 +1,4 @@ func test(): - var unused = "not used" + var unused = "not used" + + var _unused = "not used, but no warning since the variable name starts with an underscore" diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd new file mode 100644 index 0000000000..b4a42b3e3d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd @@ -0,0 +1,6 @@ +func i_return_void() -> void: + return + + +func test(): + var __ = i_return_void() diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out new file mode 100644 index 0000000000..84c9598f9a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 6 +>> VOID_ASSIGNMENT +>> Assignment operation, but the function 'i_return_void()' returns void. diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd new file mode 100644 index 0000000000..c6645c2c34 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd @@ -0,0 +1,138 @@ +func test(): + var value + + # null + value = null + print(value == null) + + # bool + value = false + print(value == null) + + # int + value = 0 + print(value == null) + + # float + value = 0.0 + print(value == null) + + # String + value = "" + print(value == null) + + # Vector2 + value = Vector2() + print(value == null) + + # Vector2i + value = Vector2i() + print(value == null) + + # Rect2 + value = Rect2() + print(value == null) + + # Rect2i + value = Rect2i() + print(value == null) + + # Vector3 + value = Vector3() + print(value == null) + + # Vector3i + value = Vector3i() + print(value == null) + + # Transform2D + value = Transform2D() + print(value == null) + + # Plane + value = Plane() + print(value == null) + + # Quaternion + value = Quaternion() + print(value == null) + + # AABB + value = AABB() + print(value == null) + + # Basis + value = Basis() + print(value == null) + + # Transform3D + value = Transform3D() + print(value == null) + + # Color + value = Color() + print(value == null) + + # StringName + value = &"" + print(value == null) + + # NodePath + value = ^"" + print(value == null) + + # RID + value = RID() + print(value == null) + + # Callable + value = Callable() + print(value == null) + + # Signal + value = Signal() + print(value == null) + + # Dictionary + value = {} + print(value == null) + + # Array + value = [] + print(value == null) + + # PackedByteArray + value = PackedByteArray() + print(value == null) + + # PackedInt32Array + value = PackedInt32Array() + print(value == null) + + # PackedInt64Array + value = PackedInt64Array() + print(value == null) + + # PackedFloat32Array + value = PackedFloat32Array() + print(value == null) + + # PackedFloat64Array + value = PackedFloat64Array() + print(value == null) + + # PackedStringArray + value = PackedStringArray() + print(value == null) + + # PackedVector2Array + value = PackedVector2Array() + print(value == null) + + # PackedVector3Array + value = PackedVector3Array() + print(value == null) + + # PackedColorArray + value = PackedColorArray() + print(value == null) diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out new file mode 100644 index 0000000000..639f6027b9 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out @@ -0,0 +1,35 @@ +GDTEST_OK +true +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd new file mode 100644 index 0000000000..ee622bf22f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd @@ -0,0 +1,138 @@ +func test(): + var value + + # null + value = null + print(value != null) + + # bool + value = false + print(value != null) + + # int + value = 0 + print(value != null) + + # float + value = 0.0 + print(value != null) + + # String + value = "" + print(value != null) + + # Vector2 + value = Vector2() + print(value != null) + + # Vector2i + value = Vector2i() + print(value != null) + + # Rect2 + value = Rect2() + print(value != null) + + # Rect2i + value = Rect2i() + print(value != null) + + # Vector3 + value = Vector3() + print(value != null) + + # Vector3i + value = Vector3i() + print(value != null) + + # Transform2D + value = Transform2D() + print(value != null) + + # Plane + value = Plane() + print(value != null) + + # Quaternion + value = Quaternion() + print(value != null) + + # AABB + value = AABB() + print(value != null) + + # Basis + value = Basis() + print(value != null) + + # Transform3D + value = Transform3D() + print(value != null) + + # Color + value = Color() + print(value != null) + + # StringName + value = &"" + print(value != null) + + # NodePath + value = ^"" + print(value != null) + + # RID + value = RID() + print(value != null) + + # Callable + value = Callable() + print(value != null) + + # Signal + value = Signal() + print(value != null) + + # Dictionary + value = {} + print(value != null) + + # Array + value = [] + print(value != null) + + # PackedByteArray + value = PackedByteArray() + print(value != null) + + # PackedInt32Array + value = PackedInt32Array() + print(value != null) + + # PackedInt64Array + value = PackedInt64Array() + print(value != null) + + # PackedFloat32Array + value = PackedFloat32Array() + print(value != null) + + # PackedFloat64Array + value = PackedFloat64Array() + print(value != null) + + # PackedStringArray + value = PackedStringArray() + print(value != null) + + # PackedVector2Array + value = PackedVector2Array() + print(value != null) + + # PackedVector3Array + value = PackedVector3Array() + print(value != null) + + # PackedColorArray + value = PackedColorArray() + print(value != null) diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out new file mode 100644 index 0000000000..d1e332afba --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out @@ -0,0 +1,35 @@ +GDTEST_OK +false +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd new file mode 100644 index 0000000000..7649062fda --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd @@ -0,0 +1,138 @@ +func test(): + var value + + # null + value = null + print(null == value) + + # bool + value = false + print(null == value) + + # int + value = 0 + print(null == value) + + # float + value = 0.0 + print(null == value) + + # String + value = "" + print(null == value) + + # Vector2 + value = Vector2() + print(null == value) + + # Vector2i + value = Vector2i() + print(null == value) + + # Rect2 + value = Rect2() + print(null == value) + + # Rect2i + value = Rect2i() + print(null == value) + + # Vector3 + value = Vector3() + print(null == value) + + # Vector3i + value = Vector3i() + print(null == value) + + # Transform2D + value = Transform2D() + print(null == value) + + # Plane + value = Plane() + print(null == value) + + # Quaternion + value = Quaternion() + print(null == value) + + # AABB + value = AABB() + print(null == value) + + # Basis + value = Basis() + print(null == value) + + # Transform3D + value = Transform3D() + print(null == value) + + # Color + value = Color() + print(null == value) + + # StringName + value = &"" + print(null == value) + + # NodePath + value = ^"" + print(null == value) + + # RID + value = RID() + print(null == value) + + # Callable + value = Callable() + print(null == value) + + # Signal + value = Signal() + print(null == value) + + # Dictionary + value = {} + print(null == value) + + # Array + value = [] + print(null == value) + + # PackedByteArray + value = PackedByteArray() + print(null == value) + + # PackedInt32Array + value = PackedInt32Array() + print(null == value) + + # PackedInt64Array + value = PackedInt64Array() + print(null == value) + + # PackedFloat32Array + value = PackedFloat32Array() + print(null == value) + + # PackedFloat64Array + value = PackedFloat64Array() + print(null == value) + + # PackedStringArray + value = PackedStringArray() + print(null == value) + + # PackedVector2Array + value = PackedVector2Array() + print(null == value) + + # PackedVector3Array + value = PackedVector3Array() + print(null == value) + + # PackedColorArray + value = PackedColorArray() + print(null == value) diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out new file mode 100644 index 0000000000..639f6027b9 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out @@ -0,0 +1,35 @@ +GDTEST_OK +true +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false +false diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd new file mode 100644 index 0000000000..8d5f9df1b8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd @@ -0,0 +1,138 @@ +func test(): + var value + + # null + value = null + print(null != value) + + # bool + value = false + print(null != value) + + # int + value = 0 + print(null != value) + + # float + value = 0.0 + print(null != value) + + # String + value = "" + print(null != value) + + # Vector2 + value = Vector2() + print(null != value) + + # Vector2i + value = Vector2i() + print(null != value) + + # Rect2 + value = Rect2() + print(null != value) + + # Rect2i + value = Rect2i() + print(null != value) + + # Vector3 + value = Vector3() + print(null != value) + + # Vector3i + value = Vector3i() + print(null != value) + + # Transform2D + value = Transform2D() + print(null != value) + + # Plane + value = Plane() + print(null != value) + + # Quaternion + value = Quaternion() + print(null != value) + + # AABB + value = AABB() + print(null != value) + + # Basis + value = Basis() + print(null != value) + + # Transform3D + value = Transform3D() + print(null != value) + + # Color + value = Color() + print(null != value) + + # StringName + value = &"" + print(null != value) + + # NodePath + value = ^"" + print(null != value) + + # RID + value = RID() + print(null != value) + + # Callable + value = Callable() + print(null != value) + + # Signal + value = Signal() + print(null != value) + + # Dictionary + value = {} + print(null != value) + + # Array + value = [] + print(null != value) + + # PackedByteArray + value = PackedByteArray() + print(null != value) + + # PackedInt32Array + value = PackedInt32Array() + print(null != value) + + # PackedInt64Array + value = PackedInt64Array() + print(null != value) + + # PackedFloat32Array + value = PackedFloat32Array() + print(null != value) + + # PackedFloat64Array + value = PackedFloat64Array() + print(null != value) + + # PackedStringArray + value = PackedStringArray() + print(null != value) + + # PackedVector2Array + value = PackedVector2Array() + print(null != value) + + # PackedVector3Array + value = PackedVector3Array() + print(null != value) + + # PackedColorArray + value = PackedColorArray() + print(null != value) diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out new file mode 100644 index 0000000000..d1e332afba --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out @@ -0,0 +1,35 @@ +GDTEST_OK +false +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gltf/config.py b/modules/gltf/config.py index a4ee871eff..52a97c93aa 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -22,7 +22,6 @@ def get_doc_classes(): "GLTFSpecGloss", "GLTFState", "GLTFTexture", - "PackedSceneGLTF", ] diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index 0b95f2c802..ec25d84756 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -9,13 +9,13 @@ <methods> </methods> <members> - <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0"> + <member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0"> </member> - <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true"> + <member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05"> </member> - <member name="zfar" type="float" setter="set_zfar" getter="get_zfar" default="4000.0"> + <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0"> </member> - <member name="znear" type="float" setter="set_znear" getter="get_znear" default="0.05"> + <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true"> </member> </members> <constants> diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 04c40dd752..f8e0007684 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -7,6 +7,28 @@ <tutorials> </tutorials> <methods> + <method name="import_scene"> + <return type="Node" /> + <argument index="0" name="path" type="String" /> + <argument index="1" name="flags" type="int" default="0" /> + <argument index="2" name="bake_fps" type="int" default="30" /> + <argument index="3" name="state" type="GLTFState" default="null" /> + <description> + Import a scene from glTF2 ".gltf" or ".glb" file. + </description> + </method> + <method name="save_scene"> + <return type="int" enum="Error" /> + <argument index="0" name="node" type="Node" /> + <argument index="1" name="path" type="String" /> + <argument index="2" name="src_path" type="String" /> + <argument index="3" name="flags" type="int" default="0" /> + <argument index="4" name="bake_fps" type="float" default="30" /> + <argument index="5" name="state" type="GLTFState" default="null" /> + <description> + Save a scene as a glTF2 ".glb" or ".gltf" file. + </description> + </method> </methods> <constants> </constants> diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index f51d287685..2eb5ee9070 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -15,12 +15,12 @@ </member> <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="0.0"> </member> + <member name="light_type" type="String" setter="set_light_type" getter="get_light_type" default=""""> + </member> <member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.0"> </member> <member name="range" type="float" setter="set_range" getter="get_range" default="0.0"> </member> - <member name="type" type="String" setter="set_type" getter="get_type" default=""""> - </member> </members> <constants> </constants> diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index bfbb12df4d..95d7283398 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -23,6 +23,8 @@ </member> <member name="parent" type="int" setter="set_parent" getter="get_parent" default="-1"> </member> + <member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)"> + </member> <member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)"> </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)"> @@ -31,8 +33,6 @@ </member> <member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1"> </member> - <member name="translation" type="Vector3" setter="set_translation" getter="get_translation" default="Vector3(0, 0, 0)"> - </member> <member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)"> </member> </members> diff --git a/modules/gltf/doc_classes/PackedSceneGLTF.xml b/modules/gltf/doc_classes/PackedSceneGLTF.xml deleted file mode 100644 index d0136c6402..0000000000 --- a/modules/gltf/doc_classes/PackedSceneGLTF.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="PackedSceneGLTF" inherits="PackedScene" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> - <methods> - <method name="export_gltf"> - <return type="int" enum="Error" /> - <argument index="0" name="node" type="Node" /> - <argument index="1" name="path" type="String" /> - <argument index="2" name="flags" type="int" default="0" /> - <argument index="3" name="bake_fps" type="float" default="1000.0" /> - <description> - </description> - </method> - <method name="import_gltf_scene"> - <return type="Node" /> - <argument index="0" name="path" type="String" /> - <argument index="1" name="flags" type="int" default="0" /> - <argument index="2" name="bake_fps" type="float" default="1000.0" /> - <argument index="3" name="state" type="GLTFState" default="null" /> - <description> - </description> - </method> - <method name="pack_gltf"> - <return type="void" /> - <argument index="0" name="path" type="String" /> - <argument index="1" name="flags" type="int" default="0" /> - <argument index="2" name="bake_fps" type="float" default="1000.0" /> - <argument index="3" name="state" type="GLTFState" default="null" /> - <description> - </description> - </method> - </methods> - <members> - <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" override="true" default="{"conn_count": 0,"conns": PackedInt32Array(),"editable_instances": [],"names": PackedStringArray(),"node_count": 0,"node_paths": [],"nodes": PackedInt32Array(),"variants": [],"version": 2}" /> - </members> - <constants> - </constants> -</class> diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp index ae080bcc9a..fd9f758f10 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp @@ -30,9 +30,11 @@ #include "editor_scene_exporter_gltf_plugin.h" #include "core/config/project_settings.h" +#include "core/error/error_list.h" #include "core/object/object.h" #include "core/templates/vector.h" #include "editor/editor_file_system.h" +#include "gltf_document.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/check_box.h" #include "scene/main/node.h" @@ -49,7 +51,6 @@ bool SceneExporterGLTFPlugin::has_main_screen() const { SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) { editor = p_node; - convert_gltf2.instantiate(); file_export_lib = memnew(EditorFileDialog); editor->get_gui_base()->add_child(file_export_lib); file_export_lib->connect("file_selected", callable_mp(this, &SceneExporterGLTFPlugin::_gltf2_dialog_action)); @@ -71,8 +72,12 @@ void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) { return; } List<String> deps; - convert_gltf2->save_scene(root, p_file, p_file, 0, 1000.0f, &deps); - EditorFileSystem::get_singleton()->scan_changes(); + Ref<GLTFDocument> doc; + doc.instantiate(); + Error err = doc->save_scene(root, p_file, p_file, 0, 30.0f, Ref<GLTFState>()); + if (err != OK) { + ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); + } } void SceneExporterGLTFPlugin::convert_scene_to_gltf2() { diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h index d952894c16..c4f277fca2 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.h +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h @@ -37,7 +37,6 @@ class SceneExporterGLTFPlugin : public EditorPlugin { GDCLASS(SceneExporterGLTFPlugin, EditorPlugin); - Ref<PackedSceneGLTF> convert_gltf2; EditorNode *editor = nullptr; EditorFileDialog *file_export_lib = nullptr; void _gltf2_dialog_action(String p_file); diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp index eca1c85bf3..12796c41d7 100644 --- a/modules/gltf/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor_scene_importer_gltf.cpp @@ -50,9 +50,9 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { - Ref<PackedSceneGLTF> importer; - importer.instantiate(); - return importer->import_scene(p_path, p_flags, p_bake_fps, r_missing_deps, r_err, Ref<GLTFState>()); + Ref<GLTFDocument> doc; + doc.instantiate(); + return doc->import_scene_gltf(p_path, p_flags, p_bake_fps, Ref<GLTFState>(), r_missing_deps, r_err); } Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, @@ -60,114 +60,3 @@ Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, int p_bake_fps) { return Ref<Animation>(); } - -void PackedSceneGLTF::_bind_methods() { - ClassDB::bind_method( - D_METHOD("export_gltf", "node", "path", "flags", "bake_fps"), - &PackedSceneGLTF::export_gltf, DEFVAL(0), DEFVAL(1000.0f)); - ClassDB::bind_method(D_METHOD("pack_gltf", "path", "flags", "bake_fps", "state"), - &PackedSceneGLTF::pack_gltf, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>())); - ClassDB::bind_method(D_METHOD("import_gltf_scene", "path", "flags", "bake_fps", "state"), - &PackedSceneGLTF::import_gltf_scene, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>())); -} -Node *PackedSceneGLTF::import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state) { - Error err = FAILED; - List<String> deps; - return import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state); -} - -Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, - List<String> *r_missing_deps, - Error *r_err, - Ref<GLTFState> r_state) { - if (r_state == Ref<GLTFState>()) { - r_state.instantiate(); - } - r_state->use_named_skin_binds = - p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; - - Ref<GLTFDocument> gltf_document; - gltf_document.instantiate(); - Error err = gltf_document->parse(r_state, p_path); - if (r_err) { - *r_err = err; - } - ERR_FAIL_COND_V(err != Error::OK, nullptr); - - Node3D *root = memnew(Node3D); - for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) { - gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]); - } - gltf_document->_process_mesh_instances(r_state, root); - if (r_state->animations.size()) { - AnimationPlayer *ap = memnew(AnimationPlayer); - root->add_child(ap); - ap->set_owner(root); - for (int i = 0; i < r_state->animations.size(); i++) { - gltf_document->_import_animation(r_state, ap, i, p_bake_fps); - } - } - - return cast_to<Node3D>(root); -} - -void PackedSceneGLTF::pack_gltf(String p_path, int32_t p_flags, - real_t p_bake_fps, Ref<GLTFState> r_state) { - Error err = FAILED; - List<String> deps; - Node *root = import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state); - ERR_FAIL_COND(err != OK); - pack(root); -} - -void PackedSceneGLTF::save_scene(Node *p_node, const String &p_path, - const String &p_src_path, uint32_t p_flags, - int p_bake_fps, List<String> *r_missing_deps, - Error *r_err) { - Error err = FAILED; - if (r_err) { - *r_err = err; - } - Ref<GLTFDocument> gltf_document; - gltf_document.instantiate(); - Ref<GLTFState> state; - state.instantiate(); - err = gltf_document->serialize(state, p_node, p_path); - if (r_err) { - *r_err = err; - } -} - -void PackedSceneGLTF::_build_parent_hierachy(Ref<GLTFState> state) { - // build the hierarchy - for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) { - for (int j = 0; j < state->nodes[node_i]->children.size(); j++) { - GLTFNodeIndex child_i = state->nodes[node_i]->children[j]; - ERR_FAIL_INDEX(child_i, state->nodes.size()); - if (state->nodes.write[child_i]->parent != -1) { - continue; - } - state->nodes.write[child_i]->parent = node_i; - } - } -} - -Error PackedSceneGLTF::export_gltf(Node *p_root, String p_path, - int32_t p_flags, - real_t p_bake_fps) { - ERR_FAIL_COND_V(!p_root, FAILED); - List<String> deps; - Error err; - String path = p_path; - int32_t flags = p_flags; - real_t baked_fps = p_bake_fps; - Ref<PackedSceneGLTF> exporter; - exporter.instantiate(); - exporter->save_scene(p_root, path, "", flags, baked_fps, &deps, &err); - int32_t error_code = err; - if (error_code != 0) { - return Error(error_code); - } - return OK; -} diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h index 7bc5f594ed..eb8775b137 100644 --- a/modules/gltf/editor_scene_importer_gltf.h +++ b/modules/gltf/editor_scene_importer_gltf.h @@ -46,35 +46,9 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { public: virtual uint32_t get_import_flags() const override; virtual void get_extensions(List<String> *r_extensions) const override; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, - List<String> *r_missing_deps = nullptr, - Error *r_err = nullptr) override; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override; }; #endif - -class PackedSceneGLTF : public PackedScene { - GDCLASS(PackedSceneGLTF, PackedScene); - -protected: - static void _bind_methods(); - -public: - virtual void save_scene(Node *p_node, const String &p_path, const String &p_src_path, - uint32_t p_flags, int p_bake_fps, - List<String> *r_missing_deps, Error *r_err = nullptr); - virtual void _build_parent_hierachy(Ref<GLTFState> state); - virtual Error export_gltf(Node *p_root, String p_path, int32_t p_flags = 0, - real_t p_bake_fps = 1000.0f); - virtual Node *import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, - List<String> *r_missing_deps, - Error *r_err, - Ref<GLTFState> r_state); - virtual Node *import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state = Ref<GLTFState>()); - virtual void pack_gltf(String p_path, int32_t p_flags = 0, - real_t p_bake_fps = 1000.0f, Ref<GLTFState> r_state = Ref<GLTFState>()); -}; #endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h index 949a601730..57aea1026c 100644 --- a/modules/gltf/gltf_accessor.h +++ b/modules/gltf/gltf_accessor.h @@ -44,8 +44,7 @@ private: int component_type = 0; bool normalized = false; int count = 0; - GLTFDocument::GLTFType - type = GLTFDocument::TYPE_SCALAR; + GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; Vector<double> min; Vector<double> max; int sparse_count = 0; diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/gltf_animation.h index 216d2161c4..be0ed2d4c6 100644 --- a/modules/gltf/gltf_animation.h +++ b/modules/gltf/gltf_animation.h @@ -55,7 +55,7 @@ public: }; struct Track { - Channel<Vector3> translation_track; + Channel<Vector3> position_track; Channel<Quaternion> rotation_track; Channel<Vector3> scale_track; Vector<Channel<float>> weight_tracks; diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/gltf_camera.cpp index efa7c5d6d7..0f895fb989 100644 --- a/modules/gltf/gltf_camera.cpp +++ b/modules/gltf/gltf_camera.cpp @@ -35,13 +35,13 @@ void GLTFCamera::_bind_methods() { ClassDB::bind_method(D_METHOD("set_perspective", "perspective"), &GLTFCamera::set_perspective); ClassDB::bind_method(D_METHOD("get_fov_size"), &GLTFCamera::get_fov_size); ClassDB::bind_method(D_METHOD("set_fov_size", "fov_size"), &GLTFCamera::set_fov_size); - ClassDB::bind_method(D_METHOD("get_zfar"), &GLTFCamera::get_zfar); - ClassDB::bind_method(D_METHOD("set_zfar", "zfar"), &GLTFCamera::set_zfar); - ClassDB::bind_method(D_METHOD("get_znear"), &GLTFCamera::get_znear); - ClassDB::bind_method(D_METHOD("set_znear", "znear"), &GLTFCamera::set_znear); + ClassDB::bind_method(D_METHOD("get_depth_far"), &GLTFCamera::get_depth_far); + ClassDB::bind_method(D_METHOD("set_depth_far", "zdepth_far"), &GLTFCamera::set_depth_far); + ClassDB::bind_method(D_METHOD("get_depth_near"), &GLTFCamera::get_depth_near); + ClassDB::bind_method(D_METHOD("set_depth_near", "zdepth_near"), &GLTFCamera::set_depth_near); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zfar"), "set_zfar", "get_zfar"); // float - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "znear"), "set_znear", "get_znear"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); // float } diff --git a/modules/gltf/gltf_camera.h b/modules/gltf/gltf_camera.h index bf94b80bef..843ff417a4 100644 --- a/modules/gltf/gltf_camera.h +++ b/modules/gltf/gltf_camera.h @@ -39,8 +39,8 @@ class GLTFCamera : public Resource { private: bool perspective = true; float fov_size = 75.0; - float zfar = 4000.0; - float znear = 0.05; + float depth_far = 4000.0; + float depth_near = 0.05; protected: static void _bind_methods(); @@ -50,9 +50,9 @@ public: void set_perspective(bool p_val) { perspective = p_val; } float get_fov_size() const { return fov_size; } void set_fov_size(float p_val) { fov_size = p_val; } - float get_zfar() const { return zfar; } - void set_zfar(float p_val) { zfar = p_val; } - float get_znear() const { return znear; } - void set_znear(float p_val) { znear = p_val; } + float get_depth_far() const { return depth_far; } + void set_depth_far(float p_val) { depth_far = p_val; } + float get_depth_near() const { return depth_near; } + void set_depth_near(float p_val) { depth_near = p_val; } }; #endif // GLTF_CAMERA_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ff0579a11c..d4f4221663 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -48,6 +48,7 @@ #include "core/io/file_access.h" #include "core/io/json.h" #include "core/math/disjoint_set.h" +#include "core/math/vector2.h" #include "core/variant/typed_array.h" #include "core/variant/variant.h" #include "core/version.h" @@ -61,6 +62,7 @@ #include "scene/resources/surface_tool.h" #include "modules/modules_enabled.gen.h" + #ifdef MODULE_CSG_ENABLED #include "modules/csg/csg_shape.h" #endif // MODULE_CSG_ENABLED @@ -70,6 +72,7 @@ #include <stdio.h> #include <stdlib.h> +#include <cstdint> #include <limits> Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) { @@ -426,8 +429,8 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { node["scale"] = _vec3_to_arr(n->scale); } - if (!n->translation.is_equal_approx(Vector3())) { - node["translation"] = _vec3_to_arr(n->translation); + if (!n->position.is_equal_approx(Vector3())) { + node["translation"] = _vec3_to_arr(n->position); } if (n->children.size()) { Array children; @@ -581,7 +584,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) { node->xform = _arr_to_xform(n["matrix"]); } else { if (n.has("translation")) { - node->translation = _arr_to_vec3(n["translation"]); + node->position = _arr_to_vec3(n["translation"]); } if (n.has("rotation")) { node->rotation = _arr_to_quaternion(n["rotation"]); @@ -591,7 +594,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) { } node->xform.basis.set_quaternion_scale(node->rotation, node->scale); - node->xform.origin = node->translation; + node->xform.origin = node->position; } if (n.has("extensions")) { @@ -2170,11 +2173,14 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { } Array array = import_mesh->get_surface_arrays(surface_i); + uint32_t format = import_mesh->get_surface_format(surface_i); + int32_t vertex_num = 0; Dictionary attributes; { Vector<Vector3> a = array[Mesh::ARRAY_VERTEX]; ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA); attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true); + vertex_num = a.size(); } { Vector<real_t> a = array[Mesh::ARRAY_TANGENT]; @@ -2217,6 +2223,58 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true); } } + for (int custom_i = 0; custom_i < 3; custom_i++) { + Vector<float> a = array[Mesh::ARRAY_CUSTOM0 + custom_i]; + if (a.size()) { + int num_channels = 4; + int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS; + switch ((format >> custom_shift) & Mesh::ARRAY_FORMAT_CUSTOM_MASK) { + case Mesh::ARRAY_CUSTOM_R_FLOAT: + num_channels = 1; + break; + case Mesh::ARRAY_CUSTOM_RG_FLOAT: + num_channels = 2; + break; + case Mesh::ARRAY_CUSTOM_RGB_FLOAT: + num_channels = 3; + break; + case Mesh::ARRAY_CUSTOM_RGBA_FLOAT: + num_channels = 4; + break; + } + int texcoord_i = 2 + 2 * custom_i; + String gltf_texcoord_key; + for (int prev_texcoord_i = 0; prev_texcoord_i < texcoord_i; prev_texcoord_i++) { + gltf_texcoord_key = vformat("TEXCOORD_%d", prev_texcoord_i); + if (!attributes.has(gltf_texcoord_key)) { + Vector<Vector2> empty; + empty.resize(vertex_num); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, empty, true); + } + } + + LocalVector<Vector2> first_channel; + first_channel.resize(vertex_num); + LocalVector<Vector2> second_channel; + second_channel.resize(vertex_num); + for (int32_t vert_i = 0; vert_i < vertex_num; vert_i++) { + float u = a[vert_i * num_channels + 0]; + float v = (num_channels == 1 ? 0.0f : a[vert_i * num_channels + 1]); + first_channel[vert_i] = Vector2(u, v); + u = 0; + v = 0; + if (num_channels >= 3) { + u = a[vert_i * num_channels + 2]; + v = (num_channels == 3 ? 0.0f : a[vert_i * num_channels + 3]); + second_channel[vert_i] = Vector2(u, v); + } + } + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, first_channel, true); + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, second_channel, true); + } + } { Vector<Color> a = array[Mesh::ARRAY_COLOR]; if (a.size()) { @@ -2252,13 +2310,12 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { } attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true); } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - int32_t vertex_count = vertex_array.size(); Vector<Color> joints_0; - joints_0.resize(vertex_count); + joints_0.resize(vertex_num); Vector<Color> joints_1; - joints_1.resize(vertex_count); + joints_1.resize(vertex_num); int32_t weights_8_count = JOINT_GROUP_SIZE * 2; - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { Color joint_0; joint_0.r = a[vertex_i * weights_8_count + 0]; joint_0.g = a[vertex_i * weights_8_count + 1]; @@ -2288,13 +2345,12 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { } attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true); } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - int32_t vertex_count = vertex_array.size(); Vector<Color> weights_0; - weights_0.resize(vertex_count); + weights_0.resize(vertex_num); Vector<Color> weights_1; - weights_1.resize(vertex_count); + weights_1.resize(vertex_num); int32_t weights_8_count = JOINT_GROUP_SIZE * 2; - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { Color weight_0; weight_0.r = a[vertex_i * weights_8_count + 0]; weight_0.g = a[vertex_i * weights_8_count + 1]; @@ -2458,7 +2514,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR); Array primitives = d["primitives"]; - const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); + const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : + Dictionary(); Ref<EditorSceneImporterMesh> import_mesh; import_mesh.instantiate(); String mesh_name = "mesh"; @@ -2468,6 +2525,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { import_mesh->set_name(_gen_unique_name(state, vformat("%s_%s", state->scene_name, mesh_name))); for (int j = 0; j < primitives.size(); j++) { + uint32_t flags = 0; Dictionary p = primitives[j]; Array array; @@ -2499,8 +2557,11 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { } ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR); + int32_t vertex_num = 0; if (a.has("POSITION")) { - array[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, a["POSITION"], true); + PackedVector3Array vertices = _decode_accessor_as_vec3(state, a["POSITION"], true); + array[Mesh::ARRAY_VERTEX] = vertices; + vertex_num = vertices.size(); } if (a.has("NORMAL")) { array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true); @@ -2514,6 +2575,60 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { if (a.has("TEXCOORD_1")) { array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true); } + for (int custom_i = 0; custom_i < 3; custom_i++) { + Vector<float> cur_custom; + Vector<Vector2> texcoord_first; + Vector<Vector2> texcoord_second; + + int texcoord_i = 2 + 2 * custom_i; + String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); + int num_channels = 0; + if (a.has(gltf_texcoord_key)) { + texcoord_first = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true); + num_channels = 2; + } + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); + if (a.has(gltf_texcoord_key)) { + texcoord_second = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true); + num_channels = 4; + } + if (!num_channels) { + break; + } + if (num_channels == 2 || num_channels == 4) { + cur_custom.resize(vertex_num * num_channels); + for (int32_t uv_i = 0; uv_i < texcoord_first.size() && uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 0] = texcoord_first[uv_i].x; + cur_custom.write[uv_i * num_channels + 1] = texcoord_first[uv_i].y; + } + // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: + for (int32_t uv_i = texcoord_first.size(); uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 0] = 0; + cur_custom.write[uv_i * num_channels + 1] = 0; + } + } + if (num_channels == 4) { + for (int32_t uv_i = 0; uv_i < texcoord_second.size() && uv_i < vertex_num; uv_i++) { + // num_channels must be 4 + cur_custom.write[uv_i * num_channels + 2] = texcoord_second[uv_i].x; + cur_custom.write[uv_i * num_channels + 3] = texcoord_second[uv_i].y; + } + // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: + for (int32_t uv_i = texcoord_second.size(); uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 2] = 0; + cur_custom.write[uv_i * num_channels + 3] = 0; + } + } + if (cur_custom.size() > 0) { + array[Mesh::ARRAY_CUSTOM0 + custom_i] = cur_custom; + int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS; + if (num_channels == 2) { + flags |= Mesh::ARRAY_CUSTOM_RG_FLOAT << custom_shift; + } else { + flags |= Mesh::ARRAY_CUSTOM_RGBA_FLOAT << custom_shift; + } + } + } if (a.has("COLOR_0")) { array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(state, a["COLOR_0"], true); has_vertex_color = true; @@ -2525,10 +2640,9 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true); ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA); int32_t weight_8_count = JOINT_GROUP_SIZE * 2; - int32_t vertex_count = joints_0.size() / JOINT_GROUP_SIZE; Vector<int> joints; - joints.resize(vertex_count * weight_8_count); - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + joints.resize(vertex_num * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { joints.write[vertex_i * weight_8_count + 0] = joints_0[vertex_i * JOINT_GROUP_SIZE + 0]; joints.write[vertex_i * weight_8_count + 1] = joints_0[vertex_i * JOINT_GROUP_SIZE + 1]; joints.write[vertex_i * weight_8_count + 2] = joints_0[vertex_i * JOINT_GROUP_SIZE + 2]; @@ -2567,9 +2681,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { Vector<float> weights; ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA); int32_t weight_8_count = JOINT_GROUP_SIZE * 2; - int32_t vertex_count = weights_0.size() / JOINT_GROUP_SIZE; - weights.resize(vertex_count * weight_8_count); - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + weights.resize(vertex_num * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { weights.write[vertex_i * weight_8_count + 0] = weights_0[vertex_i * JOINT_GROUP_SIZE + 0]; weights.write[vertex_i * weight_8_count + 1] = weights_0[vertex_i * JOINT_GROUP_SIZE + 1]; weights.write[vertex_i * weight_8_count + 2] = weights_0[vertex_i * JOINT_GROUP_SIZE + 2]; @@ -2797,7 +2910,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { mat = mat3d; } - import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat.is_valid() ? mat->get_name() : String()); + import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat.is_valid() ? mat->get_name() : String(), flags); } Vector<float> blend_weights; @@ -2953,6 +3066,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat } } } else { // Relative path to an external image file. + uri = uri.uri_decode(); uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows. // ResourceLoader will rely on the file extension to use the relevant loader. // The spec says that if mimeType is defined, it should take precedence (e.g. @@ -4356,8 +4470,8 @@ Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) { color[1] = light->color.g; color[2] = light->color.b; d["color"] = color; - d["type"] = light->type; - if (light->type == "spot") { + d["type"] = light->light_type; + if (light->light_type == "spot") { Dictionary s; float inner_cone_angle = light->inner_cone_angle; s["innerConeAngle"] = inner_cone_angle; @@ -4403,16 +4517,16 @@ Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) { Dictionary og; og["ymag"] = Math::deg2rad(camera->get_fov_size()); og["xmag"] = Math::deg2rad(camera->get_fov_size()); - og["zfar"] = camera->get_zfar(); - og["znear"] = camera->get_znear(); + og["zfar"] = camera->get_depth_far(); + og["znear"] = camera->get_depth_near(); d["orthographic"] = og; d["type"] = "orthographic"; } else if (camera->get_perspective()) { Dictionary ppt; // GLTF spec is in radians, Godot's camera is in degrees. ppt["yfov"] = Math::deg2rad(camera->get_fov_size()); - ppt["zfar"] = camera->get_zfar(); - ppt["znear"] = camera->get_znear(); + ppt["zfar"] = camera->get_depth_far(); + ppt["znear"] = camera->get_depth_near(); d["perspective"] = ppt; d["type"] = "perspective"; } @@ -4452,7 +4566,7 @@ Error GLTFDocument::_parse_lights(Ref<GLTFState> state) { light.instantiate(); ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); const String &type = d["type"]; - light->type = type; + light->light_type = type; if (d.has("color")) { const Array &arr = d["color"]; @@ -4503,8 +4617,8 @@ Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) { const Dictionary &og = d["orthographic"]; // GLTF spec is in radians, Godot's camera is in degrees. camera->set_fov_size(Math::rad2deg(real_t(og["ymag"]))); - camera->set_zfar(og["zfar"]); - camera->set_znear(og["znear"]); + camera->set_depth_far(og["zfar"]); + camera->set_depth_near(og["znear"]); } else { camera->set_fov_size(10); } @@ -4514,8 +4628,8 @@ Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) { const Dictionary &ppt = d["perspective"]; // GLTF spec is in radians, Godot's camera is in degrees. camera->set_fov_size(Math::rad2deg(real_t(ppt["yfov"]))); - camera->set_zfar(ppt["zfar"]); - camera->set_znear(ppt["znear"]); + camera->set_depth_far(ppt["zfar"]); + camera->set_depth_near(ppt["znear"]); } else { camera->set_fov_size(10); } @@ -4576,15 +4690,15 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) { for (Map<int, GLTFAnimation::Track>::Element *track_i = gltf_animation->get_tracks().front(); track_i; track_i = track_i->next()) { GLTFAnimation::Track track = track_i->get(); - if (track.translation_track.times.size()) { + if (track.position_track.times.size()) { Dictionary t; t["sampler"] = samplers.size(); Dictionary s; - s["interpolation"] = interpolation_to_string(track.translation_track.interpolation); - Vector<real_t> times = Variant(track.translation_track.times); + s["interpolation"] = interpolation_to_string(track.position_track.interpolation); + Vector<real_t> times = Variant(track.position_track.times); s["input"] = _encode_accessor_as_floats(state, times, false); - Vector<Vector3> values = Variant(track.translation_track.values); + Vector<Vector3> values = Variant(track.position_track.values); s["output"] = _encode_accessor_as_vec3(state, values, false); samplers.push_back(s); @@ -4769,10 +4883,10 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) { const Vector<float> times = _decode_accessor_as_floats(state, input, false); if (path == "translation") { - const Vector<Vector3> translations = _decode_accessor_as_vec3(state, output, false); - track->translation_track.interpolation = interp; - track->translation_track.times = Variant(times); //convert via variant - track->translation_track.values = Variant(translations); //convert via variant + const Vector<Vector3> positions = _decode_accessor_as_vec3(state, output, false); + track->position_track.interpolation = interp; + track->position_track.times = Variant(times); //convert via variant + track->position_track.values = Variant(positions); //convert via variant } else if (path == "rotation") { const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(state, output, false); track->rotation_track.interpolation = interp; @@ -4896,7 +5010,7 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshIns if (p_mesh_instance->get_material_override().is_valid()) { mat = p_mesh_instance->get_material_override(); } - import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name); + import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name, godot_mesh->surface_get_format(surface_i)); } for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { blend_weights.write[blend_i] = 0.0f; @@ -4950,7 +5064,7 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, intensity /= 100; } - if (l->type == "directional") { + if (l->light_type == "directional") { DirectionalLight3D *light = memnew(DirectionalLight3D); light->set_param(Light3D::PARAM_ENERGY, intensity); light->set_color(l->color); @@ -4961,14 +5075,14 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, // Doubling the range will double the effective brightness, so we need double attenuation (half brightness). // We want to have double intensity give double brightness, so we need half the attenuation. const float attenuation = range / intensity; - if (l->type == "point") { + if (l->light_type == "point") { OmniLight3D *light = memnew(OmniLight3D); light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation); light->set_param(OmniLight3D::PARAM_RANGE, range); light->set_color(l->color); return light; } - if (l->type == "spot") { + if (l->light_type == "spot") { SpotLight3D *light = memnew(SpotLight3D); light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation); light->set_param(SpotLight3D::PARAM_RANGE, range); @@ -4995,9 +5109,9 @@ Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_paren Ref<GLTFCamera> c = state->cameras[gltf_node->camera]; if (c->get_perspective()) { - camera->set_perspective(c->get_fov_size(), c->get_znear(), c->get_zfar()); + camera->set_perspective(c->get_fov_size(), c->get_depth_near(), c->get_depth_far()); } else { - camera->set_orthogonal(c->get_fov_size(), c->get_znear(), c->get_zfar()); + camera->set_orthogonal(c->get_fov_size(), c->get_depth_near(), c->get_depth_far()); } return camera; @@ -5011,14 +5125,10 @@ GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_ if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) { c->set_perspective(true); - c->set_fov_size(p_camera->get_fov()); - c->set_zfar(p_camera->get_far()); - c->set_znear(p_camera->get_near()); - } else { - c->set_fov_size(p_camera->get_fov()); - c->set_zfar(p_camera->get_far()); - c->set_znear(p_camera->get_near()); } + c->set_fov_size(p_camera->get_fov()); + c->set_depth_far(p_camera->get_far()); + c->set_depth_near(p_camera->get_near()); GLTFCameraIndex camera_index = state->cameras.size(); state->cameras.push_back(c); return camera_index; @@ -5031,18 +5141,18 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig l.instantiate(); l->color = p_light->get_color(); if (cast_to<DirectionalLight3D>(p_light)) { - l->type = "directional"; + l->light_type = "directional"; DirectionalLight3D *light = cast_to<DirectionalLight3D>(p_light); l->intensity = light->get_param(DirectionalLight3D::PARAM_ENERGY); l->range = FLT_MAX; // Range for directional lights is infinite in Godot. } else if (cast_to<OmniLight3D>(p_light)) { - l->type = "point"; + l->light_type = "point"; OmniLight3D *light = cast_to<OmniLight3D>(p_light); l->range = light->get_param(OmniLight3D::PARAM_RANGE); float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION); l->intensity = l->range / attenuation; } else if (cast_to<SpotLight3D>(p_light)) { - l->type = "spot"; + l->light_type = "spot"; SpotLight3D *light = cast_to<SpotLight3D>(p_light); l->range = light->get_param(SpotLight3D::PARAM_RANGE); float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION); @@ -5075,7 +5185,7 @@ void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref Transform3D xform = p_spatial->get_transform(); p_node->scale = xform.basis.get_scale(); p_node->rotation = xform.basis.get_rotation_quaternion(); - p_node->translation = xform.origin; + p_node->position = xform.origin; } Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { @@ -5658,8 +5768,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, for (int i = 0; i < track.rotation_track.times.size(); i++) { length = MAX(length, track.rotation_track.times[i]); } - for (int i = 0; i < track.translation_track.times.size(); i++) { - length = MAX(length, track.translation_track.times[i]); + for (int i = 0; i < track.position_track.times.size(); i++) { + length = MAX(length, track.position_track.times[i]); } for (int i = 0; i < track.scale_track.times.size(); i++) { length = MAX(length, track.scale_track.times[i]); @@ -5673,7 +5783,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, // Animated TRS properties will not affect a skinned mesh. const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0; - if ((track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { + if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { //make transform track int track_idx = animation->get_track_count(); animation->add_track(Animation::TYPE_TRANSFORM3D); @@ -5691,8 +5801,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, base_rot = state->nodes[track_i->key()]->rotation.normalized(); } - if (!track.translation_track.values.size()) { - base_pos = state->nodes[track_i->key()]->translation; + if (!track.position_track.values.size()) { + base_pos = state->nodes[track_i->key()]->position; } if (!track.scale_track.values.size()) { @@ -5705,8 +5815,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, Quaternion rot = base_rot; Vector3 scale = base_scale; - if (track.translation_track.times.size()) { - pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation); + if (track.position_track.times.size()) { + pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation); } if (track.rotation_track.times.size()) { @@ -5814,7 +5924,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { Transform3D mi_xform = mi->get_transform(); node->scale = mi_xform.basis.get_scale(); node->rotation = mi_xform.basis.get_rotation_quaternion(); - node->translation = mi_xform.origin; + node->position = mi_xform.origin; Dictionary json_skin; Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(mi->get_node(mi->get_skeleton_path())); @@ -5878,7 +5988,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { Transform3D bone_rest_xform = skeleton->get_bone_rest(bone_index); joint_node->scale = bone_rest_xform.basis.get_scale(); joint_node->rotation = bone_rest_xform.basis.get_rotation_quaternion(); - joint_node->translation = bone_rest_xform.origin; + joint_node->position = bone_rest_xform.origin; joint_node->joint = true; int32_t joint_node_i = state->nodes.size(); @@ -6024,8 +6134,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state } const float BAKE_FPS = 30.0f; if (track_type == Animation::TYPE_TRANSFORM3D) { - p_track.translation_track.times = times; - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.times = times; + p_track.position_track.interpolation = gltf_interpolation; p_track.rotation_track.times = times; p_track.rotation_track.interpolation = gltf_interpolation; p_track.scale_track.times = times; @@ -6033,27 +6143,27 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state p_track.scale_track.values.resize(key_count); p_track.scale_track.interpolation = gltf_interpolation; - p_track.translation_track.values.resize(key_count); - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.values.resize(key_count); + p_track.position_track.interpolation = gltf_interpolation; p_track.rotation_track.values.resize(key_count); p_track.rotation_track.interpolation = gltf_interpolation; for (int32_t key_i = 0; key_i < key_count; key_i++) { - Vector3 translation; + Vector3 position; Quaternion rotation; Vector3 scale; - Error err = p_animation->transform_track_get_key(p_track_i, key_i, &translation, &rotation, &scale); + Error err = p_animation->transform_track_get_key(p_track_i, key_i, &position, &rotation, &scale); ERR_CONTINUE(err != OK); Transform3D xform; xform.basis.set_quaternion_scale(rotation, scale); - xform.origin = translation; + xform.origin = position; xform = p_bone_rest * xform; - p_track.translation_track.values.write[key_i] = xform.get_origin(); + p_track.position_track.values.write[key_i] = xform.get_origin(); p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion(); p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); } } else if (path.find(":transform") != -1) { - p_track.translation_track.times = times; - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.times = times; + p_track.position_track.interpolation = gltf_interpolation; p_track.rotation_track.times = times; p_track.rotation_track.interpolation = gltf_interpolation; p_track.scale_track.times = times; @@ -6061,13 +6171,13 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state p_track.scale_track.values.resize(key_count); p_track.scale_track.interpolation = gltf_interpolation; - p_track.translation_track.values.resize(key_count); - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.values.resize(key_count); + p_track.position_track.interpolation = gltf_interpolation; p_track.rotation_track.values.resize(key_count); p_track.rotation_track.interpolation = gltf_interpolation; for (int32_t key_i = 0; key_i < key_count; key_i++) { Transform3D xform = p_animation->track_get_key_value(p_track_i, key_i); - p_track.translation_track.values.write[key_i] = xform.get_origin(); + p_track.position_track.values.write[key_i] = xform.get_origin(); p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion(); p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); } @@ -6083,16 +6193,16 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state Quaternion rotation_track = p_animation->track_get_key_value(p_track_i, key_i); p_track.rotation_track.values.write[key_i] = rotation_track; } - } else if (path.find(":translation") != -1) { - p_track.translation_track.times = times; - p_track.translation_track.interpolation = gltf_interpolation; + } else if (path.find(":position") != -1) { + p_track.position_track.times = times; + p_track.position_track.interpolation = gltf_interpolation; - p_track.translation_track.values.resize(key_count); - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.values.resize(key_count); + p_track.position_track.interpolation = gltf_interpolation; for (int32_t key_i = 0; key_i < key_count; key_i++) { - Vector3 translation = p_animation->track_get_key_value(p_track_i, key_i); - p_track.translation_track.values.write[key_i] = translation; + Vector3 position = p_animation->track_get_key_value(p_track_i, key_i); + p_track.position_track.values.write[key_i] = position; } } else if (path.find(":rotation") != -1) { p_track.rotation_track.times = times; @@ -6151,34 +6261,34 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state } p_track.scale_track.values.write[key_i] = bezier_track; } - } else if (path.find("/translation") != -1) { + } else if (path.find("/position") != -1) { const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS; - if (!p_track.translation_track.times.size()) { + if (!p_track.position_track.times.size()) { Vector<float> new_times; new_times.resize(keys); for (int32_t key_i = 0; key_i < keys; key_i++) { new_times.write[key_i] = key_i / BAKE_FPS; } - p_track.translation_track.times = new_times; - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.times = new_times; + p_track.position_track.interpolation = gltf_interpolation; - p_track.translation_track.values.resize(keys); - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.values.resize(keys); + p_track.position_track.interpolation = gltf_interpolation; } for (int32_t key_i = 0; key_i < keys; key_i++) { - Vector3 bezier_track = p_track.translation_track.values[key_i]; - if (path.find("/translation:x") != -1) { + Vector3 bezier_track = p_track.position_track.values[key_i]; + if (path.find("/position:x") != -1) { bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); bezier_track.x = p_bone_rest.affine_inverse().origin.x * bezier_track.x; - } else if (path.find("/translation:y") != -1) { + } else if (path.find("/position:y") != -1) { bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); bezier_track.y = p_bone_rest.affine_inverse().origin.y * bezier_track.y; - } else if (path.find("/translation:z") != -1) { + } else if (path.find("/position:z") != -1) { bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); bezier_track.z = p_bone_rest.affine_inverse().origin.z * bezier_track.z; } - p_track.translation_track.values.write[key_i] = bezier_track; + p_track.position_track.values.write[key_i] = bezier_track; } } } @@ -6197,17 +6307,17 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, continue; } String orig_track_path = animation->track_get_path(track_i); - if (String(orig_track_path).find(":translation") != -1) { - const Vector<String> node_suffix = String(orig_track_path).split(":translation"); + if (String(orig_track_path).find(":position") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":position"); const NodePath path = node_suffix[0]; const Node *node = ap->get_parent()->get_node_or_null(path); - for (Map<GLTFNodeIndex, Node *>::Element *translation_scene_node_i = state->scene_nodes.front(); translation_scene_node_i; translation_scene_node_i = translation_scene_node_i->next()) { - if (translation_scene_node_i->get() == node) { - GLTFNodeIndex node_index = translation_scene_node_i->key(); - Map<int, GLTFAnimation::Track>::Element *translation_track_i = gltf_animation->get_tracks().find(node_index); + for (Map<GLTFNodeIndex, Node *>::Element *position_scene_node_i = state->scene_nodes.front(); position_scene_node_i; position_scene_node_i = position_scene_node_i->next()) { + if (position_scene_node_i->get() == node) { + GLTFNodeIndex node_index = position_scene_node_i->key(); + Map<int, GLTFAnimation::Track>::Element *position_track_i = gltf_animation->get_tracks().find(node_index); GLTFAnimation::Track track; - if (translation_track_i) { - track = translation_track_i->get(); + if (position_track_i) { + track = position_track_i->get(); } track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); @@ -6630,3 +6740,78 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) { } return err; } + +Error GLTFDocument::save_scene(Node *p_node, const String &p_path, + const String &p_src_path, uint32_t p_flags, + float p_bake_fps, Ref<GLTFState> r_state) { + Ref<GLTFDocument> gltf_document; + gltf_document.instantiate(); + if (r_state == Ref<GLTFState>()) { + r_state.instantiate(); + } + return gltf_document->serialize(r_state, p_node, p_path); +} + +Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err) { + // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire + if (r_state == Ref<GLTFState>()) { + r_state.instantiate(); + } + r_state->use_named_skin_binds = + p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; + + Ref<GLTFDocument> gltf_document; + gltf_document.instantiate(); + Error err = gltf_document->parse(r_state, p_path); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != Error::OK, nullptr); + + Node3D *root = memnew(Node3D); + for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) { + gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]); + } + gltf_document->_process_mesh_instances(r_state, root); + if (r_state->animations.size()) { + AnimationPlayer *ap = memnew(AnimationPlayer); + root->add_child(ap); + ap->set_owner(root); + for (int i = 0; i < r_state->animations.size(); i++) { + gltf_document->_import_animation(r_state, ap, i, p_bake_fps); + } + } + + return root; +} + +void GLTFDocument::_bind_methods() { + ClassDB::bind_method(D_METHOD("save_scene", "node", "path", "src_path", "flags", "bake_fps", "state"), + &GLTFDocument::save_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); + ClassDB::bind_method(D_METHOD("import_scene", "path", "flags", "bake_fps", "state"), + &GLTFDocument::import_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); +} + +void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) { + // build the hierarchy + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) { + for (int j = 0; j < state->nodes[node_i]->children.size(); j++) { + GLTFNodeIndex child_i = state->nodes[node_i]->children[j]; + ERR_FAIL_INDEX(child_i, state->nodes.size()); + if (state->nodes.write[child_i]->parent != -1) { + continue; + } + state->nodes.write[child_i]->parent = node_i; + } + } +} + +Node *GLTFDocument::import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state) { + Error err = FAILED; + List<String> deps; + Node *node = import_scene_gltf(p_path, p_flags, p_bake_fps, r_state, &deps, &err); + if (err != OK) { + return nullptr; + } + return node; +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 7a826897a9..fb798a055a 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -44,6 +44,7 @@ #include "scene/resources/texture.h" #include "modules/modules_enabled.gen.h" +#include <cstdint> class GLTFState; class GLTFSkin; @@ -102,6 +103,16 @@ public: COMPONENT_TYPE_FLOAT = 5126, }; +protected: + static void _bind_methods(); + +public: + Node *import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state); + Node *import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err = nullptr); + Error save_scene(Node *p_node, const String &p_path, + const String &p_src_path, uint32_t p_flags, + float p_bake_fps, Ref<GLTFState> r_state); + private: template <class T> static Array to_array(const Vector<T> &p_inp) { @@ -155,6 +166,7 @@ private: r_out[keys[i]] = p_inp[keys[i]]; } } + void _build_parent_hierachy(Ref<GLTFState> state); double _filter_number(double p_float); String _get_component_type_name(const uint32_t p_component); int _get_component_type_size(const int component_type); diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/gltf_light.cpp index 95cca9cf71..c5aa8d5724 100644 --- a/modules/gltf/gltf_light.cpp +++ b/modules/gltf/gltf_light.cpp @@ -35,8 +35,8 @@ void GLTFLight::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color); ClassDB::bind_method(D_METHOD("get_intensity"), &GLTFLight::get_intensity); ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &GLTFLight::set_intensity); - ClassDB::bind_method(D_METHOD("get_type"), &GLTFLight::get_type); - ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFLight::set_type); + ClassDB::bind_method(D_METHOD("get_light_type"), &GLTFLight::get_light_type); + ClassDB::bind_method(D_METHOD("set_light_type", "light_type"), &GLTFLight::set_light_type); ClassDB::bind_method(D_METHOD("get_range"), &GLTFLight::get_range); ClassDB::bind_method(D_METHOD("set_range", "range"), &GLTFLight::set_range); ClassDB::bind_method(D_METHOD("get_inner_cone_angle"), &GLTFLight::get_inner_cone_angle); @@ -46,7 +46,7 @@ void GLTFLight::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); // Color ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity"), "set_intensity", "get_intensity"); // float - ADD_PROPERTY(PropertyInfo(Variant::STRING, "type"), "set_type", "get_type"); // String + ADD_PROPERTY(PropertyInfo(Variant::STRING, "light_type"), "set_light_type", "get_light_type"); // String ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range"), "set_range", "get_range"); // float ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float @@ -68,12 +68,12 @@ void GLTFLight::set_intensity(float p_intensity) { intensity = p_intensity; } -String GLTFLight::get_type() { - return type; +String GLTFLight::get_light_type() { + return light_type; } -void GLTFLight::set_type(String p_type) { - type = p_type; +void GLTFLight::set_light_type(String p_light_type) { + light_type = p_light_type; } float GLTFLight::get_range() { diff --git a/modules/gltf/gltf_light.h b/modules/gltf/gltf_light.h index a859ca1833..079fb18151 100644 --- a/modules/gltf/gltf_light.h +++ b/modules/gltf/gltf_light.h @@ -44,7 +44,7 @@ protected: private: Color color; float intensity = 0.0f; - String type; + String light_type; float range = 0.0f; float inner_cone_angle = 0.0f; float outer_cone_angle = 0.0f; @@ -56,8 +56,8 @@ public: float get_intensity(); void set_intensity(float p_intensity); - String get_type(); - void set_type(String p_type); + String get_light_type(); + void set_light_type(String p_light_type); float get_range(); void set_range(float p_range); diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/gltf_node.cpp index 5db7ad66c3..9f925c7bbc 100644 --- a/modules/gltf/gltf_node.cpp +++ b/modules/gltf/gltf_node.cpp @@ -47,8 +47,8 @@ void GLTFNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &GLTFNode::set_skeleton); ClassDB::bind_method(D_METHOD("get_joint"), &GLTFNode::get_joint); ClassDB::bind_method(D_METHOD("set_joint", "joint"), &GLTFNode::set_joint); - ClassDB::bind_method(D_METHOD("get_translation"), &GLTFNode::get_translation); - ClassDB::bind_method(D_METHOD("set_translation", "translation"), &GLTFNode::set_translation); + ClassDB::bind_method(D_METHOD("get_position"), &GLTFNode::get_position); + ClassDB::bind_method(D_METHOD("set_position", "position"), &GLTFNode::set_position); ClassDB::bind_method(D_METHOD("get_rotation"), &GLTFNode::get_rotation); ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &GLTFNode::set_rotation); ClassDB::bind_method(D_METHOD("get_scale"), &GLTFNode::get_scale); @@ -66,7 +66,7 @@ void GLTFNode::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "skin"), "set_skin", "get_skin"); // GLTFSkinIndex ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // GLTFSkeletonIndex ADD_PROPERTY(PropertyInfo(Variant::BOOL, "joint"), "set_joint", "get_joint"); // bool - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation"), "set_translation", "get_translation"); // Vector3 + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "set_position", "get_position"); // Vector3 ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "rotation"), "set_rotation", "get_rotation"); // Quaternion ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3 ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int> @@ -137,12 +137,12 @@ void GLTFNode::set_joint(bool p_joint) { joint = p_joint; } -Vector3 GLTFNode::get_translation() { - return translation; +Vector3 GLTFNode::get_position() { + return position; } -void GLTFNode::set_translation(Vector3 p_translation) { - translation = p_translation; +void GLTFNode::set_position(Vector3 p_position) { + position = p_position; } Quaternion GLTFNode::get_rotation() { diff --git a/modules/gltf/gltf_node.h b/modules/gltf/gltf_node.h index 378b6da8bf..3b6e061449 100644 --- a/modules/gltf/gltf_node.h +++ b/modules/gltf/gltf_node.h @@ -37,7 +37,6 @@ class GLTFNode : public Resource { GDCLASS(GLTFNode, Resource); friend class GLTFDocument; - friend class PackedSceneGLTF; private: // matrices need to be transformed to this @@ -49,7 +48,7 @@ private: GLTFSkinIndex skin = -1; GLTFSkeletonIndex skeleton = -1; bool joint = false; - Vector3 translation; + Vector3 position; Quaternion rotation; Vector3 scale = Vector3(1, 1, 1); Vector<int> children; @@ -83,8 +82,8 @@ public: bool get_joint(); void set_joint(bool p_joint); - Vector3 get_translation(); - void set_translation(Vector3 p_translation); + Vector3 get_position(); + void set_position(Vector3 p_position); Quaternion get_rotation(); void set_rotation(Quaternion p_rotation); diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index d8209523c5..896ea5fc56 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -51,7 +51,6 @@ class GLTFState : public Resource { GDCLASS(GLTFState, Resource); friend class GLTFDocument; - friend class PackedSceneGLTF; String filename; Dictionary json; diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index 85921490d2..d6020f50f0 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -80,7 +80,6 @@ void register_gltf_types() { GDREGISTER_CLASS(GLTFLight); GDREGISTER_CLASS(GLTFState); GDREGISTER_CLASS(GLTFDocument); - GDREGISTER_CLASS(PackedSceneGLTF); #endif } diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 8e8b6f14ad..487e6deac0 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -475,7 +475,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { } Pair<Transform3D, IndexKey> p; - p.first = xform; + p.first = xform * mesh_library->get_item_mesh_transform(c.item); p.second = E->get(); multimesh_items[c.item].push_back(p); } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 2331a12d0f..c170bb107e 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -255,6 +255,12 @@ void GridMapEditor::_update_cursor_transform() { cursor_transform.basis *= node->get_cell_scale(); cursor_transform = node->get_global_transform() * cursor_transform; + if (selected_palette >= 0) { + if (node && !node->get_mesh_library().is_null()) { + cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette); + } + } + if (cursor_instance.is_valid()) { RenderingServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform); RenderingServer::get_singleton()->instance_set_visible(cursor_instance, cursor_visible); diff --git a/modules/lightmapper_rd/SCsub b/modules/lightmapper_rd/SCsub index 2f04f1833e..5cc9d8ee8b 100644 --- a/modules/lightmapper_rd/SCsub +++ b/modules/lightmapper_rd/SCsub @@ -7,6 +7,9 @@ env_lightmapper_rd = env_modules.Clone() env_lightmapper_rd.GLSL_HEADER("lm_raster.glsl") env_lightmapper_rd.GLSL_HEADER("lm_compute.glsl") env_lightmapper_rd.GLSL_HEADER("lm_blendseams.glsl") +env_lightmapper_rd.Depends("lm_raster.glsl.gen.h", "lm_common_inc.glsl") +env_lightmapper_rd.Depends("lm_compute.glsl.gen.h", "lm_common_inc.glsl") +env_lightmapper_rd.Depends("lm_blendseams.glsl.gen.h", "lm_common_inc.glsl") # Godot source files env_lightmapper_rd.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index fe941e25e7..ba4ef3be8d 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -274,13 +274,12 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ return BAKE_OK; } -void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) { +void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) { HashMap<Vertex, uint32_t, VertexHash> vertex_map; //fill triangles array and vertex array LocalVector<Triangle> triangles; LocalVector<Vertex> vertex_array; - LocalVector<Box> box_array; LocalVector<Seam> seams; slice_triangle_count.resize(atlas_slices); @@ -387,16 +386,13 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i } } - Box box; - box.min_bounds[0] = taabb.position.x; - box.min_bounds[1] = taabb.position.y; - box.min_bounds[2] = taabb.position.z; - box.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001); - box.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001); - box.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001); - box.pad0 = box.pad1 = 0; //make valgrind not complain - box_array.push_back(box); - + t.min_bounds[0] = taabb.position.x; + t.min_bounds[1] = taabb.position.y; + t.min_bounds[2] = taabb.position.z; + t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001); + t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001); + t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001); + t.pad0 = t.pad1 = 0; //make valgrind not complain triangles.push_back(t); slice_triangle_count.write[t.slice]++; } @@ -505,9 +501,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i Vector<uint8_t> tb = triangles.to_byte_array(); triangle_buffer = rd->storage_buffer_create(tb.size(), tb); - Vector<uint8_t> bb = box_array.to_byte_array(); - box_buffer = rd->storage_buffer_create(bb.size(), bb); - Vector<uint8_t> tib = triangle_indices.to_byte_array(); triangle_cell_indices_buffer = rd->storage_buffer_create(tib.size(), tib); @@ -755,7 +748,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d Vector<int> slice_triangle_count; RID vertex_buffer; RID triangle_buffer; - RID box_buffer; RID lights_buffer; RID triangle_cell_indices_buffer; RID grid_texture; @@ -767,14 +759,13 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d #define FREE_BUFFERS \ rd->free(vertex_buffer); \ rd->free(triangle_buffer); \ - rd->free(box_buffer); \ rd->free(lights_buffer); \ rd->free(triangle_cell_indices_buffer); \ rd->free(grid_texture); \ rd->free(seams_buffer); \ rd->free(probe_positions_buffer); - _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, box_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata); + _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata); if (p_step_function) { p_step_function(0.47, TTR("Preparing shaders"), p_bake_userdata, true); @@ -828,62 +819,55 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 3; - u.ids.push_back(box_buffer); - base_uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 4; u.ids.push_back(triangle_cell_indices_buffer); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 5; + u.binding = 4; u.ids.push_back(lights_buffer); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 6; + u.binding = 5; u.ids.push_back(seams_buffer); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 7; + u.binding = 6; u.ids.push_back(probe_positions_buffer); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 8; + u.binding = 7; u.ids.push_back(grid_texture); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 9; + u.binding = 8; u.ids.push_back(albedo_array_tex); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 10; + u.binding = 9; u.ids.push_back(emission_array_tex); base_uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 11; + u.binding = 10; u.ids.push_back(sampler); base_uniforms.push_back(u); } diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index 7ab7f34464..a6a3740051 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -157,16 +157,13 @@ class LightmapperRD : public Lightmapper { } }; - struct Box { + struct Triangle { + uint32_t indices[3] = {}; + uint32_t slice = 0; float min_bounds[3] = {}; float pad0 = 0.0; float max_bounds[3] = {}; float pad1 = 0.0; - }; - - struct Triangle { - uint32_t indices[3] = {}; - uint32_t slice = 0; bool operator<(const Triangle &p_triangle) const { return slice < p_triangle.slice; } @@ -231,7 +228,7 @@ class LightmapperRD : public Lightmapper { Vector<Color> probe_values; BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata); - void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata); + void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata); void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform); public: diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl index 1581639036..22172d50e4 100644 --- a/modules/lightmapper_rd/lm_common_inc.glsl +++ b/modules/lightmapper_rd/lm_common_inc.glsl @@ -16,26 +16,18 @@ vertices; struct Triangle { uvec3 indices; uint slice; -}; - -layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles { - Triangle data[]; -} -triangles; - -struct Box { vec3 min_bounds; uint pad0; vec3 max_bounds; uint pad1; }; -layout(set = 0, binding = 3, std430) restrict readonly buffer Boxes { - Box data[]; +layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles { + Triangle data[]; } -boxes; +triangles; -layout(set = 0, binding = 4, std430) restrict readonly buffer GridIndices { +layout(set = 0, binding = 3, std430) restrict readonly buffer GridIndices { uint data[]; } grid_indices; @@ -63,7 +55,7 @@ struct Light { uint pad[3]; }; -layout(set = 0, binding = 5, std430) restrict readonly buffer Lights { +layout(set = 0, binding = 4, std430) restrict readonly buffer Lights { Light data[]; } lights; @@ -73,19 +65,19 @@ struct Seam { uvec2 b; }; -layout(set = 0, binding = 6, std430) restrict readonly buffer Seams { +layout(set = 0, binding = 5, std430) restrict readonly buffer Seams { Seam data[]; } seams; -layout(set = 0, binding = 7, std430) restrict readonly buffer Probes { +layout(set = 0, binding = 6, std430) restrict readonly buffer Probes { vec4 data[]; } probe_positions; -layout(set = 0, binding = 8) uniform utexture3D grid; +layout(set = 0, binding = 7) uniform utexture3D grid; -layout(set = 0, binding = 9) uniform texture2DArray albedo_tex; -layout(set = 0, binding = 10) uniform texture2DArray emission_tex; +layout(set = 0, binding = 8) uniform texture2DArray albedo_tex; +layout(set = 0, binding = 9) uniform texture2DArray emission_tex; -layout(set = 0, binding = 11) uniform sampler linear_sampler; +layout(set = 0, binding = 10) uniform sampler linear_sampler; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 9ca40535f9..a71652d5c4 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -160,18 +160,19 @@ bool trace_ray(vec3 p_from, vec3 p_to uint tidx = grid_indices.data[cell_data.y + i]; //Ray-Box test - vec3 t0 = (boxes.data[tidx].min_bounds - p_from) * inv_dir; - vec3 t1 = (boxes.data[tidx].max_bounds - p_from) * inv_dir; + Triangle triangle = triangles.data[tidx]; + vec3 t0 = (triangle.min_bounds - p_from) * inv_dir; + vec3 t1 = (triangle.max_bounds - p_from) * inv_dir; vec3 tmin = min(t0, t1), tmax = max(t0, t1); - if (max(tmin.x, max(tmin.y, tmin.z)) <= min(tmax.x, min(tmax.y, tmax.z))) { + if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) { continue; //ray box failed } //prepare triangle vertices - vec3 vtx0 = vertices.data[triangles.data[tidx].indices.x].position; - vec3 vtx1 = vertices.data[triangles.data[tidx].indices.y].position; - vec3 vtx2 = vertices.data[triangles.data[tidx].indices.z].position; + vec3 vtx0 = vertices.data[triangle.indices.x].position; + vec3 vtx1 = vertices.data[triangle.indices.y].position; + vec3 vtx2 = vertices.data[triangle.indices.z].position; #if defined(MODE_UNOCCLUDE) vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2))); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 3437dbd194..6cc7ddb424 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -43,8 +43,10 @@ #include "core/os/thread.h" #ifdef TOOLS_ENABLED +#include "core/os/keyboard.h" #include "editor/bindings_generator.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/node_dock.h" #endif @@ -1353,6 +1355,7 @@ void CSharpLanguage::_editor_init_callback() { // Enable it as a plugin EditorNode::add_editor_plugin(godotsharp_editor); + ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KEY_MASK_ALT | KEY_B); godotsharp_editor->enable_plugin(); get_singleton()->godotsharp_editor = godotsharp_editor; @@ -1861,6 +1864,28 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool * return Variant::NIL; } +void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const { + if (!script->is_valid() || !script->script_class) + return; + + GD_MONO_SCOPE_THREAD_ATTACH; + + // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls. + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + const Vector<GDMonoMethod *> &methods = top->get_all_methods(); + for (int i = 0; i < methods.size(); ++i) { + MethodInfo minfo = methods[i]->get_method_info(); + if (minfo.name != CACHED_STRING_NAME(dotctor)) { + p_list->push_back(minfo); + } + } + + top = top->get_parent_class(); + } +} + bool CSharpInstance::has_method(const StringName &p_method) const { if (!script.is_valid()) { return false; @@ -2866,12 +2891,24 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage ERR_FAIL_COND_V_MSG(elem_variant_type == Variant::NIL, -1, "Unknown array element type."); - int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string); + bool preset_hint = false; + if (elem_variant_type == Variant::STRING) { + MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); + if (PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)) == PROPERTY_HINT_ENUM) { + r_hint_string = itos(elem_variant_type) + "/" + itos(PROPERTY_HINT_ENUM) + ":" + CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + preset_hint = true; + } + } + + if (!preset_hint) { + int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string); - ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type."); + ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type."); + + // Format: type/hint:hint_string + r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string; + } - // Format: type/hint:hint_string - r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string; r_hint = PROPERTY_HINT_TYPE_STRING; } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) { @@ -3280,10 +3317,19 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { GD_MONO_SCOPE_THREAD_ATTACH; - // TODO: Filter out things unsuitable for explicit calls, like constructors. - const Vector<GDMonoMethod *> &methods = script_class->get_all_methods(); - for (int i = 0; i < methods.size(); ++i) { - p_list->push_back(methods[i]->get_method_info()); + // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls. + GDMonoClass *top = script_class; + + while (top && top != native) { + const Vector<GDMonoMethod *> &methods = top->get_all_methods(); + for (int i = 0; i < methods.size(); ++i) { + MethodInfo minfo = methods[i]->get_method_info(); + if (minfo.name != CACHED_STRING_NAME(dotctor)) { + p_list->push_back(methods[i]->get_method_info()); + } + } + + top = top->get_parent_class(); } } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index e3bbb20dec..afc17f694a 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -293,7 +293,7 @@ public: void get_property_list(List<PropertyInfo> *p_properties) const override; Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override; - /* TODO */ void get_method_list(List<MethodInfo> *p_list) const override {} + void get_method_list(List<MethodInfo> *p_list) const override; bool has_method(const StringName &p_method) const override; Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 73cabf8561..98c6881166 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -418,11 +418,15 @@ namespace GodotTools AddToolSubmenuItem("C#", _menuPopup); + var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution"); + _toolBarBuildButton = new Button { Text = "Build", - HintTooltip = "Build solution", - FocusMode = Control.FocusModeEnum.None + HintTooltip = "Build Solution".TTR(), + FocusMode = Control.FocusModeEnum.None, + Shortcut = buildSolutionShortcut, + ShortcutInTooltip = true }; _toolBarBuildButton.PressedSignal += BuildSolutionPressed; AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton); diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index 793f84fd77..5c5ced8c29 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -13,6 +13,9 @@ namespace GodotTools.Internals public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) => internal_EditorDef(setting, defaultValue, restartIfChanged); + public static object EditorShortcut(string setting) => + internal_EditorShortcut(setting); + [SuppressMessage("ReSharper", "InconsistentNaming")] public static string TTR(this string text) => internal_TTR(text); @@ -28,6 +31,9 @@ namespace GodotTools.Internals private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged); [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object internal_EditorShortcut(string setting); + + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_TTR(string text); } } diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp index d911f6461c..7433c865f5 100644 --- a/modules/mono/editor/code_completion.cpp +++ b/modules/mono/editor/code_completion.cpp @@ -123,8 +123,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr // AutoLoads OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { - const ProjectSettings::AutoloadInfo &info = E.value; + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.value(); suggestions.push_back(quoted("/root/" + String(info.name))); } } diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 6692a6efec..9a61b63c12 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -306,6 +306,12 @@ MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_d return GDMonoMarshal::variant_to_mono_object(result); } +MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) { + String setting = GDMonoMarshal::mono_string_to_godot(p_setting); + Ref<Shortcut> result = ED_GET_SHORTCUT(setting); + return GDMonoMarshal::variant_to_mono_object(result); +} + MonoString *godot_icall_Globals_TTR(MonoString *p_text) { String text = GDMonoMarshal::mono_string_to_godot(p_text); return GDMonoMarshal::mono_string_from_godot(TTR(text)); @@ -380,6 +386,7 @@ void register_editor_internal_calls() { GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale); GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef); GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef); + GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut); GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR); // Utils.OS diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs index 658582960f..1dc21b6303 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs @@ -78,7 +78,8 @@ namespace Godot /// </example> /// <seealso cref="GetNode{T}(NodePath)"/> /// <param name="path">The path to the node to fetch.</param> - /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>/// ///////// <returns> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> /// The <see cref="Node"/> at the given <paramref name="path"/>, or <see langword="null"/> if not found. /// </returns> public T GetNodeOrNull<T>(NodePath path) where T : class @@ -97,7 +98,8 @@ namespace Godot /// <exception cref="InvalidCastException"> /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. /// </exception> - /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>/// ///////// <returns> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> /// The child <see cref="Node"/> at the given index <paramref name="idx"/>. /// </returns> public T GetChild<T>(int idx) where T : class diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 53c02feaa2..ef42374041 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -376,7 +376,7 @@ namespace Godot /// <example> /// <code> /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1 - /// GD.Print(GD.RangeRange(-10, 1000)); // Prints any number from -10 to 1000 + /// GD.Print(GD.RandRange(-10, 1000)); // Prints any number from -10 to 1000 /// </code> /// </example> /// <returns>A random <see langword="int"/> number inside the given range.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index daea09ba72..c82c5f4588 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -361,6 +361,7 @@ namespace Godot /// <seealso cref="XformInv(Vector2)"/> /// <param name="v">A vector to transform.</param> /// <returns>The transformed vector.</returns> + [Obsolete("Xform is deprecated. Use the multiplication operator (Transform2D * Vector2) instead.")] public Vector2 Xform(Vector2 v) { return new Vector2(Tdotx(v), Tdoty(v)) + origin; @@ -372,6 +373,7 @@ namespace Godot /// <seealso cref="Xform(Vector2)"/> /// <param name="v">A vector to inversely transform.</param> /// <returns>The inversely transformed vector.</returns> + [Obsolete("XformInv is deprecated. Use the multiplication operator (Vector2 * Transform2D) instead.")] public Vector2 XformInv(Vector2 v) { Vector2 vInv = v - origin; @@ -447,7 +449,7 @@ namespace Godot public static Transform2D operator *(Transform2D left, Transform2D right) { - left.origin = left.Xform(right.origin); + left.origin = left * right.origin; real_t x0 = left.Tdotx(right.x); real_t x1 = left.Tdoty(right.x); @@ -462,6 +464,96 @@ namespace Godot return left; } + /// <summary> + /// Returns a Vector2 transformed (multiplied) by transformation matrix. + /// </summary> + /// <param name="transform">The transformation to apply.</param> + /// <param name="vector">A Vector2 to transform.</param> + /// <returns>The transformed Vector2.</returns> + public static Vector2 operator *(Transform2D transform, Vector2 vector) + { + return new Vector2(transform.Tdotx(vector), transform.Tdoty(vector)) + transform.origin; + } + + /// <summary> + /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix. + /// </summary> + /// <param name="vector">A Vector2 to inversely transform.</param> + /// <param name="transform">The transformation to apply.</param> + /// <returns>The inversely transformed Vector2.</returns> + public static Vector2 operator *(Vector2 vector, Transform2D transform) + { + Vector2 vInv = vector - transform.origin; + return new Vector2(transform.x.Dot(vInv), transform.y.Dot(vInv)); + } + + /// <summary> + /// Returns a Rect2 transformed (multiplied) by transformation matrix. + /// </summary> + /// <param name="transform">The transformation to apply.</param> + /// <param name="rect">A Rect2 to transform.</param> + /// <returns>The transformed Rect2.</returns> + public static Rect2 operator *(Transform2D transform, Rect2 rect) + { + Vector2 pos = transform * rect.Position; + Vector2 toX = transform.x * rect.Size.x; + Vector2 toY = transform.y * rect.Size.y; + + return new Rect2(pos, rect.Size).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY); + } + + /// <summary> + /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix. + /// </summary> + /// <param name="rect">A Rect2 to inversely transform.</param> + /// <param name="transform">The transformation to apply.</param> + /// <returns>The inversely transformed Rect2.</returns> + public static Rect2 operator *(Rect2 rect, Transform2D transform) + { + Vector2 pos = rect.Position * transform; + Vector2 to1 = new Vector2(rect.Position.x, rect.Position.y + rect.Size.y) * transform; + Vector2 to2 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y + rect.Size.y) * transform; + Vector2 to3 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y) * transform; + + return new Rect2(pos, rect.Size).Expand(to1).Expand(to2).Expand(to3); + } + + /// <summary> + /// Returns a copy of the given Vector2[] transformed (multiplied) by transformation matrix. + /// </summary> + /// <param name="transform">The transformation to apply.</param> + /// <param name="array">A Vector2[] to transform.</param> + /// <returns>The transformed copy of the Vector2[].</returns> + public static Vector2[] operator *(Transform2D transform, Vector2[] array) + { + Vector2[] newArray = new Vector2[array.Length]; + + for (int i = 0; i < array.Length; i++) + { + newArray[i] = transform * array[i]; + } + + return newArray; + } + + /// <summary> + /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix. + /// </summary> + /// <param name="array">A Vector2[] to inversely transform.</param> + /// <param name="transform">The transformation to apply.</param> + /// <returns>The inversely transformed copy of the Vector2[].</returns> + public static Vector2[] operator *(Vector2[] array, Transform2D transform) + { + Vector2[] newArray = new Vector2[array.Length]; + + for (int i = 0; i < array.Length; i++) + { + newArray[i] = array[i] * transform; + } + + return newArray; + } + public static bool operator ==(Transform2D left, Transform2D right) { return left.Equals(right); diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 905d10c9d4..bb6bc578a4 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -45,7 +45,7 @@ #include "scene/resources/primitive_meshes.h" #include "scene/resources/shape_3d.h" #include "scene/resources/sphere_shape_3d.h" -#include "scene/resources/world_margin_shape_3d.h" +#include "scene/resources/world_boundary_shape_3d.h" #ifdef TOOLS_ENABLED #include "editor/editor_node.h" diff --git a/modules/ogg/config.py b/modules/ogg/config.py index d22f9454ed..5a417ba8dd 100644 --- a/modules/ogg/config.py +++ b/modules/ogg/config.py @@ -4,3 +4,14 @@ def can_build(env, platform): def configure(env): pass + + +def get_doc_classes(): + return [ + "OGGPacketSequence", + "OGGPacketSequencePlayback", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/ogg/doc_classes/OGGPacketSequence.xml b/modules/ogg/doc_classes/OGGPacketSequence.xml new file mode 100644 index 0000000000..9d3789cb07 --- /dev/null +++ b/modules/ogg/doc_classes/OGGPacketSequence.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="OGGPacketSequence" inherits="Resource" version="4.0"> + <brief_description> + A sequence of OGG packets. + </brief_description> + <description> + A sequence of OGG packets. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_length" qualifiers="const"> + <return type="float" /> + <description> + The length of this stream, in seconds. + </description> + </method> + </methods> + <members> + <member name="granule_positions" type="Array" setter="set_packet_granule_positions" getter="get_packet_granule_positions" default="[]"> + Contains the granule positions for each page in this packet sequence. + </member> + <member name="packet_data" type="Array" setter="set_packet_data" getter="get_packet_data" default="[]"> + Contains the raw packets that make up this OGGPacketSequence. + </member> + <member name="sampling_rate" type="float" setter="set_sampling_rate" getter="get_sampling_rate" default="0.0"> + Holds sample rate information about this sequence. Must be set by another class that actually understands the codec. + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml b/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml new file mode 100644 index 0000000000..49e32f0d6e --- /dev/null +++ b/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="OGGPacketSequencePlayback" inherits="RefCounted" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp new file mode 100644 index 0000000000..b7a3ad2876 --- /dev/null +++ b/modules/ogg/ogg_packet_sequence.cpp @@ -0,0 +1,220 @@ +/*************************************************************************/ +/* ogg_packet_sequence.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "ogg_packet_sequence.h" +#include "core/variant/typed_array.h" + +void OGGPacketSequence::push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data) { + Vector<PackedByteArray> data_stored; + for (int i = 0; i < p_data.size(); i++) { + data_stored.push_back(p_data[i]); + } + page_granule_positions.push_back(p_granule_pos); + page_data.push_back(data_stored); + data_version++; +} + +void OGGPacketSequence::set_packet_data(const Array &p_data) { + data_version++; // Update the data version so old playbacks know that they can't rely on us anymore. + page_data.clear(); + for (int page_idx = 0; page_idx < p_data.size(); page_idx++) { + // Push a new page. We cleared the vector so this will be at index `page_idx`. + page_data.push_back(Vector<PackedByteArray>()); + TypedArray<PackedByteArray> this_page_data = p_data[page_idx]; + for (int packet = 0; packet < this_page_data.size(); packet++) { + page_data.write[page_idx].push_back(this_page_data[packet]); + } + } +} + +Array OGGPacketSequence::get_packet_data() const { + Array ret; + for (const Vector<PackedByteArray> &page : page_data) { + Array page_variant; + for (const PackedByteArray &packet : page) { + page_variant.push_back(packet); + } + ret.push_back(page_variant); + } + return ret; +} + +void OGGPacketSequence::set_packet_granule_positions(const Array &p_granule_positions) { + data_version++; // Update the data version so old playbacks know that they can't rely on us anymore. + page_granule_positions.clear(); + for (int page_idx = 0; page_idx < p_granule_positions.size(); page_idx++) { + int64_t granule_pos = p_granule_positions[page_idx]; + page_granule_positions.push_back(granule_pos); + } +} + +Array OGGPacketSequence::get_packet_granule_positions() const { + Array ret; + for (int64_t granule_pos : page_granule_positions) { + ret.push_back(granule_pos); + } + return ret; +} + +void OGGPacketSequence::set_sampling_rate(float p_sampling_rate) { + sampling_rate = p_sampling_rate; +} + +float OGGPacketSequence::get_sampling_rate() const { + return sampling_rate; +} + +int64_t OGGPacketSequence::get_final_granule_pos() const { + if (!page_granule_positions.is_empty()) { + return page_granule_positions[page_granule_positions.size() - 1]; + } + return -1; +} + +float OGGPacketSequence::get_length() const { + int64_t granule_pos = get_final_granule_pos(); + if (granule_pos < 0) { + return 0; + } + return granule_pos / sampling_rate; +} + +Ref<OGGPacketSequencePlayback> OGGPacketSequence::instance_playback() { + Ref<OGGPacketSequencePlayback> playback; + playback.instantiate(); + playback->ogg_packet_sequence = Ref<OGGPacketSequence>(this); + playback->data_version = data_version; + + return playback; +} + +void OGGPacketSequence::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_packet_data", "packet_data"), &OGGPacketSequence::set_packet_data); + ClassDB::bind_method(D_METHOD("get_packet_data"), &OGGPacketSequence::get_packet_data); + + ClassDB::bind_method(D_METHOD("set_packet_granule_positions", "granule_positions"), &OGGPacketSequence::set_packet_granule_positions); + ClassDB::bind_method(D_METHOD("get_packet_granule_positions"), &OGGPacketSequence::get_packet_granule_positions); + + ClassDB::bind_method(D_METHOD("set_sampling_rate", "sampling_rate"), &OGGPacketSequence::set_sampling_rate); + ClassDB::bind_method(D_METHOD("get_sampling_rate"), &OGGPacketSequence::get_sampling_rate); + + ClassDB::bind_method(D_METHOD("get_length"), &OGGPacketSequence::get_length); + + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "packet_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_data", "get_packet_data"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "granule_positions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_granule_positions", "get_packet_granule_positions"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sampling_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_sampling_rate", "get_sampling_rate"); +} + +bool OGGPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const { + ERR_FAIL_COND_V(data_version != ogg_packet_sequence->data_version, false); + ERR_FAIL_COND_V(ogg_packet_sequence->page_data.is_empty(), false); + ERR_FAIL_COND_V(ogg_packet_sequence->page_granule_positions.is_empty(), false); + // Move on to the next page if need be. This happens first to help simplify seek logic. + while (packet_cursor >= ogg_packet_sequence->page_data[page_cursor].size()) { + packet_cursor = 0; + page_cursor++; + if (page_cursor >= ogg_packet_sequence->page_data.size()) { + return false; + } + } + + ERR_FAIL_COND_V(page_cursor >= ogg_packet_sequence->page_data.size(), false); + + packet->b_o_s = page_cursor == 0 && packet_cursor == 0; + packet->e_o_s = page_cursor == ogg_packet_sequence->page_data.size() - 1 && packet_cursor == ogg_packet_sequence->page_data[page_cursor].size() - 1; + packet->granulepos = packet_cursor == ogg_packet_sequence->page_data[page_cursor].size() - 1 ? ogg_packet_sequence->page_granule_positions[page_cursor] : -1; + packet->packetno = packetno++; + packet->bytes = ogg_packet_sequence->page_data[page_cursor][packet_cursor].size(); + packet->packet = (unsigned char *)(ogg_packet_sequence->page_data[page_cursor][packet_cursor].ptr()); + + *p_packet = packet; + + packet_cursor++; + + return true; +} + +uint32_t OGGPacketSequencePlayback::seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive) { + if (before_page_inclusive == after_page_inclusive) { + return before_page_inclusive; + } + uint32_t actual_middle_page = after_page_inclusive + (before_page_inclusive - after_page_inclusive) / 2; + // Complicating the bisection search algorithm, the middle page might not have a packet that ends on it, + // which means it might not have a correct granule position. Find a nearby page that does have a packet ending on it. + uint32_t bisection_page = -1; + for (uint32_t test_page = actual_middle_page; test_page <= before_page_inclusive; test_page++) { + if (ogg_packet_sequence->page_data[test_page].size() > 0) { + bisection_page = test_page; + break; + } + } + // Check if we have to go backwards. + if (bisection_page == (unsigned int)-1) { + for (uint32_t test_page = actual_middle_page; test_page >= after_page_inclusive; test_page--) { + if (ogg_packet_sequence->page_data[test_page].size() > 0) { + bisection_page = test_page; + break; + } + } + } + if (bisection_page == (unsigned int)-1) { + return -1; + } + + int64_t bisection_granule_pos = ogg_packet_sequence->page_granule_positions[bisection_page]; + if (granule > bisection_granule_pos) { + return seek_page_internal(granule, bisection_page + 1, before_page_inclusive); + } else { + return seek_page_internal(granule, after_page_inclusive, bisection_page); + } +} + +bool OGGPacketSequencePlayback::seek_page(int64_t p_granule_pos) { + int correct_page = seek_page_internal(p_granule_pos, 0, ogg_packet_sequence->page_data.size() - 1); + if (correct_page == -1) { + return false; + } + + packet_cursor = 0; + page_cursor = correct_page; + + // Don't pretend subsequent packets are contiguous with previous ones. + packetno = 0; + + return true; +} + +OGGPacketSequencePlayback::OGGPacketSequencePlayback() { + packet = new ogg_packet(); +} + +OGGPacketSequencePlayback::~OGGPacketSequencePlayback() { + delete packet; +} diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h new file mode 100644 index 0000000000..b00ada06c1 --- /dev/null +++ b/modules/ogg/ogg_packet_sequence.h @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* ogg_packet_sequence.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OGG_PACKET_SEQUENCE_H +#define OGG_PACKET_SEQUENCE_H + +#include "core/io/resource.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/variant/native_ptr.h" +#include "core/variant/typed_array.h" +#include "core/variant/variant.h" +#include "thirdparty/libogg/ogg/ogg.h" + +class OGGPacketSequencePlayback; + +class OGGPacketSequence : public Resource { + GDCLASS(OGGPacketSequence, Resource); + + friend class OGGPacketSequencePlayback; + + // List of pages, each of which is a list of packets on that page. The innermost PackedByteArrays contain complete ogg packets. + Vector<Vector<PackedByteArray>> page_data; + + // List of the granule position for each page. + Vector<uint64_t> page_granule_positions; + + // The page after the current last page. Similar semantics to an end() iterator. + int64_t end_page = 0; + + uint64_t data_version = 0; + + float sampling_rate = 0; + float length = 0; + +protected: + static void _bind_methods(); + +public: + // Pushes information about all the pages that ended on this page. + // This should be called for each page, even for pages that no packets ended on. + void push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data); + + void set_packet_data(const Array &p_data); + Array get_packet_data() const; + + void set_packet_granule_positions(const Array &p_granule_positions); + Array get_packet_granule_positions() const; + + // Sets a sampling rate associated with this object. OGGPacketSequence doesn't understand codecs, + // so this value is naively stored as a convenience. + void set_sampling_rate(float p_sampling_rate); + + // Returns a sampling rate previously set by set_sampling_rate(). + float get_sampling_rate() const; + + // Returns a length previously set by set_length(). + float get_length() const; + + // Returns the granule position of the last page in this sequence. + int64_t get_final_granule_pos() const; + + Ref<OGGPacketSequencePlayback> instance_playback(); + + OGGPacketSequence() {} + virtual ~OGGPacketSequence() {} +}; + +class OGGPacketSequencePlayback : public RefCounted { + GDCLASS(OGGPacketSequencePlayback, RefCounted); + + friend class OGGPacketSequence; + + Ref<OGGPacketSequence> ogg_packet_sequence; + + mutable int64_t page_cursor = 0; + mutable int32_t packet_cursor = 0; + + mutable ogg_packet *packet; + + uint64_t data_version; + + mutable int64_t packetno = 0; + + // Recursive bisection search for the correct page. + uint32_t seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive); + +public: + // Calling functions must not modify this packet. + // Returns true on success, false on error or if there is no next packet. + bool next_ogg_packet(ogg_packet **p_packet) const; + + // Seeks to the page such that the previous page has a granule position less than or equal to this value, + // and the current page has a granule position greater than this value. + // Returns true on success, false on failure. + bool seek_page(int64_t p_granule_pos); + + OGGPacketSequencePlayback(); + virtual ~OGGPacketSequencePlayback(); +}; + +#endif // OGG_PACKET_SEQUENCE_H diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp index b23ea65378..3448e7063a 100644 --- a/modules/ogg/register_types.cpp +++ b/modules/ogg/register_types.cpp @@ -30,8 +30,11 @@ #include "register_types.h" -// Dummy module as libogg is needed by other modules (vorbis, theora, opus, ...) +#include "ogg_packet_sequence.h" -void register_ogg_types() {} +void register_ogg_types() { + GDREGISTER_CLASS(OGGPacketSequence); + GDREGISTER_CLASS(OGGPacketSequencePlayback); +} void unregister_ogg_types() {} diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub index 6e7b3e7b8d..1fdc8fe1b3 100644 --- a/modules/raycast/SCsub +++ b/modules/raycast/SCsub @@ -55,6 +55,9 @@ if env["builtin_embree"]: "kernels/bvh/bvh_builder_sah_mb.cpp", "kernels/bvh/bvh_builder_twolevel.cpp", "kernels/bvh/bvh_intersector1_bvh4.cpp", + "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp", + "kernels/bvh/bvh_intersector_stream_bvh4.cpp", + "kernels/bvh/bvh_intersector_stream_filters.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in embree_src] diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py index 31a25a318f..e31d88b741 100644 --- a/modules/raycast/godot_update_embree.py +++ b/modules/raycast/godot_update_embree.py @@ -61,6 +61,11 @@ cpp_files = [ "kernels/bvh/bvh_builder_twolevel.cpp", "kernels/bvh/bvh_intersector1.cpp", "kernels/bvh/bvh_intersector1_bvh4.cpp", + "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp", + "kernels/bvh/bvh_intersector_stream_bvh4.cpp", + "kernels/bvh/bvh_intersector_stream_filters.cpp", + "kernels/bvh/bvh_intersector_hybrid.cpp", + "kernels/bvh/bvh_intersector_stream.cpp", ] os.chdir("../../thirdparty") @@ -117,7 +122,7 @@ with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file: /* #undef EMBREE_GEOMETRY_INSTANCE */ /* #undef EMBREE_GEOMETRY_GRID */ /* #undef EMBREE_GEOMETRY_POINT */ -/* #undef EMBREE_RAY_PACKETS */ +#define EMBREE_RAY_PACKETS /* #undef EMBREE_COMPACT_POLYS */ #define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0 @@ -249,3 +254,8 @@ with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as con os.chdir("..") shutil.rmtree("embree-tmp") + +subprocess.run(["git", "restore", "embree/patches"]) + +for patch in os.listdir("embree/patches"): + subprocess.run(["git", "apply", "embree/patches/" + patch]) diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub deleted file mode 100644 index 8fddb23dc8..0000000000 --- a/modules/stb_vorbis/SCsub +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -env_stb_vorbis = env_modules.Clone() - -# Thirdparty source files - -thirdparty_obj = [] - -thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"] - -env_thirdparty = env_stb_vorbis.Clone() -env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) -env.modules_sources += thirdparty_obj - -# Godot source files - -module_obj = [] - -env_stb_vorbis.add_source_files(module_obj, "*.cpp") -env.modules_sources += module_obj - -# Needed to force rebuilding the module files when the thirdparty library is updated. -env.Depends(module_obj, thirdparty_obj) diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp deleted file mode 100644 index 6554c6e274..0000000000 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/*************************************************************************/ -/* audio_stream_ogg_vorbis.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "audio_stream_ogg_vorbis.h" - -#include "core/io/file_access.h" - -int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND_V(!active, 0); - - int todo = p_frames; - - int start_buffer = 0; - - int frames_mixed_this_step = p_frames; - - while (todo && active) { - float *buffer = (float *)p_buffer; - if (start_buffer > 0) { - buffer = (buffer + start_buffer * 2); - } - int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, buffer, todo * 2); - if (vorbis_stream->channels == 1 && mixed > 0) { - //mix mono to stereo - for (int i = start_buffer; i < start_buffer + mixed; i++) { - p_buffer[i].r = p_buffer[i].l; - } - } - todo -= mixed; - frames_mixed += mixed; - - if (todo) { - //end of file! - bool is_not_empty = mixed > 0 || stb_vorbis_stream_length_in_samples(ogg_stream) > 0; - if (vorbis_stream->loop && is_not_empty) { - //loop - seek(vorbis_stream->loop_offset); - loops++; - // we still have buffer to fill, start from this element in the next iteration. - start_buffer = p_frames - todo; - } else { - frames_mixed_this_step = p_frames - todo; - for (int i = p_frames - todo; i < p_frames; i++) { - p_buffer[i] = AudioFrame(0, 0); - } - active = false; - todo = 0; - } - } - } - return frames_mixed_this_step; -} - -float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() { - return vorbis_stream->sample_rate; -} - -void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) { - active = true; - seek(p_from_pos); - loops = 0; - _begin_resample(); -} - -void AudioStreamPlaybackOGGVorbis::stop() { - active = false; -} - -bool AudioStreamPlaybackOGGVorbis::is_playing() const { - return active; -} - -int AudioStreamPlaybackOGGVorbis::get_loop_count() const { - return loops; -} - -float AudioStreamPlaybackOGGVorbis::get_playback_position() const { - return float(frames_mixed) / vorbis_stream->sample_rate; -} - -void AudioStreamPlaybackOGGVorbis::seek(float p_time) { - if (!active) { - return; - } - - if (p_time >= vorbis_stream->get_length()) { - p_time = 0; - } - frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time); - - stb_vorbis_seek(ogg_stream, frames_mixed); -} - -AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { - if (ogg_alloc.alloc_buffer) { - stb_vorbis_close(ogg_stream); - memfree(ogg_alloc.alloc_buffer); - } -} - -Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() { - Ref<AudioStreamPlaybackOGGVorbis> ovs; - - ERR_FAIL_COND_V_MSG(data == nullptr, ovs, - "This AudioStreamOGGVorbis does not have an audio file assigned " - "to it. AudioStreamOGGVorbis should not be created from the " - "inspector or with `.new()`. Instead, load an audio file."); - - ovs.instantiate(); - ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this); - ovs->ogg_alloc.alloc_buffer = (char *)memalloc(decode_mem_size); - ovs->ogg_alloc.alloc_buffer_length_in_bytes = decode_mem_size; - ovs->frames_mixed = 0; - ovs->active = false; - ovs->loops = 0; - int error; - ovs->ogg_stream = stb_vorbis_open_memory((const unsigned char *)data, data_len, &error, &ovs->ogg_alloc); - if (!ovs->ogg_stream) { - memfree(ovs->ogg_alloc.alloc_buffer); - ovs->ogg_alloc.alloc_buffer = nullptr; - ERR_FAIL_COND_V(!ovs->ogg_stream, Ref<AudioStreamPlaybackOGGVorbis>()); - } - - return ovs; -} - -String AudioStreamOGGVorbis::get_stream_name() const { - return ""; //return stream_name; -} - -void AudioStreamOGGVorbis::clear_data() { - if (data) { - memfree(data); - data = nullptr; - data_len = 0; - } -} - -void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) { - int src_data_len = p_data.size(); - uint32_t alloc_try = 1024; - Vector<char> alloc_mem; - char *w; - stb_vorbis *ogg_stream = nullptr; - stb_vorbis_alloc ogg_alloc; - - // Vorbis comments may be up to UINT32_MAX, but that's arguably pretty rare. - // Let's go with 2^30 so we don't risk going out of bounds. - const uint32_t MAX_TEST_MEM = 1 << 30; - - while (alloc_try < MAX_TEST_MEM) { - alloc_mem.resize(alloc_try); - w = alloc_mem.ptrw(); - - ogg_alloc.alloc_buffer = w; - ogg_alloc.alloc_buffer_length_in_bytes = alloc_try; - - const uint8_t *src_datar = p_data.ptr(); - - int error; - ogg_stream = stb_vorbis_open_memory((const unsigned char *)src_datar, src_data_len, &error, &ogg_alloc); - - if (!ogg_stream && error == VORBIS_outofmem) { - alloc_try *= 2; - } else { - ERR_FAIL_COND(alloc_try == MAX_TEST_MEM); - ERR_FAIL_COND(ogg_stream == nullptr); - - stb_vorbis_info info = stb_vorbis_get_info(ogg_stream); - - channels = info.channels; - sample_rate = info.sample_rate; - decode_mem_size = alloc_try; - //does this work? (it's less mem..) - //decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size; - - length = stb_vorbis_stream_length_in_seconds(ogg_stream); - stb_vorbis_close(ogg_stream); - - // free any existing data - clear_data(); - - data = memalloc(src_data_len); - memcpy(data, src_datar, src_data_len); - data_len = src_data_len; - - break; - } - } - - ERR_FAIL_COND_MSG(alloc_try == MAX_TEST_MEM, vformat("Couldn't set vorbis data even with an alloc buffer of %d bytes, report bug.", MAX_TEST_MEM)); -} - -Vector<uint8_t> AudioStreamOGGVorbis::get_data() const { - Vector<uint8_t> vdata; - - if (data_len && data) { - vdata.resize(data_len); - { - uint8_t *w = vdata.ptrw(); - memcpy(w, data, data_len); - } - } - - return vdata; -} - -void AudioStreamOGGVorbis::set_loop(bool p_enable) { - loop = p_enable; -} - -bool AudioStreamOGGVorbis::has_loop() const { - return loop; -} - -void AudioStreamOGGVorbis::set_loop_offset(float p_seconds) { - loop_offset = p_seconds; -} - -float AudioStreamOGGVorbis::get_loop_offset() const { - return loop_offset; -} - -float AudioStreamOGGVorbis::get_length() const { - return length; -} - -bool AudioStreamOGGVorbis::is_monophonic() const { - return false; -} - -void AudioStreamOGGVorbis::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data); - - ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop); - ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop); - - ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset); - ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset); - - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset"); -} - -AudioStreamOGGVorbis::AudioStreamOGGVorbis() {} - -AudioStreamOGGVorbis::~AudioStreamOGGVorbis() { - clear_data(); -} diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py deleted file mode 100644 index 1eb0a8cf33..0000000000 --- a/modules/stb_vorbis/config.py +++ /dev/null @@ -1,16 +0,0 @@ -def can_build(env, platform): - return True - - -def configure(env): - pass - - -def get_doc_classes(): - return [ - "AudioStreamOGGVorbis", - ] - - -def get_doc_path(): - return "doc_classes" diff --git a/modules/stb_vorbis/register_types.cpp b/modules/stb_vorbis/register_types.cpp deleted file mode 100644 index bdb1cf69cf..0000000000 --- a/modules/stb_vorbis/register_types.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "register_types.h" - -#include "audio_stream_ogg_vorbis.h" - -#ifdef TOOLS_ENABLED -#include "core/config/engine.h" -#include "resource_importer_ogg_vorbis.h" -#endif - -void register_stb_vorbis_types() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - Ref<ResourceImporterOGGVorbis> ogg_import; - ogg_import.instantiate(); - ResourceFormatImporter::get_singleton()->add_importer(ogg_import); - } -#endif - GDREGISTER_CLASS(AudioStreamOGGVorbis); -} - -void unregister_stb_vorbis_types() { -} diff --git a/modules/stb_vorbis/register_types.h b/modules/stb_vorbis/register_types.h deleted file mode 100644 index d36d87606c..0000000000 --- a/modules/stb_vorbis/register_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef STB_VORBIS_REGISTER_TYPES_H -#define STB_VORBIS_REGISTER_TYPES_H - -void register_stb_vorbis_types(); -void unregister_stb_vorbis_types(); - -#endif // STB_VORBIS_REGISTER_TYPES_H diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 6691f86e60..7cd4db6f67 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -64,6 +64,7 @@ if env["builtin_harfbuzz"]: #'src/hb-gobject-structs.cc', "src/hb-icu.cc", "src/hb-map.cc", + "src/hb-ms-feature-ranges.cc", "src/hb-number.cc", "src/hb-ot-cff1-table.cc", "src/hb-ot-cff2-table.cc", diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index f9bbd25a5f..d1e849def8 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -30,6 +30,8 @@ #include "script_iterator.h" +// This implementation is derived from ICU: icu4c/source/extra/scrptrun/scrptrun.cpp + bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) { return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two; } @@ -48,7 +50,8 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length p_start = 0; } - ParenStackEntry paren_stack[128]; + int paren_size = PAREN_STACK_DEPTH; + ParenStackEntry *paren_stack = (ParenStackEntry *)memalloc(paren_size * sizeof(ParenStackEntry)); int script_start; int script_end = p_start; @@ -64,13 +67,22 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length UChar32 ch = str[script_end]; UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { + memfree(paren_stack); ERR_FAIL_MSG(u_errorName(err)); } if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) { if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) { - paren_stack[++paren_sp].pair_index = ch; + // If it's an open character, push it onto the stack. + paren_sp++; + if (unlikely(paren_sp >= paren_size)) { + // If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text. + paren_size += PAREN_STACK_DEPTH; + paren_stack = (ParenStackEntry *)memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)); + } + paren_stack[paren_sp].pair_index = ch; paren_stack[paren_sp].script_code = script_code; } else if (paren_sp >= 0) { + // If it's a close character, find the matching open on the stack, and use that script code. Any non-matching open characters above it on the stack will be poped. UChar32 paired_ch = u_getBidiPairedBracket(ch); while (paren_sp >= 0 && paren_stack[paren_sp].pair_index != paired_ch) { paren_sp -= 1; @@ -87,11 +99,13 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length if (same_script(script_code, sc)) { if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) { script_code = sc; + // Now that we have a final script code, fix any open characters we pushed before we knew the script code. while (start_sp < paren_sp) { paren_stack[++start_sp].script_code = script_code; } } if ((u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_CLOSE) && paren_sp >= 0) { + // If this character is a close paired character pop the matching open character from the stack. paren_sp -= 1; if (start_sp >= 0) { start_sp -= 1; @@ -109,4 +123,6 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length script_ranges.push_back(rng); } while (script_end < p_length); + + memfree(paren_stack); } diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 896a0e5c15..5efd40f7c4 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -43,6 +43,8 @@ #include <hb.h> class ScriptIterator { + static const int PAREN_STACK_DEPTH = 128; + public: struct ScriptRange { int start = 0; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 19e94adf68..22706f9b6a 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -30,6 +30,7 @@ #include "text_server_adv.h" +#include "core/error/error_macros.h" #include "core/string/print_string.h" #include "core/string/translation.h" @@ -1225,7 +1226,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced int error = 0; if (!library) { error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -1243,13 +1244,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced if (error) { FT_Done_Face(fd->face); fd->face = nullptr; - ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); - } - fd->hb_handle = hb_ft_font_create(fd->face, nullptr); - if (fd->hb_handle == nullptr) { - FT_Done_Face(fd->face); - fd->face = nullptr; - ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object.")); + ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } if (p_font_data->msdf) { @@ -1278,6 +1273,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); } + fd->hb_handle = hb_ft_font_create(fd->face, nullptr); + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; @@ -1592,14 +1589,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced FT_Done_MM_Var(library, amaster); } #else - ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); + ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { // Init bitmap font. fd->hb_handle = hb_bmp_font_create(fd, nullptr); - if (!fd->hb_handle) { - ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object.")); - } } p_font_data->cache[p_size] = fd; return true; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index e4e6797f92..8a1bd93c65 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -30,6 +30,7 @@ #include "text_server_fb.h" +#include "core/error/error_macros.h" #include "core/string/print_string.h" #ifdef MODULE_MSDFGEN_ENABLED @@ -686,7 +687,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback int error = 0; if (!library) { error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -704,7 +705,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback if (error) { FT_Done_Face(fd->face); fd->face = nullptr; - ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); + ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } if (p_font_data->msdf) { @@ -784,7 +785,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback FT_Done_MM_Var(library, amaster); } #else - ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); + ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } p_font_data->cache[p_size] = fd; diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp index 2b48e94604..54240e66fc 100644 --- a/modules/vhacd/register_types.cpp +++ b/modules/vhacd/register_types.cpp @@ -32,48 +32,55 @@ #include "scene/resources/mesh.h" #include "thirdparty/vhacd/public/VHACD.h" -static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int p_max_convex_hulls = -1) { - Vector<real_t> vertices; - vertices.resize(p_faces.size() * 9); - Vector<uint32_t> indices; - indices.resize(p_faces.size() * 3); - - for (int i = 0; i < p_faces.size(); i++) { - for (int j = 0; j < 3; j++) { - vertices.write[i * 9 + j * 3 + 0] = p_faces[i].vertex[j].x; - vertices.write[i * 9 + j * 3 + 1] = p_faces[i].vertex[j].y; - vertices.write[i * 9 + j * 3 + 2] = p_faces[i].vertex[j].z; - indices.write[i * 3 + j] = i * 3 + j; - } - } - +static Vector<Vector<Vector3>> convex_decompose(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, const Mesh::ConvexDecompositionSettings &p_settings, Vector<Vector<uint32_t>> *r_convex_indices) { VHACD::IVHACD::Parameters params; - if (p_max_convex_hulls > 0) { - params.m_maxConvexHulls = p_max_convex_hulls; - } + params.m_concavity = p_settings.max_concavity; + params.m_alpha = p_settings.symmetry_planes_clipping_bias; + params.m_beta = p_settings.revolution_axes_clipping_bias; + params.m_minVolumePerCH = p_settings.min_volume_per_convex_hull; + params.m_resolution = p_settings.resolution; + params.m_maxNumVerticesPerCH = p_settings.max_num_vertices_per_convex_hull; + params.m_planeDownsampling = p_settings.plane_downsampling; + params.m_convexhullDownsampling = p_settings.convexhull_downsampling; + params.m_pca = p_settings.normalize_mesh; + params.m_mode = p_settings.mode; + params.m_convexhullApproximation = p_settings.convexhull_approximation; + params.m_oclAcceleration = true; + params.m_maxConvexHulls = p_settings.max_convex_hulls; + params.m_projectHullVertices = p_settings.project_hull_vertices; VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); - decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params); + decomposer->Compute(p_vertices, p_vertex_count, p_triangles, p_triangle_count, params); int hull_count = decomposer->GetNConvexHulls(); - Vector<Vector<Face3>> ret; + Vector<Vector<Vector3>> ret; + ret.resize(hull_count); + + if (r_convex_indices) { + r_convex_indices->resize(hull_count); + } for (int i = 0; i < hull_count; i++) { - Vector<Face3> triangles; VHACD::IVHACD::ConvexHull hull; decomposer->GetConvexHull(i, hull); - triangles.resize(hull.m_nTriangles); - for (uint32_t j = 0; j < hull.m_nTriangles; j++) { - Face3 f; + + Vector<Vector3> &points = ret.write[i]; + points.resize(hull.m_nPoints); + + Vector3 *w = points.ptrw(); + for (uint32_t j = 0; j < hull.m_nPoints; ++j) { for (int k = 0; k < 3; k++) { - for (int l = 0; l < 3; l++) { - f.vertex[k][l] = hull.m_points[hull.m_triangles[j * 3 + k] * 3 + l]; - } + w[j][k] = hull.m_points[j * 3 + k]; } - triangles.write[j] = f; } - ret.push_back(triangles); + + if (r_convex_indices) { + Vector<uint32_t> &indices = r_convex_indices->write[i]; + indices.resize(hull.m_nTriangles * 3); + + memcpy(indices.ptrw(), hull.m_triangles, hull.m_nTriangles * 3 * sizeof(uint32_t)); + } } decomposer->Clean(); @@ -83,9 +90,9 @@ static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int } void register_vhacd_types() { - Mesh::convex_composition_function = convex_decompose; + Mesh::convex_decomposition_function = convex_decompose; } void unregister_vhacd_types() { - Mesh::convex_composition_function = nullptr; + Mesh::convex_decomposition_function = nullptr; } diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index b574576856..2c6313c80a 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -131,7 +131,7 @@ The [code]inputs[/code] array contains the values of the input ports. [code]outputs[/code] is an array whose indices should be set to the respective outputs. The [code]start_mode[/code] is usually [constant START_MODE_BEGIN_SEQUENCE], unless you have used the [code]STEP_*[/code] constants. - [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. + [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. The size needs to be predefined using [method _get_working_memory_size]. When returning, you can mask the returned value with one of the [code]STEP_*[/code] constants. </description> </method> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 7fb9707fce..890861cf82 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -117,7 +117,7 @@ void register_visual_script_types() { GDREGISTER_CLASS(VisualScriptCustomNodes); ClassDB::set_current_api(ClassDB::API_CORE); vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes); - Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", VisualScriptCustomNodes::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptCustomNodes", VisualScriptCustomNodes::get_singleton())); VisualScriptEditor::register_editor(); #endif diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index eee9e8f32b..0a6bcedf31 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -2546,16 +2546,11 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { error_line = p_line; } - List<StringName> functions; - script->get_function_list(&functions); - for (const StringName &E : functions) { - if (script->has_node(p_line)) { - _update_graph(); - _update_members(); + if (script->has_node(p_line)) { + _update_graph(); + _update_members(); - call_deferred(SNAME("call_deferred"), "_center_on_node", E, p_line); //editor might be just created and size might not exist yet - return; - } + call_deferred(SNAME("call_deferred"), "_center_on_node", p_line); // The editor might be just created and size might not exist yet. } } @@ -3592,6 +3587,11 @@ void VisualScriptEditor::_hide_timer() { hint_text->hide(); } +void VisualScriptEditor::_toggle_scripts_pressed() { + ScriptEditor::get_singleton()->toggle_scripts_panel(); + update_toggle_scripts_button(); +} + void VisualScriptEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { @@ -3606,6 +3606,8 @@ void VisualScriptEditor::_notification(int p_what) { return; } + update_toggle_scripts_button(); + edit_variable_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); edit_signal_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); func_input_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); @@ -3650,6 +3652,7 @@ void VisualScriptEditor::_notification(int p_what) { } } break; case NOTIFICATION_VISIBILITY_CHANGED: { + update_toggle_scripts_button(); members_section->set_visible(is_visible_in_tree()); } break; } @@ -4232,6 +4235,15 @@ void VisualScriptEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_h void VisualScriptEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) { } +void VisualScriptEditor::update_toggle_scripts_button() { + if (is_layout_rtl()) { + toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"), SNAME("EditorIcons"))); + } else { + toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons"))); + } + toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); +} + void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node); ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1)); @@ -4333,6 +4345,16 @@ VisualScriptEditor::VisualScriptEditor() { graph->hide(); graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed)); + status_bar = memnew(HBoxContainer); + add_child(status_bar); + status_bar->set_h_size_flags(SIZE_EXPAND_FILL); + status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); + + toggle_scripts_button = memnew(Button); + toggle_scripts_button->set_flat(true); + toggle_scripts_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_scripts_pressed)); + status_bar->add_child(toggle_scripts_button); + /// Add Buttons to Top Bar/Zoom bar. HBoxContainer *graph_hbc = graph->get_zoom_hbox(); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index ab32aae7aa..19f5aabac9 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -93,6 +93,8 @@ class VisualScriptEditor : public ScriptEditorBase { ConfirmationDialog *function_create_dialog; GraphEdit *graph; + HBoxContainer *status_bar; + Button *toggle_scripts_button; VisualScriptEditorSignalEdit *signal_editor; @@ -281,6 +283,8 @@ class VisualScriptEditor : public ScriptEditorBase { void _member_rmb_selected(const Vector2 &p_pos); void _member_option(int p_option); + void _toggle_scripts_pressed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -330,6 +334,8 @@ public: static void free_clipboard(); + void update_toggle_scripts_button() override; + VisualScriptEditor(); ~VisualScriptEditor(); }; diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 6ba5ad4fd6..205918a5f0 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1010,7 +1010,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const if (index != StringName()) { detail_prop_name += "." + String(index); } - PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, PROPERTY_HINT_TYPE_STRING, E.hint_string); + PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, E.hint, E.hint_string); _adjust_input_index(pinfo); return pinfo; } diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index bc31fff066..322314487f 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -3,9 +3,6 @@ Import("env") Import("env_modules") -# Only kept to build the thirdparty library used by the theora and webm -# modules. We now use stb_vorbis for AudioStreamOGGVorbis. - env_vorbis = env_modules.Clone() # Thirdparty source files diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp new file mode 100644 index 0000000000..e4a80c339f --- /dev/null +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -0,0 +1,435 @@ +/*************************************************************************/ +/* audio_stream_ogg_vorbis.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "audio_stream_ogg_vorbis.h" + +#include "core/io/file_access.h" +#include "core/variant/typed_array.h" +#include "thirdparty/libogg/ogg/ogg.h" + +int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!ready, 0); + ERR_FAIL_COND_V(!active, 0); + + int todo = p_frames; + + int start_buffer = 0; + + int frames_mixed_this_step = p_frames; + + while (todo && active) { + AudioFrame *buffer = p_buffer; + if (start_buffer > 0) { + buffer = buffer + start_buffer; + } + int mixed = _mix_frames_vorbis(buffer, todo); + if (mixed < 0) { + return 0; + } + todo -= mixed; + frames_mixed += mixed; + start_buffer += mixed; + if (!have_packets_left) { + //end of file! + bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0; + if (vorbis_stream->loop && is_not_empty) { + //loop + + seek(vorbis_stream->loop_offset); + loops++; + // we still have buffer to fill, start from this element in the next iteration. + start_buffer = p_frames - todo; + } else { + frames_mixed_this_step = p_frames - todo; + for (int i = p_frames - todo; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + active = false; + todo = 0; + } + } + } + return frames_mixed_this_step; +} + +int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!ready, 0); + if (!have_samples_left) { + ogg_packet *packet = nullptr; + int err; + + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + have_packets_left = false; + WARN_PRINT("ran out of packets in stream"); + return -1; + } + + ERR_FAIL_COND_V_MSG((err = vorbis_synthesis(&block, packet)), 0, "Error during vorbis synthesis " + itos(err)); + ERR_FAIL_COND_V_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), 0, "Error during vorbis block processing " + itos(err)); + + have_packets_left = !packet->e_o_s; + } + + float **pcm; // Accessed with pcm[channel_idx][sample_idx]. + + int frames = vorbis_synthesis_pcmout(&dsp_state, &pcm); + if (frames > p_frames) { + frames = p_frames; + have_samples_left = true; + } else { + have_samples_left = false; + } + + if (info.channels > 1) { + for (int frame = 0; frame < frames; frame++) { + p_buffer[frame].l = pcm[0][frame]; + p_buffer[frame].r = pcm[0][frame]; + } + } else { + for (int frame = 0; frame < frames; frame++) { + p_buffer[frame].l = pcm[0][frame]; + p_buffer[frame].r = pcm[0][frame]; + } + } + vorbis_synthesis_read(&dsp_state, frames); + return frames; +} + +float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() { + return vorbis_data->get_sampling_rate(); +} + +bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() { + vorbis_info_init(&info); + info_is_allocated = true; + vorbis_comment_init(&comment); + comment_is_allocated = true; + + ERR_FAIL_COND_V(vorbis_data.is_null(), false); + vorbis_data_playback = vorbis_data->instance_playback(); + + ogg_packet *packet; + int err; + + for (int i = 0; i < 3; i++) { + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + WARN_PRINT("Not enough packets to parse header"); + return false; + } + + err = vorbis_synthesis_headerin(&info, &comment, packet); + ERR_FAIL_COND_V_MSG(err != 0, false, "Error parsing header"); + } + + err = vorbis_synthesis_init(&dsp_state, &info); + ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing dsp state"); + dsp_state_is_allocated = true; + + err = vorbis_block_init(&dsp_state, &block); + ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing block"); + block_is_allocated = true; + + ready = true; + + return true; +} + +void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) { + ERR_FAIL_COND(!ready); + active = true; + seek(p_from_pos); + loops = 0; + _begin_resample(); +} + +void AudioStreamPlaybackOGGVorbis::stop() { + active = false; +} + +bool AudioStreamPlaybackOGGVorbis::is_playing() const { + return active; +} + +int AudioStreamPlaybackOGGVorbis::get_loop_count() const { + return loops; +} + +float AudioStreamPlaybackOGGVorbis::get_playback_position() const { + return float(frames_mixed) / vorbis_data->get_sampling_rate(); +} + +void AudioStreamPlaybackOGGVorbis::seek(float p_time) { + ERR_FAIL_COND(!ready); + ERR_FAIL_COND(vorbis_stream.is_null()); + if (!active) { + return; + } + + vorbis_synthesis_restart(&dsp_state); + + if (p_time >= vorbis_stream->get_length()) { + p_time = 0; + } + frames_mixed = uint32_t(vorbis_data->get_sampling_rate() * p_time); + + const int64_t desired_sample = p_time * get_stream_sampling_rate(); + + if (!vorbis_data_playback->seek_page(desired_sample)) { + WARN_PRINT("seek failed"); + return; + } + + ogg_packet *packet; + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + WARN_PRINT_ONCE("seeking beyond limits"); + return; + } + + // The granule position of the page we're seeking through. + int64_t granule_pos = 0; + + int headers_remaining = 0; + int samples_in_page = 0; + int err; + while (true) { + if (vorbis_synthesis_idheader(packet)) { + headers_remaining = 3; + } + if (!headers_remaining) { + ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err)); + ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err)); + + int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr); + ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err)); + + samples_in_page += samples_out; + + } else { + headers_remaining--; + } + if (packet->granulepos != -1 && headers_remaining == 0) { + // This indicates the end of the page. + granule_pos = packet->granulepos; + break; + } + if (packet->e_o_s) { + break; + } + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + // We should get an e_o_s flag before this happens. + WARN_PRINT("Vorbis file ended without warning."); + break; + } + } + + int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample); + + if (samples_to_burn > samples_in_page) { + WARN_PRINT("Burning more samples than we have in this page. Check seek algorithm."); + } else if (samples_to_burn < 0) { + WARN_PRINT("Burning negative samples doesn't make sense. Check seek algorithm."); + } + + // Seek again, this time we'll burn a specific number of samples instead of all of them. + if (!vorbis_data_playback->seek_page(desired_sample)) { + WARN_PRINT("seek failed"); + return; + } + + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + WARN_PRINT_ONCE("seeking beyond limits"); + return; + } + vorbis_synthesis_restart(&dsp_state); + + while (true) { + if (vorbis_synthesis_idheader(packet)) { + headers_remaining = 3; + } + if (!headers_remaining) { + ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err)); + ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err)); + + int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr); + int read_samples = samples_to_burn > samples_out ? samples_out : samples_to_burn; + ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err)); + samples_to_burn -= read_samples; + + if (samples_to_burn <= 0) { + break; + } + } else { + headers_remaining--; + } + if (packet->granulepos != -1 && headers_remaining == 0) { + // This indicates the end of the page. + break; + } + if (packet->e_o_s) { + break; + } + if (!vorbis_data_playback->next_ogg_packet(&packet)) { + // We should get an e_o_s flag before this happens. + WARN_PRINT("Vorbis file ended without warning."); + break; + } + } +} + +AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { + if (block_is_allocated) { + vorbis_block_clear(&block); + } + if (dsp_state_is_allocated) { + vorbis_dsp_clear(&dsp_state); + } + if (comment_is_allocated) { + vorbis_comment_clear(&comment); + } + if (info_is_allocated) { + vorbis_info_clear(&info); + } +} + +Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() { + Ref<AudioStreamPlaybackOGGVorbis> ovs; + + ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr); + + ovs.instantiate(); + ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this); + ovs->vorbis_data = packet_sequence; + ovs->frames_mixed = 0; + ovs->active = false; + ovs->loops = 0; + if (ovs->_alloc_vorbis()) { + return ovs; + } + // Failed to allocate data structures. + return nullptr; +} + +String AudioStreamOGGVorbis::get_stream_name() const { + return ""; //return stream_name; +} + +void AudioStreamOGGVorbis::maybe_update_info() { + ERR_FAIL_COND(packet_sequence.is_null()); + + vorbis_info info; + vorbis_comment comment; + int err; + + vorbis_info_init(&info); + vorbis_comment_init(&comment); + + int packet_count = 0; + Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback(); + + for (int i = 0; i < 3; i++) { + ogg_packet *packet; + if (!packet_sequence_playback->next_ogg_packet(&packet)) { + WARN_PRINT("Failed to get header packet"); + break; + } + if (i == 0) { + packet->b_o_s = 1; + } + + if (i == 0) { + ERR_FAIL_COND(!vorbis_synthesis_idheader(packet)); + } + + err = vorbis_synthesis_headerin(&info, &comment, packet); + ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err)); + + packet_count++; + } + + packet_sequence->set_sampling_rate(info.rate); + + vorbis_comment_clear(&comment); + vorbis_info_clear(&info); +} + +void AudioStreamOGGVorbis::set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence) { + packet_sequence = p_packet_sequence; + if (packet_sequence.is_valid()) { + maybe_update_info(); + } +} + +Ref<OGGPacketSequence> AudioStreamOGGVorbis::get_packet_sequence() const { + return packet_sequence; +} + +void AudioStreamOGGVorbis::set_loop(bool p_enable) { + loop = p_enable; +} + +bool AudioStreamOGGVorbis::has_loop() const { + return loop; +} + +void AudioStreamOGGVorbis::set_loop_offset(float p_seconds) { + loop_offset = p_seconds; +} + +float AudioStreamOGGVorbis::get_loop_offset() const { + return loop_offset; +} + +float AudioStreamOGGVorbis::get_length() const { + ERR_FAIL_COND_V(packet_sequence.is_null(), 0); + return packet_sequence->get_length(); +} + +bool AudioStreamOGGVorbis::is_monophonic() const { + return false; +} + +void AudioStreamOGGVorbis::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOGGVorbis::set_packet_sequence); + ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOGGVorbis::get_packet_sequence); + + ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop); + ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop); + + ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset); + ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_sequence", "get_packet_sequence"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset"); +} + +AudioStreamOGGVorbis::AudioStreamOGGVorbis() {} + +AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {} diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 1311c4ce7a..59a1318a6b 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -28,29 +28,49 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef AUDIO_STREAM_STB_VORBIS_H -#define AUDIO_STREAM_STB_VORBIS_H +#ifndef AUDIO_STREAM_LIBVORBIS_H +#define AUDIO_STREAM_LIBVORBIS_H -#include "core/io/resource_loader.h" +#include "core/variant/variant.h" +#include "modules/ogg/ogg_packet_sequence.h" #include "servers/audio/audio_stream.h" - -#include "thirdparty/misc/stb_vorbis.h" +#include "thirdparty/libvorbis/vorbis/codec.h" class AudioStreamOGGVorbis; class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled); - stb_vorbis *ogg_stream = nullptr; - stb_vorbis_alloc ogg_alloc; uint32_t frames_mixed = 0; bool active = false; int loops = 0; + vorbis_info info; + vorbis_comment comment; + vorbis_dsp_state dsp_state; + vorbis_block block; + + bool info_is_allocated = false; + bool comment_is_allocated = false; + bool dsp_state_is_allocated = false; + bool block_is_allocated = false; + + bool ready = false; + + bool have_samples_left = false; + bool have_packets_left = false; + friend class AudioStreamOGGVorbis; + Ref<OGGPacketSequence> vorbis_data; + Ref<OGGPacketSequencePlayback> vorbis_data_playback; Ref<AudioStreamOGGVorbis> vorbis_stream; + int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames); + + // Allocates vorbis data structures. Returns true upon success, false on failure. + bool _alloc_vorbis(); + protected: virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; @@ -72,20 +92,20 @@ public: class AudioStreamOGGVorbis : public AudioStream { GDCLASS(AudioStreamOGGVorbis, AudioStream); OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged. - RES_BASE_EXTENSION("oggstr"); + RES_BASE_EXTENSION("oggvorbisstr"); friend class AudioStreamPlaybackOGGVorbis; - void *data = nullptr; - uint32_t data_len = 0; - - int decode_mem_size = 0; - float sample_rate = 1.0; int channels = 1; float length = 0.0; bool loop = false; float loop_offset = 0.0; - void clear_data(); + + // Performs a seek to the beginning of the stream, should not be called during playback! + // Also causes allocation and deallocation. + void maybe_update_info(); + + Ref<OGGPacketSequence> packet_sequence; protected: static void _bind_methods(); @@ -100,8 +120,8 @@ public: virtual Ref<AudioStreamPlayback> instance_playback() override; virtual String get_stream_name() const override; - void set_data(const Vector<uint8_t> &p_data); - Vector<uint8_t> get_data() const; + void set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence); + Ref<OGGPacketSequence> get_packet_sequence() const; virtual float get_length() const override; //if supported, otherwise return 0 @@ -111,4 +131,4 @@ public: virtual ~AudioStreamOGGVorbis(); }; -#endif +#endif // AUDIO_STREAM_LIBVORBIS_H diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py index 8a384e3066..978eccb29f 100644 --- a/modules/vorbis/config.py +++ b/modules/vorbis/config.py @@ -4,3 +4,14 @@ def can_build(env, platform): def configure(env): pass + + +def get_doc_classes(): + return [ + "AudioStreamOGGVorbis", + "AudioStreamPlaybackOGGVorbis", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml index 94fdff5d43..a680a2f999 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -1,25 +1,23 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="AudioStreamOGGVorbis" inherits="AudioStream" version="4.0"> <brief_description> - OGG Vorbis audio stream driver. </brief_description> <description> - OGG Vorbis audio stream driver. </description> <tutorials> </tutorials> <methods> </methods> <members> - <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()"> - Contains the audio data in bytes. - </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> If [code]true[/code], the stream will automatically loop when it reaches the end. </member> <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0"> Time in seconds at which the stream starts after being looped. </member> + <member name="packet_sequence" type="OGGPacketSequence" setter="set_packet_sequence" getter="get_packet_sequence"> + Contains the raw OGG data for this stream. + </member> </members> <constants> </constants> diff --git a/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml new file mode 100644 index 0000000000..3120f2a9e6 --- /dev/null +++ b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioStreamPlaybackOGGVorbis" inherits="AudioStreamPlaybackResampled" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index d3e77ea629..de3f41afdd 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -30,8 +30,19 @@ #include "register_types.h" -// Dummy module as libvorbis is needed by other modules (theora ...) +#include "audio_stream_ogg_vorbis.h" +#include "resource_importer_ogg_vorbis.h" -void register_vorbis_types() {} +void register_vorbis_types() { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + Ref<ResourceImporterOGGVorbis> ogg_vorbis_importer; + ogg_vorbis_importer.instantiate(); + ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer); + } +#endif + GDREGISTER_CLASS(AudioStreamOGGVorbis); + GDREGISTER_CLASS(AudioStreamPlaybackOGGVorbis); +} void unregister_vorbis_types() {} diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 85de698efd..33ee6cf359 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -30,16 +30,19 @@ #include "resource_importer_ogg_vorbis.h" +#include "audio_stream_ogg_vorbis.h" #include "core/io/file_access.h" #include "core/io/resource_saver.h" #include "scene/resources/texture.h" +#include "thirdparty/libogg/ogg/ogg.h" +#include "thirdparty/libvorbis/vorbis/codec.h" String ResourceImporterOGGVorbis::get_importer_name() const { - return "ogg_vorbis"; + return "oggvorbisstr"; } String ResourceImporterOGGVorbis::get_visible_name() const { - return "OGGVorbis"; + return "oggvorbisstr"; } void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const { @@ -47,7 +50,7 @@ void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extens } String ResourceImporterOGGVorbis::get_save_extension() const { - return "oggstr"; + return "oggvorbisstr"; } String ResourceImporterOGGVorbis::get_resource_type() const { @@ -81,23 +84,106 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin uint64_t len = f->get_length(); - Vector<uint8_t> data; - data.resize(len); - uint8_t *w = data.ptrw(); + Vector<uint8_t> file_data; + file_data.resize(len); + uint8_t *w = file_data.ptrw(); f->get_buffer(w, len); memdelete(f); - Ref<AudioStreamOGGVorbis> ogg_stream; - ogg_stream.instantiate(); - - ogg_stream->set_data(data); - ERR_FAIL_COND_V(!ogg_stream->get_data().size(), ERR_FILE_CORRUPT); - ogg_stream->set_loop(loop); - ogg_stream->set_loop_offset(loop_offset); - - return ResourceSaver::save(p_save_path + ".oggstr", ogg_stream); + Ref<AudioStreamOGGVorbis> ogg_vorbis_stream; + ogg_vorbis_stream.instantiate(); + + Ref<OGGPacketSequence> ogg_packet_sequence; + ogg_packet_sequence.instantiate(); + + ogg_stream_state stream_state; + ogg_sync_state sync_state; + ogg_page page; + ogg_packet packet; + bool initialized_stream = false; + + ogg_sync_init(&sync_state); + int err; + size_t cursor = 0; + size_t packet_count = 0; + bool done = false; + while (!done) { + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + while (ogg_sync_pageout(&sync_state, &page) != 1) { + if (cursor >= len) { + done = true; + break; + } + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V(cursor > len, Error::ERR_INVALID_DATA); + size_t copy_size = len - cursor; + if (copy_size > OGG_SYNC_BUFFER_SIZE) { + copy_size = OGG_SYNC_BUFFER_SIZE; + } + memcpy(sync_buf, &file_data[cursor], copy_size); + ogg_sync_wrote(&sync_state, copy_size); + cursor += copy_size; + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + } + if (done) { + break; + } + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + + // Have a page now. + if (!initialized_stream) { + ogg_stream_init(&stream_state, ogg_page_serialno(&page)); + ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err)); + initialized_stream = true; + } + ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err)); + ogg_stream_pagein(&stream_state, &page); + ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err)); + int desync_iters = 0; + + Vector<Vector<uint8_t>> packet_data; + int64_t granule_pos = 0; + + while (true) { + err = ogg_stream_packetout(&stream_state, &packet); + if (err == -1) { + // According to the docs this is usually recoverable, but don't sit here spinning forever. + desync_iters++; + ERR_FAIL_COND_V_MSG(desync_iters > 100, Error::ERR_INVALID_DATA, "Packet sync issue during ogg import"); + continue; + } else if (err == 0) { + // Not enough data to fully reconstruct a packet. Go on to the next page. + break; + } + if (packet_count == 0 && vorbis_synthesis_idheader(&packet) == 0) { + WARN_PRINT("Found a non-vorbis-header packet in a header position"); + // Clearly this logical stream is not a vorbis stream, so destroy it and try again with the next page. + ogg_stream_destroy(&stream_state); + initialized_stream = false; + break; + } + granule_pos = packet.granulepos; + + PackedByteArray data; + data.resize(packet.bytes); + memcpy(data.ptrw(), packet.packet, packet.bytes); + packet_data.push_back(data); + packet_count++; + } + if (initialized_stream) { + ogg_packet_sequence->push_page(granule_pos, packet_data); + } + } + + ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence); + ogg_vorbis_stream->set_loop(loop); + ogg_vorbis_stream->set_loop_offset(loop_offset); + + return ResourceSaver::save(p_save_path + ".oggvorbisstr", ogg_vorbis_stream); } ResourceImporterOGGVorbis::ResourceImporterOGGVorbis() { diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h index 60fe3381fb..acdc1a3d38 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h +++ b/modules/vorbis/resource_importer_ogg_vorbis.h @@ -28,25 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RESOURCEIMPORTEROGGVORBIS_H -#define RESOURCEIMPORTEROGGVORBIS_H +#ifndef RESOURCE_IMPORTER_OGG_VORBIS_H +#define RESOURCE_IMPORTER_OGG_VORBIS_H -#include "audio_stream_ogg_vorbis.h" #include "core/io/resource_importer.h" class ResourceImporterOGGVorbis : public ResourceImporter { GDCLASS(ResourceImporterOGGVorbis, ResourceImporter); + enum { + OGG_SYNC_BUFFER_SIZE = 8192, + }; + +private: + // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0; + public: - virtual String get_importer_name() const override; - virtual String get_visible_name() const override; virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual String get_save_extension() const override; virtual String get_resource_type() const override; - + virtual String get_importer_name() const override; + virtual String get_visible_name() const override; virtual int get_preset_count() const override; virtual String get_preset_name(int p_idx) const override; - virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override; @@ -55,4 +59,4 @@ public: ResourceImporterOGGVorbis(); }; -#endif // RESOURCEIMPORTEROGGVORBIS_H +#endif // RESOURCE_IMPORTER_OGG_VORBIS_H diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor_debugger_server_websocket.cpp index 2e61cbfc08..d248433d82 100644 --- a/modules/websocket/editor_debugger_server_websocket.cpp +++ b/modules/websocket/editor_debugger_server_websocket.cpp @@ -48,11 +48,19 @@ void EditorDebuggerServerWebSocket::poll() { server->poll(); } -Error EditorDebuggerServerWebSocket::start() { - int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); +Error EditorDebuggerServerWebSocket::start(const String &p_uri) { + int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); + String bind_host = EditorSettings::get_singleton()->get("network/debug/remote_host"); + if (!p_uri.is_empty() && p_uri != "ws://") { + String scheme, path; + Error err = p_uri.parse_url(scheme, bind_host, bind_port, path); + ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER); + } + server->set_bind_ip(bind_host); Vector<String> compatible_protocols; compatible_protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer. - return server->listen(remote_port, compatible_protocols); + return server->listen(bind_port, compatible_protocols); } void EditorDebuggerServerWebSocket::stop() { diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h index d9543bb647..14ab0109b2 100644 --- a/modules/websocket/editor_debugger_server_websocket.h +++ b/modules/websocket/editor_debugger_server_websocket.h @@ -48,7 +48,7 @@ public: void _peer_disconnected(int p_peer, bool p_was_clean); void poll() override; - Error start() override; + Error start(const String &p_uri) override; void stop() override; bool is_active() const override; bool is_connection_available() const override; |