diff options
Diffstat (limited to 'modules')
45 files changed, 2260 insertions, 2261 deletions
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index ee66097ffd..0330ab4604 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -113,7 +113,7 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f std::string s_path(w_path.begin(), w_path.end()); importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); // Cannot remove pivot points because the static mesh will be in the wrong place - importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); int32_t max_bone_weights = 4; //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) { // const int eight_bones = 8; @@ -282,673 +282,438 @@ T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, co ERR_FAIL_V(p_values[0]); } -Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) { - ERR_FAIL_COND_V(scene == NULL, NULL); - Spatial *root = memnew(Spatial); - AnimationPlayer *ap = NULL; - if (p_flags & IMPORT_ANIMATION) { - ap = memnew(AnimationPlayer); - root->add_child(ap); - ap->set_owner(root); - ap->set_name(TTR("AnimationPlayer")); - } - Set<String> bone_names; - Set<String> light_names; - Set<String> camera_names; - real_t factor = 1.0f; - String ext = p_path.get_file().get_extension().to_lower(); - if ((ext == "fbx")) { - if (scene->mMetaData != NULL) { - scene->mMetaData->Get("UnitScaleFactor", factor); - factor = factor * 0.01f; +void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms) { + + Transform mesh_offset = _get_global_assimp_node_transform(p_assimp_node); + //mesh_offset.basis = Basis(); + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + const aiMesh *mesh = state.assimp_scene->mMeshes[i]; + int owned_by = -1; + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + + if (ownership.has(name)) { + owned_by = ownership[name]; + break; + } } - } - for (size_t l = 0; l < scene->mNumLights; l++) { - Light *light = NULL; - aiLight *ai_light = scene->mLights[l]; - ERR_CONTINUE(ai_light == NULL); - if (ai_light->mType == aiLightSource_DIRECTIONAL) { - light = memnew(DirectionalLight); - Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); - dir.normalize(); - Transform xform; - Quat quat; - quat.set_euler(dir); - Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); - pos = factor * pos; - xform.origin = pos; - light->set_transform(xform); - } else if (ai_light->mType == aiLightSource_POINT) { - light = memnew(OmniLight); - Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); - Transform xform; - xform.origin = pos; - pos = factor * pos; - light->set_transform(xform); - // No idea for energy - light->set_param(Light::PARAM_ATTENUATION, 0.0f); - } else if (ai_light->mType == aiLightSource_SPOT) { - light = memnew(SpotLight); - Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); - pos = factor * pos; - Transform xform; - xform.origin = pos; - Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); - dir.normalize(); - Quat quat; - quat.set_euler(dir); - xform.basis = quat; - light->set_transform(xform); - // No idea for energy - light->set_param(Light::PARAM_ATTENUATION, 0.0f); + + if (owned_by == -1) { //no owned, create new unique id + owned_by = 1; + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + owned_by = MAX(E->get() + 1, owned_by); + } } - ERR_CONTINUE(light == NULL); - light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b)); - root->add_child(light); - light->set_name(_ai_string_to_string(ai_light->mName)); - light->set_owner(root); - light_names.insert(_ai_string_to_string(scene->mLights[l]->mName)); - } - for (size_t c = 0; c < scene->mNumCameras; c++) { - aiCamera *ai_camera = scene->mCameras[c]; - Camera *camera = memnew(Camera); - float near = ai_camera->mClipPlaneNear; - if (Math::is_equal_approx(near, 0.0f)) { - near = 0.1f; + + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + ownership[name] = owned_by; + //store the actuall full path for the bone transform + //when skeleton finds it's place in the tree, it will be restored + bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix); } - camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar); - Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z); + } - Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized(); - Quat quat; - quat.set_euler(look_at); - Transform xform; - xform.basis = quat; - xform.set_origin(pos); - root->add_child(camera); - camera->set_transform(xform); - camera->set_name(_ai_string_to_string(ai_camera->mName)); - camera->set_owner(root); - camera_names.insert(_ai_string_to_string(scene->mCameras[c]->mName)); - } - Map<Skeleton *, MeshInstance *> skeletons; - Map<String, Transform> bone_rests; - Vector<MeshInstance *> meshes; - int32_t mesh_count = 0; - Skeleton *s = memnew(Skeleton); - Set<String> removed_bones; - Map<String, Map<uint32_t, String> > path_morph_mesh_names; - _generate_node(p_path, scene, scene->mRootNode, root, root, bone_names, light_names, camera_names, skeletons, bone_rests, meshes, mesh_count, s, p_max_bone_weights, removed_bones, path_morph_mesh_names); - for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) { - E->key()->localize_rests(); - } - Set<String> removed_nodes; - Set<Node *> keep_nodes; - _keep_node(p_path, root, root, keep_nodes); - _fill_kept_node(keep_nodes); - _filter_node(p_path, root, root, keep_nodes, removed_nodes); - if (p_flags & IMPORT_ANIMATION) { - for (size_t i = 0; i < scene->mNumAnimations; i++) { - _import_animation(p_path, meshes, scene, ap, i, p_bake_fps, skeletons, removed_nodes, removed_bones, path_morph_mesh_names); - } - List<StringName> animation_names; - ap->get_animation_list(&animation_names); - if (animation_names.empty()) { - root->remove_child(ap); - memdelete(ap); - } - } - return root; + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_bone_groups(state, p_assimp_node->mChildren[i], ownership, bind_xforms); + } } -void EditorSceneImporterAssimp::_fill_kept_node(Set<Node *> &keep_nodes) { - for (Set<Node *>::Element *E = keep_nodes.front(); E; E = E->next()) { - Node *node = E->get(); - while (node != NULL) { - if (keep_nodes.has(node) == false) { - keep_nodes.insert(node); +void EditorSceneImporterAssimp::_fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms) { + + String name = _assimp_get_string(p_assimp_node->mName); + if (name == String()) { + name = "AuxiliaryBone" + itos(holecount++); + } + + Transform pose = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (!ownership.has(name)) { + //not a bone, it's a hole + Vector<SkeletonHole> holes = p_holes; + SkeletonHole hole; //add a new one + hole.name = name; + hole.pose = pose; + hole.node = p_assimp_node; + hole.parent = p_parent_name; + holes.push_back(hole); + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, holes, bind_xforms); + } + + return; + } else if (ownership[name] != p_skeleton_id) { + //oh, it's from another skeleton? fine.. reparent all bones to this skeleton. + int prev_owner = ownership[name]; + ERR_EXPLAIN("A previous skeleton exists for bone '" + name + "', this type of skeleton layout is unsupported."); + ERR_FAIL_COND(skeleton_map.has(prev_owner)); + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + if (E->get() == prev_owner) { + E->get() = p_skeleton_id; } - node = node->get_parent(); } } -} -String EditorSceneImporterAssimp::_find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root) { - for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) { - if (meshes.has(E->get())) { - String name = meshes[E->get()]; - if (name != "") { - return name; - } + //valid bone, first fill holes if needed + for (int i = 0; i < p_holes.size(); i++) { + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(p_holes[i].name); + int parent_idx = p_skeleton->find_bone(p_holes[i].parent); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); } + + Transform pose_transform = _get_global_assimp_node_transform(p_holes[i].node); + p_skeleton->set_bone_rest(bone_idx, pose_transform); + + state.bone_owners[p_holes[i].name] = skeleton_map[p_skeleton_id]; + } + + //finally fill bone + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(name); + int parent_idx = p_skeleton->find_bone(p_parent_name); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); + } + //p_skeleton->set_bone_pose(bone_idx, pose); + if (bind_xforms.has(name)) { + //for now this is the full path to the bone in rest pose + //when skeleton finds it's place in the tree, it will get fixed + p_skeleton->set_bone_rest(bone_idx, bind_xforms[name]); + } + state.bone_owners[name] = skeleton_map[p_skeleton_id]; + //go to children + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, Vector<SkeletonHole>(), bind_xforms); } - return ""; } -void EditorSceneImporterAssimp::_set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node) { - for (int32_t j = 0; j < s->get_bone_count(); j++) { - String bone_name = s->get_bone_name(j); - const aiNode *ai_bone_node = _ai_find_node(p_node, bone_name); - if (ai_bone_node == NULL) { - continue; - } - ai_bone_node = ai_bone_node->mParent; - while (ai_bone_node != NULL) { - int32_t node_parent_index = -1; - String parent_bone_name = _ai_string_to_string(ai_bone_node->mName); - node_parent_index = s->find_bone(parent_bone_name); - if (node_parent_index != -1) { - s->set_bone_parent(j, node_parent_index); - break; +void EditorSceneImporterAssimp::_generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms) { + + //find skeletons at this level, there may be multiple root nodes for each + Map<int, List<aiNode *> > skeletons_found; + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + int skeleton = ownership[name]; + if (!skeletons_found.has(skeleton)) { + skeletons_found[skeleton] = List<aiNode *>(); } - ai_bone_node = ai_bone_node->mParent; + skeletons_found[skeleton].push_back(p_assimp_node->mChildren[i]); } } + + //go via the potential skeletons found and generate the actual skeleton + for (Map<int, List<aiNode *> >::Element *E = skeletons_found.front(); E; E = E->next()) { + ERR_CONTINUE(skeleton_map.has(E->key())); //skeleton already exists? this can't be.. skip + Skeleton *skeleton = memnew(Skeleton); + //this the only way to reliably use multiple meshes with one skeleton, at the cost of less precision + skeleton->set_use_bones_in_world_transform(true); + skeleton_map[E->key()] = state.skeletons.size(); + state.skeletons.push_back(skeleton); + int holecount = 1; + //fill the bones and their relationships + for (List<aiNode *>::Element *F = E->get().front(); F; F = F->next()) { + _fill_node_relationships(state, F->get(), ownership, skeleton_map, E->key(), skeleton, "", holecount, Vector<SkeletonHole>(), bind_xforms); + } + } + + //go to the children + for (uint32_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + continue; //a bone, so don't bother with this + } + _generate_skeletons(state, p_assimp_node->mChildren[i], ownership, skeleton_map, bind_xforms); + } } -void EditorSceneImporterAssimp::_insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path) { +Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) { + ERR_FAIL_COND_V(scene == NULL, NULL); - if (track->mNumRotationKeys || track->mNumPositionKeys || track->mNumScalingKeys) { - //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM); - animation->track_set_path(track_idx, node_path); - //first determine animation length + ImportState state; + state.path = p_path; + state.assimp_scene = scene; + state.max_bone_weights = p_max_bone_weights; + state.root = memnew(Spatial); + state.fbx = false; + state.animation_player = NULL; - for (size_t i = 0; i < track->mNumRotationKeys; i++) { - length = MAX(length, track->mRotationKeys[i].mTime / ticks_per_second); - } - for (size_t i = 0; i < track->mNumPositionKeys; i++) { - length = MAX(length, track->mPositionKeys[i].mTime / ticks_per_second); - } - for (size_t i = 0; i < track->mNumScalingKeys; i++) { - length = MAX(length, track->mScalingKeys[i].mTime / ticks_per_second); + real_t scale_factor = 1.0f; + { + //handle scale + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + if (scene->mMetaData != NULL) { + float factor = 1.0; + scene->mMetaData->Get("UnitScaleFactor", factor); + scale_factor = factor * 0.01f; + } + state.fbx = true; } + } - float increment = 1.0 / float(p_bake_fps); - float time = 0.0; + state.root->set_scale(Vector3(scale_factor, scale_factor, scale_factor)); - Vector3 base_pos; - Quat base_rot; - Vector3 base_scale = Vector3(1, 1, 1); + //fill light map cache + for (size_t l = 0; l < scene->mNumLights; l++) { - if (track->mNumRotationKeys != 0) { - aiQuatKey key = track->mRotationKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - real_t w = key.mValue.w; - Quat q(x, y, z, w); - q = q.normalized(); - base_rot = q; - } + aiLight *ai_light = scene->mLights[l]; + ERR_CONTINUE(ai_light == NULL); + state.light_cache[_assimp_get_string(ai_light->mName)] = l; + } - if (track->mNumPositionKeys != 0) { - aiVectorKey key = track->mPositionKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - base_pos = Vector3(x, y, z); + //fill camera cache + for (size_t c = 0; c < scene->mNumCameras; c++) { + aiCamera *ai_camera = scene->mCameras[c]; + ERR_CONTINUE(ai_camera == NULL); + state.camera_cache[_assimp_get_string(ai_camera->mName)] = c; + } + + if (scene->mRootNode) { + Map<String, Transform> bind_xforms; //temporary map to store bind transforms + //guess the skeletons, since assimp does not really support them directly + Map<String, int> ownership; //bone names to groups + //fill this map with bone names and which group where they detected to, going mesh by mesh + _generate_bone_groups(state, state.assimp_scene->mRootNode, ownership, bind_xforms); + Map<int, int> skeleton_map; //maps previously created groups to actual skeletons + //generates the skeletons when bones are found in the hierarchy, and follows them (including gaps/holes). + _generate_skeletons(state, state.assimp_scene->mRootNode, ownership, skeleton_map, bind_xforms); + + //generate nodes + for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) { + _generate_node(state, scene->mRootNode->mChildren[i], state.root); } - if (track->mNumScalingKeys != 0) { - aiVectorKey key = track->mScalingKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - base_scale = Vector3(x, y, z); + //assign skeletons to nodes + + for (Map<MeshInstance *, Skeleton *>::Element *E = state.mesh_skeletons.front(); E; E = E->next()) { + MeshInstance *mesh = E->key(); + Skeleton *skeleton = E->get(); + NodePath skeleton_path = mesh->get_path_to(skeleton); + mesh->set_skeleton_path(skeleton_path); } + } - bool last = false; + if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) { - Vector<Vector3> pos_values; - Vector<float> pos_times; - Vector<Vector3> scale_values; - Vector<float> scale_times; - Vector<Quat> rot_values; - Vector<float> rot_times; + state.animation_player = memnew(AnimationPlayer); + state.root->add_child(state.animation_player); + state.animation_player->set_owner(state.root); - for (size_t p = 0; p < track->mNumPositionKeys; p++) { - aiVector3D pos = track->mPositionKeys[p].mValue; - pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); - pos_times.push_back(track->mPositionKeys[p].mTime / ticks_per_second); + for (uint32_t i = 0; i < scene->mNumAnimations; i++) { + _import_animation(state, i, p_bake_fps); } + } + + return state.root; +} + +void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name) { + + const aiNodeAnim *assimp_track = assimp_anim->mChannels[p_track]; + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, p_path); + //first determine animation length - for (size_t r = 0; r < track->mNumRotationKeys; r++) { - aiQuaternion quat = track->mRotationKeys[r].mValue; - rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); - rot_times.push_back(track->mRotationKeys[r].mTime / ticks_per_second); + float increment = 1.0 / float(p_bake_fps); + float time = 0.0; + + bool last = false; + + int skeleton_bone = -1; + + if (p_skeleton) { + skeleton_bone = p_skeleton->find_bone(p_name); + } + + Vector<Vector3> pos_values; + Vector<float> pos_times; + Vector<Vector3> scale_values; + Vector<float> scale_times; + Vector<Quat> rot_values; + Vector<float> rot_times; + + for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) { + aiVector3D pos = assimp_track->mPositionKeys[p].mValue; + pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); + pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second); + } + + for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) { + aiQuaternion quat = assimp_track->mRotationKeys[r].mValue; + rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); + rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second); + } + + for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) { + aiVector3D scale = assimp_track->mScalingKeys[sc].mValue; + scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); + scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second); + } + while (true) { + Vector3 pos; + Quat rot; + Vector3 scale(1, 1, 1); + + if (pos_values.size()) { + pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); } - for (size_t sc = 0; sc < track->mNumScalingKeys; sc++) { - aiVector3D scale = track->mScalingKeys[sc].mValue; - scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); - scale_times.push_back(track->mScalingKeys[sc].mTime / ticks_per_second); + if (rot_values.size()) { + rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); } - while (true) { - Vector3 pos = base_pos; - Quat rot = base_rot; - Vector3 scale = base_scale; - if (pos_values.size()) { - pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); - } + if (scale_values.size()) { + scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); + } - if (rot_values.size()) { - rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); - } + if (skeleton_bone >= 0) { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; - if (scale_values.size()) { - scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); - } + Transform rest_xform = p_skeleton->get_bone_rest(skeleton_bone); + xform = rest_xform.affine_inverse() * xform; + rot = xform.basis.get_rotation_quat(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } - if (sk != NULL && sk->find_bone(node_name) != -1) { - Transform xform; - xform.basis.set_quat_scale(rot, scale); - xform.origin = pos; - - int bone = sk->find_bone(node_name); - Transform rest_xform = sk->get_bone_rest(bone); - xform = rest_xform.affine_inverse() * xform; - rot = xform.basis.get_rotation_quat(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } - { - Transform xform; - xform.basis.set_quat_scale(rot, scale); - xform.origin = pos; - Transform anim_xform; - String ext = p_path.get_file().get_extension().to_lower(); - if (ext == "fbx") { - real_t factor = 1.0f; - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("UnitScaleFactor", factor); - } - anim_xform = anim_xform.scaled(Vector3(factor, factor, factor)); - } - xform = anim_xform * xform; - rot = xform.basis.get_rotation_quat(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } - rot.normalize(); + rot.normalize(); - animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); - if (last) { - break; - } - time += increment; - if (time >= length) { - last = true; - time = length; - } + if (last) { //done this way so a key is always inserted past the end (for proper interpolation) + break; + } + time += increment; + if (time >= animation->get_length()) { + last = true; } } } -void EditorSceneImporterAssimp::_import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names) { - String name = "Animation"; - aiAnimation const *anim = NULL; - if (p_index != -1) { - anim = p_scene->mAnimations[p_index]; - if (anim->mName.length > 0) { - name = _ai_anim_string_to_string(anim->mName); - } +void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) { + + ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations); + + const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index]; + String name = _assimp_anim_string_to_string(anim->mName); + if (name == String()) { + name = "Animation " + itos(p_animation_index + 1); } - Ref<Animation> animation; - animation.instance(); - float length = 0.0f; - animation->set_name(name); - float ticks_per_second = p_scene->mAnimations[p_index]->mTicksPerSecond; + float ticks_per_second = anim->mTicksPerSecond; - if (p_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) { + if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) { int32_t time_mode = 0; - p_scene->mMetaData->Get("TimeMode", time_mode); - ticks_per_second = _get_fbx_fps(time_mode, p_scene); + state.assimp_scene->mMetaData->Get("TimeMode", time_mode); + ticks_per_second = _get_fbx_fps(time_mode, state.assimp_scene); } - if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) { - ticks_per_second = 1000.0f; - } + //? + //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) { + // ticks_per_second = 1000.0f; + //} if (Math::is_equal_approx(ticks_per_second, 0.0f)) { ticks_per_second = 25.0f; } - length = anim->mDuration / ticks_per_second; - if (anim) { - Map<String, Vector<const aiNodeAnim *> > node_tracks; - for (size_t i = 0; i < anim->mNumChannels; i++) { - const aiNodeAnim *track = anim->mChannels[i]; - String node_name = _ai_string_to_string(track->mNodeName); - NodePath node_path = node_name; - bool is_bone = false; - if (node_name.split(ASSIMP_FBX_KEY).size() > 1) { - String p_track_type = node_name.split(ASSIMP_FBX_KEY)[1]; - if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") { - continue; - } - } - for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) { - Skeleton *sk = E->key(); - const String path = ap->get_owner()->get_path_to(sk); - if (path.empty()) { - continue; - } - if (sk->find_bone(node_name) == -1) { - continue; - } - node_path = path + ":" + node_name; - ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); - _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, sk, track, node_name, node_path); - is_bone = true; - } - if (is_bone) { - continue; - } - Node *node = ap->get_owner()->find_node(node_name); - if (node == NULL) { - continue; - } - if (p_removed_nodes.has(node_name)) { - continue; - } - const String path = ap->get_owner()->get_path_to(node); - if (path.empty()) { - print_verbose("Can't animate path"); - continue; - } - node_path = path; - if (ap->get_owner()->has_node(node_path) == false) { - continue; - } - _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, NULL, track, node_name, node_path); - } - for (size_t i = 0; i < anim->mNumChannels; i++) { - const aiNodeAnim *track = anim->mChannels[i]; - String node_name = _ai_string_to_string(track->mNodeName); - Vector<String> split_name = node_name.split(ASSIMP_FBX_KEY); - String bare_name = split_name[0]; - Node *node = ap->get_owner()->find_node(bare_name); - if (node != NULL && split_name.size() > 1) { - Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.find(bare_name); - Vector<const aiNodeAnim *> ai_tracks; - if (E) { - ai_tracks = E->get(); - ai_tracks.push_back(anim->mChannels[i]); - } else { - ai_tracks.push_back(anim->mChannels[i]); - } - node_tracks.insert(bare_name, ai_tracks); - } - } - for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) { - Skeleton *sk = E->key(); - Map<String, Vector<const aiNodeAnim *> > anim_tracks; - for (int32_t i = 0; i < sk->get_bone_count(); i++) { - String _bone_name = sk->get_bone_name(i); - Vector<const aiNodeAnim *> ai_tracks; + Ref<Animation> animation; + animation.instance(); + animation->set_name(name); + animation->set_length(anim->mDuration / ticks_per_second); - if (sk->find_bone(_bone_name) == -1) { - continue; - } - for (size_t j = 0; j < anim->mNumChannels; j++) { - if (_ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY).size() == 1) { - continue; - } - String track_name = _ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY)[0]; - if (track_name != _bone_name) { - continue; - } - if (sk->find_bone(_bone_name) == -1) { - continue; - } - ai_tracks.push_back(anim->mChannels[j]); - } - if (ai_tracks.size() == 0) { - continue; - } - anim_tracks.insert(_bone_name, ai_tracks); - } - for (Map<String, Vector<const aiNodeAnim *> >::Element *F = anim_tracks.front(); F; F = F->next()) { - _insert_pivot_anim_track(p_meshes, F->key(), F->get(), ap, sk, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene); - } - } - for (Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.front(); E; E = E->next()) { - if (p_removed_nodes.has(E->key())) { - continue; - } - if (removed_bones.find(E->key())) { + //regular tracks + + for (size_t i = 0; i < anim->mNumChannels; i++) { + const aiNodeAnim *track = anim->mChannels[i]; + String node_name = _assimp_get_string(track->mNodeName); + /* + if (node_name.find(ASSIMP_FBX_KEY) != -1) { + String p_track_type = node_name.get_slice(ASSIMP_FBX_KEY, 1); + if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") { continue; } - _insert_pivot_anim_track(p_meshes, E->key(), E->get(), ap, NULL, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene); } - for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { - const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; - const String prop_name = _ai_string_to_string(anim_mesh->mName); - const String mesh_name = prop_name.split("*")[0]; - if (p_removed_nodes.has(mesh_name)) { - continue; - } - ERR_CONTINUE(prop_name.split("*").size() != 2); - const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(ap->get_owner()->find_node(mesh_name)); - ERR_CONTINUE(mesh_instance == NULL); - if (ap->get_owner()->find_node(mesh_instance->get_name()) == NULL) { - print_verbose("Can't find mesh in scene: " + mesh_instance->get_name()); - continue; - } - const String path = ap->get_owner()->get_path_to(mesh_instance); - if (path.empty()) { - print_verbose("Can't find mesh in scene"); - continue; - } - Ref<Mesh> mesh = mesh_instance->get_mesh(); - ERR_CONTINUE(mesh.is_null()); - const Map<String, Map<uint32_t, String> >::Element *E = p_path_morph_mesh_names.find(mesh_name); - ERR_CONTINUE(E == NULL); - for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { - for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { - const Map<uint32_t, String>::Element *F = E->get().find(anim_mesh->mKeys[k].mValues[j]); - ERR_CONTINUE(F == NULL); - const String prop = "blend_shapes/" + F->get(); - const NodePath node_path = String(path) + ":" + prop; - ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); - int32_t blend_track_idx = -1; - if (animation->find_track(node_path) == -1) { - blend_track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_VALUE); - animation->track_set_interpolation_type(blend_track_idx, Animation::INTERPOLATION_LINEAR); - animation->track_set_path(blend_track_idx, node_path); - } else { - blend_track_idx = animation->find_track(node_path); - } - float t = anim_mesh->mKeys[k].mTime / ticks_per_second; - float w = anim_mesh->mKeys[k].mWeights[j]; - animation->track_insert_key(blend_track_idx, t, w); - } - } +*/ + if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) { + continue; //do not bother } - } - animation->set_length(length); - if (animation->get_track_count()) { - ap->add_animation(name, animation); - } -} -void EditorSceneImporterAssimp::_insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene) { - NodePath node_path; - if (sk != NULL) { - const String path = ap->get_owner()->get_path_to(sk); - if (path.empty()) { - return; - } - if (sk->find_bone(p_node_name) == -1) { - return; - } - node_path = path + ":" + p_node_name; - } else { - Node *node = ap->get_owner()->find_node(p_node_name); - if (node == NULL) { - return; - } - const String path = ap->get_owner()->get_path_to(node); - node_path = path; - } - if (node_path.is_empty()) { - return; - } + bool is_bone = state.bone_owners.has(node_name); + NodePath node_path; + Skeleton *skeleton = NULL; - Vector<Vector3> pos_values; - Vector<float> pos_times; - Vector<Vector3> scale_values; - Vector<float> scale_times; - Vector<Quat> rot_values; - Vector<float> rot_times; - Vector3 base_pos; - Quat base_rot; - Vector3 base_scale = Vector3(1, 1, 1); - bool is_translation = false; - bool is_rotation = false; - bool is_scaling = false; - for (int32_t k = 0; k < F.size(); k++) { - String p_track_type = _ai_string_to_string(F[k]->mNodeName).split(ASSIMP_FBX_KEY)[1]; - if (p_track_type == "_Translation") { - is_translation = is_translation || true; - } else if (p_track_type == "_Rotation") { - is_rotation = is_rotation || true; - } else if (p_track_type == "_Scaling") { - is_scaling = is_scaling || true; + if (is_bone) { + skeleton = state.skeletons[state.bone_owners[node_name]]; + String path = state.root->get_path_to(skeleton); + path += ":" + node_name; + node_path = path; } else { - continue; + + ERR_CONTINUE(!state.node_map.has(node_name)); + Node *node = state.node_map[node_name]; + node_path = state.root->get_path_to(node); } - ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); - if (F[k]->mNumRotationKeys || F[k]->mNumPositionKeys || F[k]->mNumScalingKeys) { + _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name); + } - if (is_rotation) { - for (size_t i = 0; i < F[k]->mNumRotationKeys; i++) { - length = MAX(length, F[k]->mRotationKeys[i].mTime / ticks_per_second); - } - } - if (is_translation) { - for (size_t i = 0; i < F[k]->mNumPositionKeys; i++) { - length = MAX(length, F[k]->mPositionKeys[i].mTime / ticks_per_second); - } - } - if (is_scaling) { - for (size_t i = 0; i < F[k]->mNumScalingKeys; i++) { - length = MAX(length, F[k]->mScalingKeys[i].mTime / ticks_per_second); - } - } + //blend shape tracks - if (is_rotation == false && is_translation == false && is_scaling == false) { - return; - } + for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { - if (is_rotation) { - if (F[k]->mNumRotationKeys != 0) { - aiQuatKey key = F[k]->mRotationKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - real_t w = key.mValue.w; - Quat q(x, y, z, w); - q = q.normalized(); - base_rot = q; - } - } + const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; - if (is_translation) { - if (F[k]->mNumPositionKeys != 0) { - aiVectorKey key = F[k]->mPositionKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - base_pos = Vector3(x, y, z); - } - } + const String prop_name = _assimp_get_string(anim_mesh->mName); + const String mesh_name = prop_name.split("*")[0]; - if (is_scaling) { - if (F[k]->mNumScalingKeys != 0) { - aiVectorKey key = F[k]->mScalingKeys[0]; - real_t x = key.mValue.x; - real_t y = key.mValue.y; - real_t z = key.mValue.z; - base_scale = Vector3(x, y, z); - } - } - if (is_translation) { - for (size_t p = 0; p < F[k]->mNumPositionKeys; p++) { - aiVector3D pos = F[k]->mPositionKeys[p].mValue; - pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); - pos_times.push_back(F[k]->mPositionKeys[p].mTime / ticks_per_second); - } - } + ERR_CONTINUE(prop_name.split("*").size() != 2); - if (is_rotation) { - for (size_t r = 0; r < F[k]->mNumRotationKeys; r++) { - aiQuaternion quat = F[k]->mRotationKeys[r].mValue; - rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); - rot_times.push_back(F[k]->mRotationKeys[r].mTime / ticks_per_second); - } - } + ERR_CONTINUE(!state.node_map.has(mesh_name)); - if (is_scaling) { - for (size_t sc = 0; sc < F[k]->mNumScalingKeys; sc++) { - aiVector3D scale = F[k]->mScalingKeys[sc].mValue; - scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); - scale_times.push_back(F[k]->mScalingKeys[sc].mTime / ticks_per_second); - } - } + const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(state.node_map[mesh_name]); + + ERR_CONTINUE(mesh_instance == NULL); + + String base_path = state.root->get_path_to(mesh_instance); + + Ref<Mesh> mesh = mesh_instance->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + + //add the tracks for this mesh + int base_track = animation->get_track_count(); + for (int j = 0; j < mesh->get_blend_shape_count(); j++) { + + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j)); } - } - int32_t track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM); - animation->track_set_path(track_idx, node_path); - float increment = 1.0 / float(p_bake_fps); - float time = 0.0; - bool last = false; - while (true) { - Vector3 pos = Vector3(); - Quat rot = Quat(); - Vector3 scale = Vector3(1.0f, 1.0f, 1.0f); - if (is_translation && pos_values.size()) { - pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); - Transform anim_xform; - String ext = p_path.get_file().get_extension().to_lower(); - if (ext == "fbx") { - aiNode *ai_node = _ai_find_node(p_scene->mRootNode, p_node_name); - Transform mesh_xform = _get_global_ai_node_transform(p_scene, ai_node); - pos = mesh_xform.origin + pos; - real_t factor = 1.0f; - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("UnitScaleFactor", factor); - factor = factor * 0.01f; - } - pos = pos * factor; + + for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { + for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { + + float t = anim_mesh->mKeys[k].mTime / ticks_per_second; + float w = anim_mesh->mKeys[k].mWeights[j]; + + animation->track_insert_key(base_track + j, t, w); } } - if (is_rotation && rot_values.size()) { - rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); - } - if (is_scaling && scale_values.size()) { - scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); - } - animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + } - if (last) { - break; - } - time += increment; - if (time >= length) { - last = true; - time = length; - } + if (animation->get_track_count()) { + state.animation_player->add_animation(name, animation); } } @@ -976,611 +741,320 @@ float EditorSceneImporterAssimp::_get_fbx_fps(int32_t time_mode, const aiScene * return 0; } -Transform EditorSceneImporterAssimp::_get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node) { +Transform EditorSceneImporterAssimp::_get_global_assimp_node_transform(const aiNode *p_current_node) { aiNode const *current_node = p_current_node; Transform xform; while (current_node != NULL) { - xform = _ai_matrix_transform(current_node->mTransformation) * xform; + xform = _assimp_matrix_transform(current_node->mTransformation) * xform; current_node = current_node->mParent; } return xform; } -void EditorSceneImporterAssimp::_generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights) { - for (size_t i = 0; i < p_node->mNumMeshes; i++) { - const unsigned int mesh_idx = p_node->mMeshes[i]; - const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; - for (size_t j = 0; j < ai_mesh->mNumBones; j++) { - String bone_name = _ai_string_to_string(ai_mesh->mBones[j]->mName); - if (p_skeleton->find_bone(bone_name) != -1) { - continue; - } - p_mesh_bones.insert(bone_name, true); - p_skeleton->add_bone(bone_name); - int32_t idx = p_skeleton->find_bone(bone_name); - Transform xform = _ai_matrix_transform(ai_mesh->mBones[j]->mOffsetMatrix); - String ext = p_path.get_file().get_extension().to_lower(); - if (ext == "fbx") { - Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node); - mesh_xform.basis = Basis(); - xform = mesh_xform.affine_inverse() * xform; - } - p_skeleton->set_bone_rest(idx, xform.affine_inverse()); - } - } -} +Ref<Texture> EditorSceneImporterAssimp::_load_texture(ImportState &state, String p_path) { + Vector<String> split_path = p_path.get_basename().split("*"); + if (split_path.size() == 2) { + size_t texture_idx = split_path[1].to_int(); + ERR_FAIL_COND_V(texture_idx >= state.assimp_scene->mNumTextures, Ref<Texture>()); + aiTexture *tex = state.assimp_scene->mTextures[texture_idx]; + String filename = _assimp_raw_string_to_string(tex->mFilename); + filename = filename.get_file(); + print_verbose("Open Asset Import: Loading embedded texture " + filename); + if (tex->mHeight == 0) { + if (tex->CheckFormat("png")) { + Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); -void EditorSceneImporterAssimp::_generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi) { - for (size_t i = 0; i < p_node->mNumMeshes; i++) { - const unsigned int mesh_idx = p_node->mMeshes[i]; - const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; - - for (size_t j = 0; j < ai_mesh->mNumBones; j++) { - aiNode *bone_node = p_scene->mRootNode->FindNode(ai_mesh->mBones[j]->mName); - ERR_CONTINUE(bone_node == NULL); - aiNode *bone_node_parent = bone_node->mParent; - while (bone_node_parent != NULL) { - String bone_parent_name = _ai_string_to_string(bone_node_parent->mName); - bone_parent_name = bone_parent_name.split(ASSIMP_FBX_KEY)[0]; - if (bone_parent_name == p_mi->get_name()) { - break; - } - if (p_mi->get_parent() == NULL) { - break; - } - if (bone_parent_name == p_mi->get_parent()->get_name()) { - break; - } - if (bone_node_parent->mParent == p_scene->mRootNode) { - break; - } - if (p_skeleton->find_bone(bone_parent_name) == -1) { - p_mesh_bones.insert(bone_parent_name, true); - } - bone_node_parent = bone_node_parent->mParent; - } - } - } -} -void EditorSceneImporterAssimp::_calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node) { - if (s->get_bone_count() > 0) { - String bone_name = s->get_bone_name(0); - p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, bone_name); - for (size_t i = 0; i < p_scene->mRootNode->mNumChildren; i++) { - if (p_ai_skeleton_root == NULL) { - break; + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("jpg")) { + Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("dds")) { + ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented"); + ERR_FAIL_COND_V(true, Ref<Texture>()); + //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + //Ref<ImageTexture> t; + //t.instance(); + //t->create_from_image(img); + //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + //return t; } - aiNode *found = p_scene->mRootNode->mChildren[i]->FindNode(p_ai_skeleton_root->mName); - if (found) { - p_ai_skeleton_root = p_scene->mRootNode->mChildren[i]; - break; + } else { + Ref<Image> img; + img.instance(); + PoolByteArray arr; + uint32_t size = tex->mWidth * tex->mHeight; + arr.resize(size); + memcpy(arr.write().ptr(), tex->pcData, size); + ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>()); + //ARGB8888 to RGBA8888 + for (int32_t i = 0; i < arr.size() / 4; i++) { + arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; + arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; + arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; + arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; } - } - } + img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); - if (p_ai_skeleton_root == NULL) { - p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_node->mName); - while (p_ai_skeleton_root && p_ai_skeleton_root->mParent && p_ai_skeleton_root->mParent != p_scene->mRootNode) { - p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_ai_skeleton_root->mName)->mParent; + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; } + return Ref<Texture>(); } - p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, _ai_string_to_string(p_ai_skeleton_root->mName).split(ASSIMP_FBX_KEY)[0]); -} - -void EditorSceneImporterAssimp::_fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones) { - String node_name = _ai_string_to_string(p_node->mName); - if (p_mesh_bones.find(node_name) != NULL && p_skeleton->find_bone(node_name) == -1) { - r_removed_bones.insert(node_name); - p_skeleton->add_bone(node_name); - int32_t idx = p_skeleton->find_bone(node_name); - Transform xform = _get_global_ai_node_transform(p_scene, p_node); - xform = _format_rot_xform(p_path, p_scene) * xform; - p_skeleton->set_bone_rest(idx, xform); - } - - for (size_t i = 0; i < p_node->mNumChildren; i++) { - _fill_skeleton(p_scene, p_node->mChildren[i], p_current, p_owner, p_skeleton, p_mesh_bones, p_bone_rests, p_tracks, p_path, r_removed_bones); - } + Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture"); + return p_texture; } -void EditorSceneImporterAssimp::_keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes) { - if (p_current == p_owner) { - r_keep_nodes.insert(p_current); - } - - if (p_current->get_class() != Spatial().get_class()) { - r_keep_nodes.insert(p_current); - } - - for (int i = 0; i < p_current->get_child_count(); i++) { - _keep_node(p_path, p_current->get_child(i), p_owner, r_keep_nodes); - } -} +Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportState &state, int p_index, bool p_double_sided) { -void EditorSceneImporterAssimp::_filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes) { - if (p_keep_nodes.has(p_current) == false) { - r_removed_nodes.insert(p_current->get_name()); - p_current->queue_delete(); - } - for (int i = 0; i < p_current->get_child_count(); i++) { - _filter_node(p_path, p_current->get_child(i), p_owner, p_keep_nodes, r_removed_nodes); - } -} + ERR_FAIL_INDEX_V(p_index, (int)state.assimp_scene->mNumMaterials, Ref<Material>()); -void EditorSceneImporterAssimp::_generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) { - Spatial *child_node = NULL; - if (p_node == NULL) { - return; - } - String node_name = _ai_string_to_string(p_node->mName); - real_t factor = 1.0f; - String ext = p_path.get_file().get_extension().to_lower(); - if (ext == "fbx") { - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("UnitScaleFactor", factor); - factor = factor * 0.01f; - } - } - { - Transform xform = _ai_matrix_transform(p_node->mTransformation); - - child_node = memnew(Spatial); - p_parent->add_child(child_node); - child_node->set_owner(p_owner); - if (p_node == p_scene->mRootNode) { - if ((ext == "fbx") && p_node == p_scene->mRootNode) { - xform = xform.scaled(Vector3(factor, factor, factor)); - Transform format_xform = _format_rot_xform(p_path, p_scene); - xform = format_xform * xform; - } - } - child_node->set_transform(xform * child_node->get_transform()); - } + aiMaterial *ai_material = state.assimp_scene->mMaterials[p_index]; + Ref<SpatialMaterial> mat; + mat.instance(); - if (p_node->mNumMeshes > 0) { - MeshInstance *mesh_node = memnew(MeshInstance); - p_parent->add_child(mesh_node); - mesh_node->set_owner(p_owner); - mesh_node->set_transform(child_node->get_transform()); - { - Map<String, bool> mesh_bones; - p_skeleton->set_use_bones_in_world_transform(true); - _generate_node_bone(p_scene, p_node, mesh_bones, p_skeleton, p_path, p_max_bone_weights); - Set<String> tracks; - _get_track_set(p_scene, tracks); - aiNode *skeleton_root = NULL; - _calculate_skeleton_root(p_skeleton, p_scene, skeleton_root, mesh_bones, p_node); - _generate_node_bone_parents(p_scene, p_node, mesh_bones, p_skeleton, mesh_node); - if (p_skeleton->get_bone_count() > 0) { - _fill_skeleton(p_scene, skeleton_root, mesh_node, p_owner, p_skeleton, mesh_bones, p_bone_rests, tracks, p_path, r_removed_bones); - _set_bone_parent(p_skeleton, p_owner, p_scene->mRootNode); - } - MeshInstance *mi = Object::cast_to<MeshInstance>(mesh_node); - if (mi) { - r_mesh_instances.push_back(mi); - } - _add_mesh_to_mesh_instance(p_node, p_scene, p_skeleton, p_path, mesh_node, p_owner, r_bone_name, r_mesh_count, p_max_bone_weights, r_name_morph_mesh_names); - } - if (mesh_node != NULL && p_skeleton->get_bone_count() > 0 && p_owner->find_node(p_skeleton->get_name()) == NULL) { - Node *node = p_owner->find_node(_ai_string_to_string(p_scene->mRootNode->mName)); - ERR_FAIL_COND(node == NULL); - node->add_child(p_skeleton); - p_skeleton->set_owner(p_owner); - if (ext == "fbx") { - Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node); - mesh_xform.origin = Vector3(); - p_skeleton->set_transform(mesh_xform); - } - r_skeletons.insert(p_skeleton, mesh_node); - } - for (size_t i = 0; i < p_node->mNumMeshes; i++) { - if (p_scene->mMeshes[p_node->mMeshes[i]]->HasBones()) { - mesh_node->set_name(node_name); - // Meshes without skeletons must not have skeletons - mesh_node->set_skeleton_path(String(mesh_node->get_path_to(p_owner)) + "/" + p_owner->get_path_to(p_skeleton)); - } + int32_t mat_two_sided = 0; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) { + if (mat_two_sided > 0) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); } - child_node->get_parent()->remove_child(child_node); - memdelete(child_node); - child_node = mesh_node; - } else if (p_light_names.has(node_name)) { - Spatial *light_node = Object::cast_to<Light>(p_owner->find_node(node_name)); - ERR_FAIL_COND(light_node == NULL); - if (!p_parent->has_node(light_node->get_path())) { - p_parent->add_child(light_node); - } - light_node->set_owner(p_owner); - light_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) * - light_node->get_transform().scaled(Vector3(factor, factor, factor))); - child_node->get_parent()->remove_child(child_node); - memdelete(child_node); - child_node = light_node; - } else if (p_camera_names.has(node_name)) { - Spatial *camera_node = Object::cast_to<Camera>(p_owner->find_node(node_name)); - ERR_FAIL_COND(camera_node == NULL); - if (!p_parent->has_node(camera_node->get_path())) { - p_parent->add_child(camera_node); - } - camera_node->set_owner(p_owner); - camera_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) * - camera_node->get_transform().scaled(Vector3(factor, factor, factor))); - camera_node->scale(Vector3(factor, factor, factor)); - child_node->get_parent()->remove_child(child_node); - memdelete(child_node); - child_node = camera_node; - } - child_node->set_name(node_name); - for (size_t i = 0; i < p_node->mNumChildren; i++) { - _generate_node(p_path, p_scene, p_node->mChildren[i], child_node, p_owner, r_bone_name, p_light_names, p_camera_names, r_skeletons, p_bone_rests, r_mesh_instances, r_mesh_count, p_skeleton, p_max_bone_weights, r_removed_bones, r_name_morph_mesh_names); } -} - -aiNode *EditorSceneImporterAssimp::_ai_find_node(aiNode *ai_child_node, const String bone_name) { - if (_ai_string_to_string(ai_child_node->mName) == bone_name) { - return ai_child_node; + //const String mesh_name = _assimp_get_string(ai_mesh->mName); + aiString mat_name; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { + mat->set_name(_assimp_get_string(mat_name)); } - aiNode *target = NULL; - for (size_t i = 0; i < ai_child_node->mNumChildren; i++) { - target = _ai_find_node(ai_child_node->mChildren[i], bone_name); - if (target != NULL) { - return target; - } - } - return target; -} + aiTextureType tex_normal = aiTextureType_NORMALS; + { + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; -Transform EditorSceneImporterAssimp::_format_rot_xform(const String p_path, const aiScene *p_scene) { - String ext = p_path.get_file().get_extension().to_lower(); + if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); - Transform xform; - int32_t up_axis = 0; - Vector3 up_axis_vec3 = Vector3(); - - int32_t front_axis = 0; - Vector3 front_axis_vec3 = Vector3(); - - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("UpAxis", up_axis); - if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_X) { - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("FrontAxis", front_axis); - if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { - // y - } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { - // z - //front_axis_vec3 = Vector3(0.0f, Math::deg2rad(-180.f), 0.0f); - } - } - } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Y) { - up_axis_vec3 = Vector3(Math::deg2rad(-90.f), 0.0f, 0.0f); - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("FrontAxis", front_axis); - if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { - // x - } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { - // z - } - } - } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Z) { - up_axis_vec3 = Vector3(0.0f, Math ::deg2rad(90.f), 0.0f); - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("FrontAxis", front_axis); - if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { - // x - } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { - // y + if (texture != NULL) { + if (map_mode != NULL) { + _set_texture_mapping_mode(map_mode, texture); + } + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); } } } } - int32_t up_axis_sign = 0; - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("UpAxisSign", up_axis_sign); - up_axis_vec3 = up_axis_vec3 * up_axis_sign; - } - - int32_t front_axis_sign = 0; - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("FrontAxisSign", front_axis_sign); - front_axis_vec3 = front_axis_vec3 * front_axis_sign; - } + { + aiString ai_filename = aiString(); + String filename = ""; - int32_t coord_axis = 0; - Vector3 coord_axis_vec3 = Vector3(); - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("CoordAxis", coord_axis); - if (coord_axis == AssetImportFbx::COORD_LEFT) { - } else if (coord_axis == AssetImportFbx::COORD_RIGHT) { + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } } } - int32_t coord_axis_sign = 0; - if (p_scene->mMetaData != NULL) { - p_scene->mMetaData->Get("CoordAxisSign", coord_axis_sign); - } - - Quat up_quat; - up_quat.set_euler(up_axis_vec3); - - Quat coord_quat; - coord_quat.set_euler(coord_axis_vec3); - - Quat front_quat; - front_quat.set_euler(front_axis_vec3); + aiTextureType tex_emissive = aiTextureType_EMISSIVE; - xform.basis.set_quat(up_quat * coord_quat * front_quat); - return xform; -} + if (ai_material->GetTextureCount(tex_emissive) > 0) { -void EditorSceneImporterAssimp::_get_track_set(const aiScene *p_scene, Set<String> &tracks) { - for (size_t i = 0; i < p_scene->mNumAnimations; i++) { - for (size_t j = 0; j < p_scene->mAnimations[i]->mNumChannels; j++) { - aiString ai_name = p_scene->mAnimations[i]->mChannels[j]->mNodeName; - String name = _ai_string_to_string(ai_name); - tracks.insert(name); - } - } -} + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; -void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) { - Ref<ArrayMesh> mesh; - mesh.instance(); - bool has_uvs = false; - for (size_t i = 0; i < p_node->mNumMeshes; i++) { - const unsigned int mesh_idx = p_node->mMeshes[i]; - const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; - - Map<uint32_t, Vector<float> > vertex_weight; - Map<uint32_t, Vector<String> > vertex_bone_name; - for (size_t b = 0; b < ai_mesh->mNumBones; b++) { - aiBone *bone = ai_mesh->mBones[b]; - for (size_t w = 0; w < bone->mNumWeights; w++) { - String name = _ai_string_to_string(bone->mName); - aiVertexWeight ai_weights = bone->mWeights[w]; - uint32_t vertexId = ai_weights.mVertexId; - Map<uint32_t, Vector<float> >::Element *result = vertex_weight.find(vertexId); - Vector<float> weights; - if (result != NULL) { - weights.append_array(result->value()); - } - weights.push_back(ai_weights.mWeight); - if (vertex_weight.has(vertexId)) { - vertex_weight[vertexId] = weights; - } else { - vertex_weight.insert(vertexId, weights); - } - Map<uint32_t, Vector<String> >::Element *bone_result = vertex_bone_name.find(vertexId); - Vector<String> bone_names; - if (bone_result != NULL) { - bone_names.append_array(bone_result->value()); - } - bone_names.push_back(name); - if (vertex_bone_name.has(vertexId)) { - vertex_bone_name[vertexId] = bone_names; - } else { - vertex_bone_name.insert(vertexId, bone_names); + if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture); } } } + } - Ref<SurfaceTool> st; - st.instance(); - st->begin(Mesh::PRIMITIVE_TRIANGLES); - - for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { - if (ai_mesh->HasTextureCoords(0)) { - has_uvs = true; - st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y)); - } - if (ai_mesh->HasTextureCoords(1)) { - has_uvs = true; - st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y)); - } - if (ai_mesh->HasVertexColors(0)) { - Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a); - st->add_color(color); - } - if (ai_mesh->mNormals != NULL) { - const aiVector3D normals = ai_mesh->mNormals[j]; - const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); - st->add_normal(godot_normal); - if (ai_mesh->HasTangentsAndBitangents()) { - const aiVector3D tangents = ai_mesh->mTangents[j]; - const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); - const aiVector3D bitangent = ai_mesh->mBitangents[j]; - const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); - float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; - st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); - } - } + aiTextureType tex_albedo = aiTextureType_DIFFUSE; + if (ai_material->GetTextureCount(tex_albedo) > 0) { - if (s != NULL && s->get_bone_count() > 0) { - Map<uint32_t, Vector<String> >::Element *I = vertex_bone_name.find(j); - Vector<int32_t> bones; - if (I != NULL) { - Vector<String> bone_names; - bone_names.append_array(I->value()); - for (int32_t f = 0; f < bone_names.size(); f++) { - int32_t bone = s->find_bone(bone_names[f]); - ERR_EXPLAIN("Asset Importer: Mesh can't find bone " + bone_names[f]); - ERR_FAIL_COND(bone == -1); - bones.push_back(bone); - } - if (s->get_bone_count()) { - int32_t add = CLAMP(p_max_bone_weights - bones.size(), 0, p_max_bone_weights); - for (int32_t f = 0; f < add; f++) { - bones.push_back(0); - } - } - st->add_bones(bones); - Map<uint32_t, Vector<float> >::Element *E = vertex_weight.find(j); - Vector<float> weights; - if (E != NULL) { - weights = E->value(); - if (weights.size() != p_max_bone_weights) { - int32_t add = CLAMP(p_max_bone_weights - weights.size(), 0, p_max_bone_weights); - for (int32_t f = 0; f < add; f++) { - weights.push_back(0.0f); - } - } + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } - ERR_CONTINUE(weights.size() == 0); - st->add_weights(weights); + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); } } - const aiVector3D pos = ai_mesh->mVertices[j]; - Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); - st->add_vertex(godot_pos); } - for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { - const aiFace face = ai_mesh->mFaces[j]; - ERR_FAIL_COND(face.mNumIndices != 3); - Vector<size_t> order; - order.push_back(2); - order.push_back(1); - order.push_back(0); - for (int32_t k = 0; k < order.size(); k++) { - st->add_index(face.mIndices[order[k]]); + } else { + aiColor4D clr_diffuse; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) { + if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a)); + } + } + + aiString tex_gltf_base_color_path = aiString(); + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) { + String filename = _assimp_raw_string_to_string(tex_gltf_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); } } - if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { - st->generate_tangents(); - } - aiMaterial *ai_material = p_scene->mMaterials[ai_mesh->mMaterialIndex]; - Ref<SpatialMaterial> mat; - mat.instance(); - - int32_t mat_two_sided = 0; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) { - if (mat_two_sided > 0) { - mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) { + if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); } - - const String mesh_name = _ai_string_to_string(ai_mesh->mName); - aiString mat_name; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { - mat->set_name(_ai_string_to_string(mat_name)); - } - - aiTextureType tex_normal = aiTextureType_NORMALS; - { - aiString ai_filename = aiString(); - String filename = ""; - aiTextureMapMode map_mode[2]; - - if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { - filename = _ai_raw_string_to_string(ai_filename); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - - if (texture != NULL) { - if (map_mode != NULL) { - _set_texture_mapping_mode(map_mode, texture); - } - mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); } } - } - - { - aiString ai_filename = aiString(); - String filename = ""; - - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) { - filename = _ai_raw_string_to_string(ai_filename); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); - } - } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) { + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); } } - aiTextureType tex_emissive = aiTextureType_EMISSIVE; - - if (ai_material->GetTextureCount(tex_emissive) > 0) { - - aiString ai_filename = aiString(); - String filename = ""; - aiTextureMapMode map_mode[2]; + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } + } - if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { - filename = _ai_raw_string_to_string(ai_filename); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - _set_texture_mapping_mode(map_mode, texture); - mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true); - mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture); - } + { + aiString tex_fbx_pbs_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_normal_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); } } } + } - aiTextureType tex_albedo = aiTextureType_DIFFUSE; - if (ai_material->GetTextureCount(tex_albedo) > 0) { - - aiString ai_filename = aiString(); - String filename = ""; - aiTextureMapMode map_mode[2]; - if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { - filename = _ai_raw_string_to_string(ai_filename); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) { - _set_texture_mapping_mode(map_mode, texture); - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); - } - mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); - } - } - } - } else { - aiColor4D clr_diffuse; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) { - if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) { - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + if (p_double_sided) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + + { + aiString tex_fbx_stingray_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_stingray_normal_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); } - mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a)); } } + } - aiString tex_gltf_base_color_path = aiString(); - aiTextureMapMode map_mode[2]; - if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) { - String filename = _ai_raw_string_to_string(tex_gltf_base_color_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); bool found = false; - _find_texture_path(p_path, path, found); + _find_texture_path(state.path, path, found); if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); if (texture != NULL) { if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { - _set_texture_mapping_mode(map_mode, texture); mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } @@ -1589,263 +1063,274 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, } } else { aiColor4D pbr_base_color; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) { - if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) { - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); - } + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) { mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); } } - { - aiString tex_fbx_pbs_base_color_path = aiString(); - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); - if (texture != NULL) { - if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); - } - mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); - } - } - } else { - aiColor4D pbr_base_color; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) { - mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); - } - } - aiUVTransform pbr_base_color_uv_xform; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { - mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); - mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); - } + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); } + } - { - aiString tex_fbx_pbs_normal_path = aiString(); - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_normal_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); - if (texture != NULL) { - mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + { + aiString tex_fbx_pbs_emissive_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_emissive_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); } } + } else { + aiColor4D pbr_emmissive_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) { + mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a)); + } } - aiString cull_mode; - if (p_node->mMetaData) { - p_node->mMetaData->Get("Culling", cull_mode); - } - if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) { - mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + real_t pbr_emission_intensity; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) { + mat->set_emission_energy(pbr_emission_intensity); } + } - { - aiString tex_fbx_stingray_normal_path = aiString(); - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_stingray_normal_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); - if (texture != NULL) { - mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); - } - } + aiString tex_gltf_pbr_metallicroughness_path; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) { + String filename = _assimp_raw_string_to_string(tex_gltf_pbr_metallicroughness_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); } } + } else { + float pbr_roughness = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + float pbr_metallic = 0.0f; - { - aiString tex_fbx_pbs_base_color_path = aiString(); - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); - if (texture != NULL) { - if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); - } - mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); - } - } - } else { - aiColor4D pbr_base_color; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) { - mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); } } - - aiUVTransform pbr_base_color_uv_xform; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { - mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); - mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } else { + float pbr_metallic = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); } } - { - aiString tex_fbx_pbs_emissive_path = aiString(); - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_emissive_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - _find_texture_path(p_path, path, found); - if (texture != NULL) { - if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); - } - mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); - } - } - } else { - aiColor4D pbr_emmissive_color; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) { - mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a)); + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); } } + } else { + float pbr_roughness = 0.04f; - real_t pbr_emission_intensity; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) { - mat->set_emission_energy(pbr_emission_intensity); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); } } + } - aiString tex_gltf_pbr_metallicroughness_path; - if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) { - String filename = _ai_raw_string_to_string(tex_gltf_pbr_metallicroughness_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); bool found = false; - _find_texture_path(p_path, path, found); + _find_texture_path(state.path, path, found); if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); + Ref<Texture> texture = _load_texture(state, path); if (texture != NULL) { mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); - mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); - mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); - mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); } } } else { - float pbr_roughness = 0.0f; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) { - mat->set_roughness(pbr_roughness); - } float pbr_metallic = 0.0f; - - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) { + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) { mat->set_metallic(pbr_metallic); } } - { - aiString tex_fbx_pbs_metallic_path; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); - mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); - } - } - } else { - float pbr_metallic = 0.0f; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) { - mat->set_metallic(pbr_metallic); + + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); } } + } else { + float pbr_roughness = 0.04f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + } + } + + return mat; +} + +Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton, bool p_double_sided_material) { + + Ref<ArrayMesh> mesh; + mesh.instance(); + bool has_uvs = false; - aiString tex_fbx_pbs_rough_path; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_rough_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); - mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + for (int i = 0; i < p_surface_indices.size(); i++) { + const unsigned int mesh_idx = p_surface_indices[i]; + const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; + + Map<uint32_t, Vector<BoneInfo> > vertex_weights; + + if (p_skeleton) { + for (size_t b = 0; b < ai_mesh->mNumBones; b++) { + aiBone *bone = ai_mesh->mBones[b]; + String bone_name = _assimp_get_string(bone->mName); + int bone_index = p_skeleton->find_bone(bone_name); + ERR_CONTINUE(bone_index == -1); //bone refers to an unexisting index, wtf. + + for (size_t w = 0; w < bone->mNumWeights; w++) { + + aiVertexWeight ai_weights = bone->mWeights[w]; + + BoneInfo bi; + + uint32_t vertex_index = ai_weights.mVertexId; + bi.bone = bone_index; + bi.weight = ai_weights.mWeight; + ; + + if (!vertex_weights.has(vertex_index)) { + vertex_weights[vertex_index] = Vector<BoneInfo>(); } - } - } else { - float pbr_roughness = 0.04f; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) { - mat->set_roughness(pbr_roughness); + vertex_weights[vertex_index].push_back(bi); } } } - { - aiString tex_fbx_pbs_metallic_path; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); - mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); - } - } - } else { - float pbr_metallic = 0.0f; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) { - mat->set_metallic(pbr_metallic); + Ref<SurfaceTool> st; + st.instance(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + + for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { + if (ai_mesh->HasTextureCoords(0)) { + has_uvs = true; + st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y)); + } + if (ai_mesh->HasTextureCoords(1)) { + has_uvs = true; + st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y)); + } + if (ai_mesh->HasVertexColors(0)) { + Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a); + st->add_color(color); + } + if (ai_mesh->mNormals != NULL) { + const aiVector3D normals = ai_mesh->mNormals[j]; + const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); + st->add_normal(godot_normal); + if (ai_mesh->HasTangentsAndBitangents()) { + const aiVector3D tangents = ai_mesh->mTangents[j]; + const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); + const aiVector3D bitangent = ai_mesh->mBitangents[j]; + const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); + float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; + st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); } } - aiString tex_fbx_pbs_rough_path; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { - String filename = _ai_raw_string_to_string(tex_fbx_pbs_rough_path); - String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); - bool found = false; - _find_texture_path(p_path, path, found); - if (found) { - Ref<Texture> texture = _load_texture(p_scene, path); - if (texture != NULL) { - mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); - mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); - } - } - } else { - float pbr_roughness = 0.04f; + if (vertex_weights.has(j)) { - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) { - mat->set_roughness(pbr_roughness); + Vector<BoneInfo> bone_info = vertex_weights[j]; + Vector<int> bones; + bones.resize(bone_info.size()); + Vector<float> weights; + weights.resize(bone_info.size()); + for (int k = 0; k < bone_info.size(); k++) { + bones.write[k] = bone_info[k].bone; + weights.write[k] = bone_info[k].weight; } + + st->add_bones(bones); + st->add_weights(weights); } + + const aiVector3D pos = ai_mesh->mVertices[j]; + Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); + st->add_vertex(godot_pos); + } + + for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { + const aiFace face = ai_mesh->mFaces[j]; + ERR_CONTINUE(face.mNumIndices != 3); + Vector<size_t> order; + order.push_back(2); + order.push_back(1); + order.push_back(0); + for (int32_t k = 0; k < order.size(); k++) { + st->add_index(face.mIndices[order[k]]); + } + } + if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { + st->generate_tangents(); + } + + Ref<Material> material; + + if (!state.material_cache.has(ai_mesh->mMaterialIndex)) { + material = _generate_material_from_index(state, ai_mesh->mMaterialIndex, p_double_sided_material); } Array array_mesh = st->commit_to_arrays(); @@ -1855,13 +1340,16 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, Map<uint32_t, String> morph_mesh_idx_names; for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { - String ai_anim_mesh_name = _ai_string_to_string(ai_mesh->mAnimMeshes[j]->mName); - mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); - if (ai_anim_mesh_name.empty()) { - ai_anim_mesh_name = String("morph_") + itos(j); + if (i == 0) { + //only do this the first time + String ai_anim_mesh_name = _assimp_get_string(ai_mesh->mAnimMeshes[j]->mName); + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + if (ai_anim_mesh_name.empty()) { + ai_anim_mesh_name = String("morph_") + itos(j); + } + mesh->add_blend_shape(ai_anim_mesh_name); } - mesh->add_blend_shape(ai_anim_mesh_name); - morph_mesh_idx_names.insert(j, ai_anim_mesh_name); + Array array_copy; array_copy.resize(VisualServer::ARRAY_MAX); @@ -1945,82 +1433,192 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, morphs[j] = array_copy; } - r_name_morph_mesh_names.insert(_ai_string_to_string(p_node->mName), morph_mesh_idx_names); + mesh->add_surface_from_arrays(primitive, array_mesh, morphs); - mesh->surface_set_material(i, mat); - mesh->surface_set_name(i, _ai_string_to_string(ai_mesh->mName)); - r_mesh_count++; - print_line(String("Open Asset Import: Created mesh (including instances) ") + _ai_string_to_string(ai_mesh->mName) + " " + itos(r_mesh_count) + " of " + itos(p_scene->mNumMeshes)); + mesh->surface_set_material(i, material); + mesh->surface_set_name(i, _assimp_get_string(ai_mesh->mName)); } - p_mesh_instance->set_mesh(mesh); + + return mesh; } -Ref<Texture> EditorSceneImporterAssimp::_load_texture(const aiScene *p_scene, String p_path) { - Vector<String> split_path = p_path.get_basename().split("*"); - if (split_path.size() == 2) { - size_t texture_idx = split_path[1].to_int(); - ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Texture>()); - aiTexture *tex = p_scene->mTextures[texture_idx]; - String filename = _ai_raw_string_to_string(tex->mFilename); - filename = filename.get_file(); - print_verbose("Open Asset Import: Loading embedded texture " + filename); - if (tex->mHeight == 0) { - if (tex->CheckFormat("png")) { - Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); +void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent) { - Ref<ImageTexture> t; - t.instance(); - t->create_from_image(img); - t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - return t; - } else if (tex->CheckFormat("jpg")) { - Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); - Ref<ImageTexture> t; - t.instance(); - t->create_from_image(img); - t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - return t; - } else if (tex->CheckFormat("dds")) { - ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented"); - ERR_FAIL_COND_V(true, Ref<Texture>()); - //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); - //Ref<ImageTexture> t; - //t.instance(); - //t->create_from_image(img); - //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - //return t; + Spatial *new_node = NULL; + String node_name = _assimp_get_string(p_assimp_node->mName); + Transform node_transform = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (p_assimp_node->mNumMeshes > 0) { + /* MESH NODE */ + Ref<Mesh> mesh; + Skeleton *skeleton = NULL; + { + + //see if we have mesh cache for this. + Vector<int> surface_indices; + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + int mesh_index = p_assimp_node->mMeshes[i]; + surface_indices.push_back(mesh_index); + + //take the chane and attempt to find the skeleton from the bones + if (!skeleton) { + aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]]; + for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) { + aiBone *bone = ai_mesh->mBones[j]; + String bone_name = _assimp_get_string(bone->mName); + if (state.bone_owners.has(bone_name)) { + skeleton = state.skeletons[state.bone_owners[bone_name]]; + break; + } + } + } } - } else { - Ref<Image> img; - img.instance(); - PoolByteArray arr; - uint32_t size = tex->mWidth * tex->mHeight; - arr.resize(size); - memcpy(arr.write().ptr(), tex->pcData, size); - ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>()); - //ARGB8888 to RGBA8888 - for (int32_t i = 0; i < arr.size() / 4; i++) { - arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; - arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; - arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; - arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; + surface_indices.sort(); + String mesh_key; + for (int i = 0; i < surface_indices.size(); i++) { + if (i > 0) { + mesh_key += ":"; + } + mesh_key += itos(surface_indices[i]); } - img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); - ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); - Ref<ImageTexture> t; - t.instance(); - t->create_from_image(img); - t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - return t; + if (!state.mesh_cache.has(mesh_key)) { + //adding cache + aiString cull_mode; //cull is on mesh, which is kind of stupid tbh + bool double_sided_material = false; + if (p_assimp_node->mMetaData) { + p_assimp_node->mMetaData->Get("Culling", cull_mode); + } + if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) { + double_sided_material = true; + } + + mesh = _generate_mesh_from_surface_indices(state, surface_indices, skeleton, double_sided_material); + state.mesh_cache[mesh_key] = mesh; + } + + mesh = state.mesh_cache[mesh_key]; } - return Ref<Texture>(); + + MeshInstance *mesh_node = memnew(MeshInstance); + if (skeleton) { + state.mesh_skeletons[mesh_node] = skeleton; + } + mesh_node->set_mesh(mesh); + new_node = mesh_node; + + } else if (state.light_cache.has(node_name)) { + + Light *light = NULL; + aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[node_name]]; + ERR_FAIL_COND(!ai_light); + + if (ai_light->mType == aiLightSource_DIRECTIONAL) { + light = memnew(DirectionalLight); + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + + node_transform *= light_transform; + + } else if (ai_light->mType == aiLightSource_POINT) { + light = memnew(OmniLight); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Transform xform; + xform.origin = pos; + + node_transform *= xform; + + light->set_transform(xform); + + //light->set_param(Light::PARAM_ATTENUATION, 1); + } else if (ai_light->mType == aiLightSource_SPOT) { + light = memnew(SpotLight); + + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + node_transform *= light_transform; + + //light->set_param(Light::PARAM_ATTENUATION, 0.0f); + } + ERR_FAIL_COND(light == NULL); + light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b)); + new_node = light; + } else if (state.camera_cache.has(node_name)) { + + aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[node_name]]; + ERR_FAIL_COND(!ai_camera); + + Camera *camera = memnew(Camera); + + float near = ai_camera->mClipPlaneNear; + if (Math::is_equal_approx(near, 0.0f)) { + near = 0.1f; + } + camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar); + + Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z); + Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized(); + Vector3 up = Vector3(ai_camera->mUp.x, ai_camera->mUp.y, ai_camera->mUp.z); + + Transform xform; + xform.set_look_at(pos, look_at, up); + + new_node = camera; + } else if (state.bone_owners.has(node_name)) { + + //have to actually put the skeleton somewhere, you know. + Skeleton *skeleton = state.skeletons[state.bone_owners[node_name]]; + if (skeleton->get_parent()) { + //a bone for a skeleton already added.. + //could go downwards here to add meshes children of skeleton bones + //but let's not support it for now. + return; + } + //restore rest poses to local, now that we know where the skeleton finally is + Transform skeleton_transform; + if (p_assimp_node->mParent) { + skeleton_transform = _get_global_assimp_node_transform(p_assimp_node->mParent); + } + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Transform rest = skeleton_transform.affine_inverse() * skeleton->get_bone_rest(i); + skeleton->set_bone_rest(i, rest.affine_inverse()); + } + + skeleton->localize_rests(); + node_name = "Skeleton"; //don't use the bone root name + node_transform = Transform(); //dont transform + + new_node = skeleton; + } else { + //generic node + new_node = memnew(Spatial); + } + + { + + new_node->set_name(node_name); + new_node->set_transform(node_transform); + p_parent->add_child(new_node); + new_node->set_owner(state.root); + } + + state.node_map[node_name] = new_node; + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_node(state, p_assimp_node->mChildren[i], new_node); } - Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture"); - return p_texture; } void EditorSceneImporterAssimp::_calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) { @@ -2122,31 +1720,25 @@ void EditorSceneImporterAssimp::_find_texture_path(const String &p_path, _Direct } } -String EditorSceneImporterAssimp::_ai_string_to_string(const aiString p_string) const { - Vector<char> raw_name; - raw_name.resize(p_string.length); - memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); +String EditorSceneImporterAssimp::_assimp_get_string(const aiString p_string) const { + //convert an assimp String to a Godot String String name; - name.parse_utf8(raw_name.ptrw(), raw_name.size()); + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); if (name.find(":") != -1) { String replaced_name = name.split(":")[1]; print_verbose("Replacing " + name + " containing : with " + replaced_name); name = replaced_name; } - if (name.find(".") != -1) { - String replaced_name = name.replace(".", ""); - print_verbose("Replacing " + name + " containing . with " + replaced_name); - name = replaced_name; - } + + name = name.replace(".", ""); //can break things, specially bone names + return name; } -String EditorSceneImporterAssimp::_ai_anim_string_to_string(const aiString p_string) const { - Vector<char> raw_name; - raw_name.resize(p_string.length); - memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); +String EditorSceneImporterAssimp::_assimp_anim_string_to_string(const aiString p_string) const { + String name; - name.parse_utf8(raw_name.ptrw(), raw_name.size()); + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); if (name.find(":") != -1) { String replaced_name = name.split(":")[1]; print_verbose("Replacing " + name + " containing : with " + replaced_name); @@ -2155,12 +1747,9 @@ String EditorSceneImporterAssimp::_ai_anim_string_to_string(const aiString p_str return name; } -String EditorSceneImporterAssimp::_ai_raw_string_to_string(const aiString p_string) const { - Vector<char> raw_name; - raw_name.resize(p_string.length); - memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); +String EditorSceneImporterAssimp::_assimp_raw_string_to_string(const aiString p_string) const { String name; - name.parse_utf8(raw_name.ptrw(), raw_name.size()); + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); return name; } @@ -2168,14 +1757,10 @@ Ref<Animation> EditorSceneImporterAssimp::import_animation(const String &p_path, return Ref<Animation>(); } -const Transform EditorSceneImporterAssimp::_ai_matrix_transform(const aiMatrix4x4 p_matrix) { +const Transform EditorSceneImporterAssimp::_assimp_matrix_transform(const aiMatrix4x4 p_matrix) { aiMatrix4x4 matrix = p_matrix; Transform xform; - xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4); - xform.basis.inverse(); - xform.basis.transpose(); - Vector3 scale = xform.basis.get_scale(); - Quat rot = xform.basis.get_rotation_quat(); - xform.basis.set_quat_scale(rot, scale); + //xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4); + xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4); return xform; } diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h index 8f9ed434ae..598845236e 100644 --- a/modules/assimp/editor_scene_importer_assimp.h +++ b/modules/assimp/editor_scene_importer_assimp.h @@ -146,37 +146,65 @@ private: COORD_LEFT = 1 }; }; - Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights); - void _fill_kept_node(Set<Node *> &keep_nodes); - String _find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root); - void _set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node); - Transform _get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node); - void _generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights); - void _generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi); - void _calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node); - void _fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones); - void _keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes); - void _filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes); - void _generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names); - aiNode *_ai_find_node(aiNode *ai_child_node, const String bone_name); - Transform _format_rot_xform(const String p_path, const aiScene *p_scene); - void _get_track_set(const aiScene *p_scene, Set<String> &tracks); - void _insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path); - void _add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names); - Ref<Texture> _load_texture(const aiScene *p_scene, String p_path); + + struct ImportState { + + String path; + const aiScene *assimp_scene; + uint32_t max_bone_weights; + Spatial *root; + Map<String, Ref<Mesh> > mesh_cache; + Map<int, Ref<Material> > material_cache; + Map<String, int> light_cache; + Map<String, int> camera_cache; + Vector<Skeleton *> skeletons; + Map<String, int> bone_owners; //maps bones to skeleton index owned by + Map<String, Node *> node_map; + Map<MeshInstance *, Skeleton *> mesh_skeletons; + bool fbx; //for some reason assimp does some things different for FBX + AnimationPlayer *animation_player; + }; + + struct BoneInfo { + uint32_t bone; + float weight; + }; + + struct SkeletonHole { //nodes may be part of the skeleton by used by vertex + String name; + String parent; + Transform pose; + const aiNode *node; + }; + + const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix); + String _assimp_get_string(const aiString p_string) const; + Transform _get_global_assimp_node_transform(const aiNode *p_current_node); + void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w); void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture); void _find_texture_path(const String &p_path, String &path, bool &r_found); void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension); - String _ai_string_to_string(const aiString p_string) const; - String _ai_anim_string_to_string(const aiString p_string) const; - String _ai_raw_string_to_string(const aiString p_string) const; - void _import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names); - void _insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene); + + Ref<Texture> _load_texture(ImportState &state, String p_path); + Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided); + Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false); + void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent); + void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms); + void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms); + void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms); + + void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name); + + void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps); + + Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights); + + String _assimp_anim_string_to_string(const aiString p_string) const; + String _assimp_raw_string_to_string(const aiString p_string) const; float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene); template <class T> T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); - const Transform _ai_matrix_transform(const aiMatrix4x4 p_matrix); void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; struct ImportFormat { diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 0a94690989..059c06c37c 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -432,7 +432,8 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, } break; - default: {} + default: { + } } wb = PoolVector<uint8_t>::Write(); diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 000917507a..193f1bb48d 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -690,7 +690,9 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * case COMPRESS_ZSTD: { mode = Compression::MODE_ZSTD; } break; - default: { ERR_FAIL_V(0); } + default: { + ERR_FAIL_V(0); + } } int req_size = Compression::get_max_compressed_buffer_size(ofs, mode); @@ -727,7 +729,8 @@ size_t NetworkedMultiplayerENet::enet_decompress(void *context, const enet_uint8 ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD); } break; - default: {} + default: { + } } if (ret < 0) { return 0; diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index 5410d367db..6f54436bf9 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -115,7 +115,8 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f case Image::FORMAT_RGBA5551: { detected_channels = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 3450a032c5..8dbbd2e4eb 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -34,7 +34,7 @@ #include "pluginscript_instance.h" #include "pluginscript_script.h" -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED #define __ASSERT_SCRIPT_REASON "Cannot retrieve pluginscript class for this script, is you code correct ?" #define ASSERT_SCRIPT_VALID() \ { \ diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4385cf12ad..0676317f6e 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -597,7 +597,7 @@ Error GDScript::reload(bool p_keep_state) { return err; } } -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { const GDScriptWarning &warning = E->get(); if (ScriptDebugger::get_singleton()) { diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index ded873c7d3..c67e390e32 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -444,6 +444,7 @@ public: virtual void get_reserved_words(List<String> *p_words) const; virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ae67521749..f7be0ce37c 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -480,16 +480,16 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: switch (cast_type.kind) { case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN); - codegen.opcodes.push_back(cn->cast_type.builtin_type); + codegen.opcodes.push_back(cast_type.builtin_type); } break; case GDScriptDataType::NATIVE: { int class_idx; - if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) { + if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) { - class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type]; + class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type]; class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } else { - _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn); + _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn); return -1; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator @@ -498,7 +498,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: case GDScriptDataType::SCRIPT: case GDScriptDataType::GDSCRIPT: { - Variant script = cn->cast_type.script_type; + Variant script = cast_type.script_type; int idx = codegen.get_constant_pos(script); idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) @@ -1863,6 +1863,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->base = base; p_script->_base = base.ptr(); p_script->member_indices = base->member_indices; + + if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; + } + } + } } break; default: { _set_error("Parser bug: invalid inheritance.", p_class); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index fafc73b7e6..df8fc2267d 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -44,12 +44,43 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const p_delimiters->push_back("#"); } + void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); p_delimiters->push_back("\"\"\" \"\"\""); } + +String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { + + String processed_template = p_template; + +#ifdef TOOLS_ENABLED + if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + processed_template = processed_template.replace("%INT_TYPE%", ": int"); + processed_template = processed_template.replace("%STRING_TYPE%", ": String"); + processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); + processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); + } else { + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); + } +#else + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); +#endif + + processed_template = processed_template.replace("%BASE%", p_base_class_name); + processed_template = processed_template.replace("%TS%", _get_indentation()); + + return processed_template; +} + Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String _template = "extends %BASE%\n" "\n" @@ -65,27 +96,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" "#%TS%pass\n"; -#ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - _template = _template.replace("%INT_TYPE%", ": int"); - _template = _template.replace("%STRING_TYPE%", ": String"); - _template = _template.replace("%FLOAT_TYPE%", ": float"); - _template = _template.replace("%VOID_RETURN%", " -> void"); - } else { - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); - } -#else - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); -#endif - - _template = _template.replace("%BASE%", p_base_class_name); - _template = _template.replace("%TS%", _get_indentation()); + _template = _get_processed_template(_template, p_base_class_name); Ref<GDScript> script; script.instance(); @@ -101,10 +112,8 @@ bool GDScriptLanguage::is_using_templates() { void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name); - src = src.replace("%TS%", _get_indentation()); - p_script->set_source_code(src); + String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); + p_script->set_source_code(_template); } bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { @@ -1061,7 +1070,8 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break; case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break; case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break; - default: {} + default: { + } } if (vop == Variant::OP_MAX) { @@ -1116,7 +1126,8 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G } break; } } break; - default: {} + default: { + } } // It may have found a null, but that's never useful @@ -3371,7 +3382,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; - default: {} + default: { + } } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 46c9efd54f..4fd136d5cc 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -74,6 +74,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "lerp", "inverse_lerp", "range_lerp", + "smoothstep", "dectime", "randomize", "randi", @@ -369,6 +370,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(4); r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_COUNT(3); VALIDATE_ARG_NUM(0); @@ -1435,6 +1443,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1618,6 +1627,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_SMOOTHSTEP: { + MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight")); + mi.return_val.type = Variant::REAL; + return mi; + } break; case MATH_DECTIME: { MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step")); mi.return_val.type = Variant::REAL; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index fcb8f32e54..14bf3d7560 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -65,6 +65,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index e75b8a14a3..8a9eacd835 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -888,7 +888,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case GDScriptTokenizer::TK_OP_SUB: e.op = OperatorNode::OP_NEG; break; case GDScriptTokenizer::TK_OP_NOT: e.op = OperatorNode::OP_NOT; break; case GDScriptTokenizer::TK_OP_BIT_INVERT: e.op = OperatorNode::OP_BIT_INVERT; break; - default: {} + default: { + } } tokenizer->advance(); @@ -1875,7 +1876,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to } } break; - default: { break; } + default: { + break; + } } //now se if all are constants if (!all_constants) @@ -1988,7 +1991,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to return op->arguments[2]; } } break; - default: { ERR_FAIL_V(op); } + default: { + ERR_FAIL_V(op); + } } ERR_FAIL_V(op); @@ -3495,6 +3500,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("'class_name' is only valid for the main class namespace."); return; } + if (self_path.empty()) { + _set_error("'class_name' not allowed in built-in scripts."); + return; + } if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) { _set_error("'class_name' syntax: 'class_name <UniqueName>'"); @@ -3681,6 +3690,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); } } + for (int i = 0; i < p_class->subclasses.size(); i++) { + if (p_class->subclasses[i]->name == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + } #endif // DEBUG_ENABLED if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -4625,6 +4639,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } + + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == member.identifier) { + _set_error("A class named '" + String(member.identifier) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } #ifdef DEBUG_ENABLED for (int i = 0; i < current_class->functions.size(); i++) { if (current_class->functions[i]->name == member.identifier) { @@ -4869,6 +4890,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { @@ -4939,6 +4967,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == enum_name) { + _set_error("A class named '" + String(enum_name) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -5024,6 +5059,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + ClassNode::Constant constant; constant.type.has_type = true; constant.type.kind = DataType::BUILTIN; @@ -6351,7 +6393,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Variant::COLOR: { error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; } break; - default: {} + default: { + } } } if (error) { @@ -6469,7 +6512,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; - default: {} + default: { + } } p_node->set_datatype(_resolve_type(node_type, p_node->line)); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index abc739d645..36503af4d7 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -339,7 +339,8 @@ StringName GDScriptTokenizer::get_token_literal(int p_offset) const { return "null"; case Variant::BOOL: return value ? "true" : "false"; - default: {} + default: { + } } } case TK_OP_AND: @@ -534,13 +535,14 @@ void GDScriptTokenizerText::_advance() { } } #ifdef DEBUG_ENABLED - if (comment.begins_with("#warning-ignore:")) { - String code = comment.get_slice(":", 1); + String comment_content = comment.trim_prefix("#").trim_prefix(" "); + if (comment_content.begins_with("warning-ignore:")) { + String code = comment_content.get_slice(":", 1); warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower())); - } else if (comment.begins_with("#warning-ignore-all:")) { - String code = comment.get_slice(":", 1); + } else if (comment_content.begins_with("warning-ignore-all:")) { + String code = comment_content.get_slice(":", 1); warning_global_skips.insert(code.strip_edges().to_lower()); - } else if (comment.strip_edges() == "#warnings-disable") { + } else if (comment_content.strip_edges() == "warnings-disable") { ignore_warnings = true; } #endif // DEBUG_ENABLED @@ -1302,7 +1304,8 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) ERR_FAIL_V(Vector<uint8_t>()); } break; - default: {} + default: { + } }; token_array.push_back(token); diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 0a75ad4784..dbebaae38d 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -5,7 +5,13 @@ </brief_description> <description> This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect you do need a mobile phone with a gyroscope and accelerometer. - Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters. + Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters, you can change this by setting [member eye_height]. + You can initialise this interface as follows: + [codeblock] + var interface = ARVRServer.find_interface("Native mobile") + if interface and interface.initialize(): + get_viewport().arvr = true + [/codeblock] </description> <tutorials> </tutorials> @@ -20,6 +26,9 @@ <member name="display_width" type="float" setter="set_display_width" getter="get_display_width"> The width of the display in centimeters. </member> + <member name="eye_height" type="float" setter="set_eye_height" getter="get_eye_height"> + The height at which the camera is placed in relation to the ground (i.e. [ARVROrigin] node). + </member> <member name="iod" type="float" setter="set_iod" getter="get_iod"> The interocular distance, also known as the interpupillary distance. The distance between the pupils of the left and right eye. </member> diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index b4fbd417d7..dc7ed03548 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -200,6 +200,9 @@ void MobileVRInterface::set_position_from_sensors() { }; void MobileVRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_eye_height", "eye_height"), &MobileVRInterface::set_eye_height); + ClassDB::bind_method(D_METHOD("get_eye_height"), &MobileVRInterface::get_eye_height); + ClassDB::bind_method(D_METHOD("set_iod", "iod"), &MobileVRInterface::set_iod); ClassDB::bind_method(D_METHOD("get_iod"), &MobileVRInterface::get_iod); @@ -218,6 +221,7 @@ void MobileVRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_k2", "k"), &MobileVRInterface::set_k2); ClassDB::bind_method(D_METHOD("get_k2"), &MobileVRInterface::get_k2); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "eye_height", PROPERTY_HINT_RANGE, "0.0,3.0,0.1"), "set_eye_height", "get_eye_height"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens"); @@ -226,6 +230,14 @@ void MobileVRInterface::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2"); } +void MobileVRInterface::set_eye_height(const real_t p_eye_height) { + eye_height = p_eye_height; +} + +real_t MobileVRInterface::get_eye_height() const { + return eye_height; +} + void MobileVRInterface::set_iod(const real_t p_iod) { intraocular_dist = p_iod; }; @@ -328,6 +340,7 @@ Size2 MobileVRInterface::get_render_targetsize() { // we use half our window size Size2 target_size = OS::get_singleton()->get_window_size(); + target_size.x *= 0.5 * oversample; target_size.y *= oversample; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index adc420ea5f..e595daf16e 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -107,6 +107,9 @@ protected: static void _bind_methods(); public: + void set_eye_height(const real_t p_eye_height); + real_t get_eye_height() const; + void set_iod(const real_t p_iod); real_t get_iod() const; diff --git a/modules/mono/SCsub b/modules/mono/SCsub index b6ba9b1c23..341d57f3e4 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -5,70 +5,6 @@ Import('env_modules') env_mono = env_modules.Clone() -# TODO move functions to their own modules - -def make_cs_files_header(src, dst, version_dst): - from compat import byte_to_str - - with open(dst, 'w') as header: - header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - header.write('#ifndef CS_COMPRESSED_H\n') - header.write('#define CS_COMPRESSED_H\n\n') - header.write('#ifdef TOOLS_ENABLED\n\n') - header.write('#include "core/map.h"\n') - header.write('#include "core/ustring.h"\n') - inserted_files = '' - import os - latest_mtime = 0 - cs_file_count = 0 - for root, _, files in os.walk(src): - files = [f for f in files if f.endswith('.cs')] - for file in files: - cs_file_count += 1 - filepath = os.path.join(root, file) - filepath_src_rel = os.path.relpath(filepath, src) - mtime = os.path.getmtime(filepath) - latest_mtime = mtime if mtime > latest_mtime else latest_mtime - with open(filepath, 'rb') as f: - buf = f.read() - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - name = str(cs_file_count) - header.write('\n') - header.write('// ' + filepath_src_rel + '\n') - header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') - header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') - header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') - for i, buf_idx in enumerate(range(len(buf))): - if i > 0: - header.write(', ') - header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ - 'CompressedFile(_cs_' + name + '_compressed_size, ' \ - '_cs_' + name + '_uncompressed_size, ' \ - '_cs_' + name + '_compressed));\n' - header.write(' };\n') - header.write('\nstruct CompressedFile\n' '{\n' - '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' - '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' - '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' - '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' - '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' - ) - header.write('\n#endif // TOOLS_ENABLED\n') - header.write('\n#endif // CS_COMPRESSED_H\n') - - glue_version = int(latest_mtime) # The latest modified time will do for now - - with open(version_dst, 'w') as version_header: - version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - version_header.write('#ifndef CS_GLUE_VERSION_H\n') - version_header.write('#define CS_GLUE_VERSION_H\n\n') - version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') - version_header.write('\n#endif // CS_GLUE_VERSION_H\n') - - env_mono.add_source_files(env.modules_sources, '*.cpp') env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp') @@ -77,7 +13,12 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp') if env['tools']: env_mono.add_source_files(env.modules_sources, 'editor/*.cpp') # NOTE: It is safe to generate this file here, since this is still executed serially - make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h') + import build_scripts.make_cs_compressed_header as make_cs_compressed_header + make_cs_compressed_header.generate_header( + 'glue/Managed/Files', + 'glue/cs_compressed.gen.h', + 'glue/cs_glue_version.gen.h' + ) vars = Variables() vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) @@ -88,272 +29,29 @@ vars.Update(env_mono) if env_mono['mono_glue']: env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) + import os.path + if not os.path.isfile('glue/mono_glue.gen.cpp'): + raise RuntimeError('Missing mono glue sources. Did you forget to generate them?') + if env_mono['tools'] or env_mono['target'] != 'release': env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD']) -# Configure TLS checks +# Configure Thread Local Storage + +import build_scripts.tls_configure as tls_configure -import tls_configure conf = Configure(env_mono) tls_configure.configure(conf) env_mono = conf.Finish() +# Configure Mono -# Build GodotSharpTools solution - - -import os - - -def find_nuget_unix(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' - - return None - - -def find_nuget_windows(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - - if mono_root: - mono_bin_dir = os.path.join(mono_root, 'bin') - nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') - - if os.path.isfile(nuget_mono): - return nuget_mono - - # Standalone NuGet - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - return None - - -def find_msbuild_unix(filename): - import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' +import build_scripts.mono_configure as mono_configure - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' +mono_configure.configure(env, env_mono) - return None +# Build GodotSharpTools +import build_scripts.godotsharptools_build as godotsharptools_build -def find_msbuild_windows(): - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError('Cannot find mono root directory') - - framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_bin_dir = os.path.join(mono_root, 'bin') - msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') - - if os.path.isfile(msbuild_mono): - # The (Csc/Vbc/Fsc)ToolExe environment variables are required when - # building with Mono's MSBuild. They must point to the batch files - # in Mono's bin directory to make sure they are executed with Mono. - mono_msbuild_env = { - 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), - 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), - 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') - } - return (msbuild_mono, framework_path, mono_msbuild_env) - - msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() - - if msbuild_tools_path: - return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) - - return None - - -def mono_build_solution(source, target, env): - import subprocess - from shutil import copyfile - - sln_path = os.path.abspath(str(source[0])) - target_path = os.path.abspath(str(target[0])) - - framework_path = '' - msbuild_env = os.environ.copy() - - # Needed when running from Developer Command Prompt for VS - if 'PLATFORM' in msbuild_env: - del msbuild_env['PLATFORM'] - - # Find MSBuild - if os.name == 'nt': - msbuild_info = find_msbuild_windows() - if msbuild_info is None: - raise RuntimeError('Cannot find MSBuild executable') - msbuild_path = msbuild_info[0] - framework_path = msbuild_info[1] - msbuild_env.update(msbuild_info[2]) - else: - msbuild_path = find_msbuild_unix('msbuild') - if msbuild_path is None: - xbuild_fallback = env['xbuild_fallback'] - - if xbuild_fallback and os.name == 'nt': - print('Option \'xbuild_fallback\' not supported on Windows') - xbuild_fallback = False - - if xbuild_fallback: - print('Cannot find MSBuild executable, trying with xbuild') - print('Warning: xbuild is deprecated') - - msbuild_path = find_msbuild_unix('xbuild') - - if msbuild_path is None: - raise RuntimeError('Cannot find xbuild executable') - else: - raise RuntimeError('Cannot find MSBuild executable') - - print('MSBuild path: ' + msbuild_path) - - # Find NuGet - nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() - if nuget_path is None: - raise RuntimeError('Cannot find NuGet executable') - - print('NuGet path: ' + nuget_path) - - # Do NuGet restore - - try: - subprocess.check_call([nuget_path, 'restore', sln_path]) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: NuGet restore failed') - - # Build solution - - build_config = 'Release' - - msbuild_args = [ - msbuild_path, - sln_path, - '/p:Configuration=' + build_config, - ] - - if framework_path: - msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] - - try: - subprocess.check_call(msbuild_args, env=msbuild_env) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: Build failed') - - # Copy files - - src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) - asm_file = 'GodotSharpTools.dll' - - if not os.path.isdir(dst_dir): - if os.path.exists(dst_dir): - raise RuntimeError('Target directory is a file') - os.makedirs(dst_dir) - - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) - - # Dependencies - copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) - -if env['tools']: - output_dir = Dir('#bin').abspath - editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') - - mono_sln_builder = Builder(action=mono_build_solution) - env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) - env_mono.MonoBuildSolution( - os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), - 'editor/GodotSharpTools/GodotSharpTools.sln' - ) +godotsharptools_build.build(env_mono) diff --git a/modules/mono/build_scripts/__init__.py b/modules/mono/build_scripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/mono/build_scripts/__init__.py diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/godotsharptools_build.py new file mode 100644 index 0000000000..af3a5cb5c6 --- /dev/null +++ b/modules/mono/build_scripts/godotsharptools_build.py @@ -0,0 +1,263 @@ +# Build GodotSharpTools solution + + +import os + +from SCons.Script import Builder, Dir + + +def find_nuget_unix(): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(env): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + from . import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + +def find_msbuild_unix(filename): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_msbuild_windows(env): + from . import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Cannot find mono root directory') + + framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_bin_dir = os.path.join(mono_root, 'bin') + msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') + + if os.path.isfile(msbuild_mono): + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. + mono_msbuild_env = { + 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), + 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), + 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') + } + return (msbuild_mono, framework_path, mono_msbuild_env) + + msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) + + return None + + +def mono_build_solution(source, target, env): + import subprocess + from shutil import copyfile + + sln_path = os.path.abspath(str(source[0])) + target_path = os.path.abspath(str(target[0])) + + framework_path = '' + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] + + # Find MSBuild + if os.name == 'nt': + msbuild_info = find_msbuild_windows(env) + if msbuild_info is None: + raise RuntimeError('Cannot find MSBuild executable') + msbuild_path = msbuild_info[0] + framework_path = msbuild_info[1] + msbuild_env.update(msbuild_info[2]) + else: + msbuild_path = find_msbuild_unix('msbuild') + if msbuild_path is None: + xbuild_fallback = env['xbuild_fallback'] + + if xbuild_fallback and os.name == 'nt': + print('Option \'xbuild_fallback\' not supported on Windows') + xbuild_fallback = False + + if xbuild_fallback: + print('Cannot find MSBuild executable, trying with xbuild') + print('Warning: xbuild is deprecated') + + msbuild_path = find_msbuild_unix('xbuild') + + if msbuild_path is None: + raise RuntimeError('Cannot find xbuild executable') + else: + raise RuntimeError('Cannot find MSBuild executable') + + print('MSBuild path: ' + msbuild_path) + + # Find NuGet + nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + + try: + subprocess.check_call([nuget_path, 'restore', sln_path]) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: NuGet restore failed') + + # Build solution + + build_config = 'Release' + + msbuild_args = [ + msbuild_path, + sln_path, + '/p:Configuration=' + build_config, + ] + + if framework_path: + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] + + try: + subprocess.check_call(msbuild_args, env=msbuild_env) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: Build failed') + + # Copy files + + src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) + asm_file = 'GodotSharpTools.dll' + + if not os.path.isdir(dst_dir): + if os.path.exists(dst_dir): + raise RuntimeError('Target directory is a file') + os.makedirs(dst_dir) + + copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) + + # Dependencies + copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) + +def build(env_mono): + if not env_mono['tools']: + return + + output_dir = Dir('#bin').abspath + editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') + + mono_sln_builder = Builder(action=mono_build_solution) + env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) + env_mono.MonoBuildSolution( + os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), + 'editor/GodotSharpTools/GodotSharpTools.sln' + ) diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py new file mode 100644 index 0000000000..1f9177cef8 --- /dev/null +++ b/modules/mono/build_scripts/make_cs_compressed_header.py @@ -0,0 +1,61 @@ + +def generate_header(src, dst, version_dst): + from compat import byte_to_str + + with open(dst, 'w') as header: + header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + header.write('#ifndef CS_COMPRESSED_H\n') + header.write('#define CS_COMPRESSED_H\n\n') + header.write('#ifdef TOOLS_ENABLED\n\n') + header.write('#include "core/map.h"\n') + header.write('#include "core/ustring.h"\n') + inserted_files = '' + import os + latest_mtime = 0 + cs_file_count = 0 + for root, _, files in os.walk(src): + files = [f for f in files if f.endswith('.cs')] + for file in files: + cs_file_count += 1 + filepath = os.path.join(root, file) + filepath_src_rel = os.path.relpath(filepath, src) + mtime = os.path.getmtime(filepath) + latest_mtime = mtime if mtime > latest_mtime else latest_mtime + with open(filepath, 'rb') as f: + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + name = str(cs_file_count) + header.write('\n') + header.write('// ' + filepath_src_rel + '\n') + header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') + header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') + for i, buf_idx in enumerate(range(len(buf))): + if i > 0: + header.write(', ') + header.write(byte_to_str(buf[buf_idx])) + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ + 'CompressedFile(_cs_' + name + '_compressed_size, ' \ + '_cs_' + name + '_uncompressed_size, ' \ + '_cs_' + name + '_compressed));\n' + header.write(' };\n') + header.write('\nstruct CompressedFile\n' '{\n' + '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' + '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' + ) + header.write('\n#endif // TOOLS_ENABLED\n') + header.write('\n#endif // CS_COMPRESSED_H\n') + + glue_version = int(latest_mtime) # The latest modified time will do for now + + with open(version_dst, 'w') as version_header: + version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + version_header.write('#ifndef CS_GLUE_VERSION_H\n') + version_header.write('#define CS_GLUE_VERSION_H\n\n') + version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + version_header.write('\n#endif // CS_GLUE_VERSION_H\n') diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py new file mode 100644 index 0000000000..160580e116 --- /dev/null +++ b/modules/mono/build_scripts/mono_configure.py @@ -0,0 +1,414 @@ +import imp +import os +import sys +import subprocess + +from distutils.version import LooseVersion +from SCons.Script import BoolVariable, Dir, Environment, Variables + +if os.name == 'nt': + from . import mono_reg_utils as monoreg + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + +def copy_file(src_dir, dst_dir, name): + from shutil import copyfile + + src_path = os.path.join(src_dir, name) + dst_path = os.path.join(dst_dir, name) + + if not os.path.isdir(dst_dir): + os.mkdir(dst_dir) + + copyfile(src_path, dst_path) + + +def configure(env, env_mono): + envvars = Variables() + envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) + envvars.Update(env) + + bits = env['bits'] + + tools_enabled = env['tools'] + mono_static = env['mono_static'] + copy_mono_root = env['copy_mono_root'] + + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + + if env['platform'] == 'windows': + mono_root = '' + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + print('Found Mono root directory: ' + mono_root) + + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env_mono, mono_version) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + + if env.msvc: + mono_static_lib_name = 'libmono-static-sgen' + else: + mono_static_lib_name = 'libmonosgen-2.0' + + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) + + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) + + env.Append(LIBS='psapi') + env.Append(LIBS='version') + else: + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + + copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + else: + is_apple = (sys.platform == 'darwin' or "osxcross" in env) + + sharedlib_ext = '.dylib' if is_apple else '.so' + + mono_root = '' + mono_lib_path = '' + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + + if not mono_root and is_apple: + # Try with some known directories under OSX + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] + for hint_dir in hint_dirs: + if os.path.isdir(hint_dir): + mono_root = hint_dir + break + + # We can't use pkg-config to link mono statically, + # but we can still use it to find the mono root directory + if not mono_root and mono_static: + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) + if not mono_root: + raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') + + if mono_root: + print('Found Mono root directory: ' + mono_root) + + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env_mono, mono_version) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') + + if not mono_lib: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + env_mono.Append(CPPFLAGS=['-D_REENTRANT']) + + if mono_static: + mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') + + if is_apple: + env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) + else: + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + else: + env.Append(LIBS=[mono_lib]) + + if is_apple: + env.Append(LIBS=['iconv', 'pthread']) + else: + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) + + if not mono_static: + mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) + + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + else: + assert not mono_static + + # TODO: Add option to force using pkg-config + print('Mono root directory not found. Using pkg-config instead') + + mono_version = pkgconfig_try_find_mono_version() + configure_for_mono_version(env_mono, mono_version) + + env.ParseConfig('pkg-config monosgen-2 --libs') + env_mono.ParseConfig('pkg-config monosgen-2 --cflags') + + mono_lib_path = '' + mono_so_name = '' + + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found: + mono_lib_path = hint_dir + mono_so_name = name_found + break + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) + + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + + env.Append(LINKFLAGS='-rdynamic') + + if not tools_enabled: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + make_template_dir(env, mono_root) + + if copy_mono_root: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + if tools_enabled: + copy_mono_root_files(env, mono_root) + else: + print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") + + +def make_template_dir(env, mono_root): + from shutil import rmtree + + platform = env['platform'] + target = env['target'] + + template_dir_name = '' + + if platform in ['windows', 'osx', 'x11']: + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) + else: + assert False + + output_dir = Dir('#bin').abspath + template_dir = os.path.join(output_dir, template_dir_name) + + template_mono_root_dir = os.path.join(template_dir, 'Mono') + + if os.path.isdir(template_mono_root_dir): + rmtree(template_mono_root_dir) # Clean first + + # Copy etc/mono/ + + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) + + +def copy_mono_root_files(env, mono_root): + from glob import glob + from shutil import copy + from shutil import rmtree + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + output_dir = Dir('#bin').abspath + editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') + + if os.path.isdir(editor_mono_root_dir): + rmtree(editor_mono_root_dir) # Clean first + + # Copy etc/mono/ + + editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) + + # Copy framework assemblies + + mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') + + editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') + editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') + + if not os.path.isdir(editor_mono_framework_dir): + os.makedirs(editor_mono_framework_dir) + if not os.path.isdir(editor_mono_framework_facades_dir): + os.makedirs(editor_mono_framework_facades_dir) + + for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): + copy(assembly, editor_mono_framework_dir) + for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): + copy(assembly, editor_mono_framework_facades_dir) + + +def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): + from distutils.dir_util import copy_tree + from glob import glob + from shutil import copy + + if not os.path.isdir(target_mono_config_dir): + os.makedirs(target_mono_config_dir) + + mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') + if not os.path.isdir(mono_etc_dir): + mono_etc_dir = '' + etc_hint_dirs = [] + if platform != 'windows': + etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] + if 'MONO_CFG_DIR' in os.environ: + etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] + for etc_hint_dir in etc_hint_dirs: + if os.path.isdir(etc_hint_dir): + mono_etc_dir = etc_hint_dir + break + if not mono_etc_dir: + raise RuntimeError('Mono installation etc directory not found') + + copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) + copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) + copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) + copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) + + for file in glob(os.path.join(mono_etc_dir, '*')): + if os.path.isfile(file): + copy(file, target_mono_config_dir) + + +def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): + from shutil import copy + + if platform == 'windows': + target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') + + if not os.path.isdir(target_mono_bin_dir): + os.makedirs(target_mono_bin_dir) + + copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) + else: + target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') + + if not os.path.isdir(target_mono_lib_dir): + os.makedirs(target_mono_lib_dir) + + if platform == 'osx': + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) + elif platform == 'x11': + copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) + + +def configure_for_mono_version(env, mono_version): + if mono_version is None: + raise RuntimeError('Mono JIT compiler version not found') + print('Found Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion('5.12.0'): + env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) + + +def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): + return os.path.join(hint_dir, '..') + return '' + + +def pkgconfig_try_find_mono_version(): + from compat import decode_utf8 + + lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() + greater_version = None + for line in lines: + try: + version = LooseVersion(decode_utf8(line)) + if greater_version is None or version > greater_version: + greater_version = version + except ValueError: + pass + return greater_version + + +def mono_root_try_find_mono_version(mono_root): + from compat import decode_utf8 + + mono_bin = os.path.join(mono_root, 'bin') + if os.path.isfile(os.path.join(mono_bin, 'mono')): + mono_binary = os.path.join(mono_bin, 'mono') + elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): + mono_binary = os.path.join(mono_bin, 'mono.exe') + else: + return None + output = subprocess.check_output([mono_binary, '--version']) + first_line = decode_utf8(output.splitlines()[0]) + try: + return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) + except (ValueError, IndexError): + return None diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index 583708bf07..583708bf07 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py diff --git a/modules/mono/tls_configure.py b/modules/mono/build_scripts/tls_configure.py index 622280b00b..622280b00b 100644 --- a/modules/mono/tls_configure.py +++ b/modules/mono/build_scripts/tls_configure.py diff --git a/modules/mono/config.py b/modules/mono/config.py index 6b88600c56..3b2e96765e 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,25 +1,12 @@ - -import imp -import os -import sys -import subprocess - -from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, Variables - - -monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') - - def can_build(env, platform): if platform in ['javascript']: return False # Not yet supported return True -def is_enabled(): - # The module is disabled by default. Use module_mono_enabled=yes to enable it. - return False +def configure(env): + env.use_ptrcall = True + env.add_module_version_string('mono') def get_doc_classes(): @@ -34,407 +21,6 @@ def get_doc_path(): return 'doc_classes' -def find_file_in_dir(directory, files, prefix='', extension=''): - if not extension.startswith('.'): - extension = '.' + extension - for curfile in files: - if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): - return curfile - return '' - - -def copy_file(src_dir, dst_dir, name): - from shutil import copyfile - - src_path = os.path.join(src_dir, name) - dst_path = os.path.join(dst_dir, name) - - if not os.path.isdir(dst_dir): - os.mkdir(dst_dir) - - copyfile(src_path, dst_path) - - -def configure(env): - env.use_ptrcall = True - env.add_module_version_string('mono') - - envvars = Variables() - envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) - envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) - envvars.Update(env) - - bits = env['bits'] - - tools_enabled = env['tools'] - mono_static = env['mono_static'] - copy_mono_root = env['copy_mono_root'] - - mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] - - if env['platform'] == 'windows': - mono_root = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - if mono_static: - lib_suffix = Environment()['LIBSUFFIX'] - - if env.msvc: - mono_static_lib_name = 'libmono-static-sgen' - else: - mono_static_lib_name = 'libmonosgen-2.0' - - if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): - raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) - - env.Append(LINKFLAGS='Mincore' + lib_suffix) - env.Append(LINKFLAGS='msvcrt' + lib_suffix) - env.Append(LINKFLAGS='LIBCMT' + lib_suffix) - env.Append(LINKFLAGS='Psapi' + lib_suffix) - else: - env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) - - env.Append(LIBS='psapi') - env.Append(LIBS='version') - else: - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') - - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) - else: - env.Append(LIBS=mono_lib_name) - - mono_bin_path = os.path.join(mono_root, 'bin') - - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - - copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') - else: - is_apple = (sys.platform == 'darwin' or "osxcross" in env) - - sharedlib_ext = '.dylib' if is_apple else '.so' - - mono_root = '' - mono_lib_path = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - - if not mono_root and is_apple: - # Try with some known directories under OSX - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] - for hint_dir in hint_dirs: - if os.path.isdir(hint_dir): - mono_root = hint_dir - break - - # We can't use pkg-config to link mono statically, - # but we can still use it to find the mono root directory - if not mono_root and mono_static: - mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) - if not mono_root: - raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') - - if mono_root: - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') - - if not mono_lib: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - env.Append(CPPFLAGS=['-D_REENTRANT']) - - if mono_static: - mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') - - if is_apple: - env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - else: - env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) - else: - env.Append(LIBS=[mono_lib]) - - if is_apple: - env.Append(LIBS=['iconv', 'pthread']) - else: - env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) - - if not mono_static: - mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - else: - assert not mono_static - - # TODO: Add option to force using pkg-config - print('Mono root directory not found. Using pkg-config instead') - - mono_version = pkgconfig_try_find_mono_version() - configure_for_mono_version(env, mono_version) - - env.ParseConfig('pkg-config monosgen-2 --cflags --libs') - - mono_lib_path = '' - mono_so_name = '' - - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found: - mono_lib_path = hint_dir - mono_so_name = name_found - break - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - - env.Append(LINKFLAGS='-rdynamic') - - if not tools_enabled: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - make_template_dir(env, mono_root) - - if copy_mono_root: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - if tools_enabled: - copy_mono_root_files(env, mono_root) - else: - print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") - - -def make_template_dir(env, mono_root): - from shutil import rmtree - - platform = env['platform'] - target = env['target'] - - template_dir_name = '' - - if platform in ['windows', 'osx', 'x11']: - template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) - else: - assert False - - output_dir = Dir('#bin').abspath - template_dir = os.path.join(output_dir, template_dir_name) - - template_mono_root_dir = os.path.join(template_dir, 'Mono') - - if os.path.isdir(template_mono_root_dir): - rmtree(template_mono_root_dir) # Clean first - - # Copy etc/mono/ - - template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) - - -def copy_mono_root_files(env, mono_root): - from glob import glob - from shutil import copy - from shutil import rmtree - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - output_dir = Dir('#bin').abspath - editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') - - if os.path.isdir(editor_mono_root_dir): - rmtree(editor_mono_root_dir) # Clean first - - # Copy etc/mono/ - - editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) - - # Copy framework assemblies - - mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') - - editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') - editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') - - if not os.path.isdir(editor_mono_framework_dir): - os.makedirs(editor_mono_framework_dir) - if not os.path.isdir(editor_mono_framework_facades_dir): - os.makedirs(editor_mono_framework_facades_dir) - - for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): - copy(assembly, editor_mono_framework_dir) - for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): - copy(assembly, editor_mono_framework_facades_dir) - - -def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): - from distutils.dir_util import copy_tree - from glob import glob - from shutil import copy - - if not os.path.isdir(target_mono_config_dir): - os.makedirs(target_mono_config_dir) - - mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') - if not os.path.isdir(mono_etc_dir): - mono_etc_dir = '' - etc_hint_dirs = [] - if platform != 'windows': - etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] - if 'MONO_CFG_DIR' in os.environ: - etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] - for etc_hint_dir in etc_hint_dirs: - if os.path.isdir(etc_hint_dir): - mono_etc_dir = etc_hint_dir - break - if not mono_etc_dir: - raise RuntimeError('Mono installation etc directory not found') - - copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) - copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) - copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) - copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) - - for file in glob(os.path.join(mono_etc_dir, '*')): - if os.path.isfile(file): - copy(file, target_mono_config_dir) - - -def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): - from shutil import copy - - if platform == 'windows': - target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') - - if not os.path.isdir(target_mono_bin_dir): - os.makedirs(target_mono_bin_dir) - - copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) - else: - target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') - - if not os.path.isdir(target_mono_lib_dir): - os.makedirs(target_mono_lib_dir) - - if platform == 'osx': - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) - elif platform == 'x11': - copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) - - -def configure_for_mono_version(env, mono_version): - if mono_version is None: - raise RuntimeError('Mono JIT compiler version not found') - print('Found Mono JIT compiler version: ' + str(mono_version)) - if mono_version >= LooseVersion('5.12.0'): - env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) - - -def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): - return os.path.join(hint_dir, '..') - return '' - - -def pkgconfig_try_find_mono_version(): - from compat import decode_utf8 - - lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() - greater_version = None - for line in lines: - try: - version = LooseVersion(decode_utf8(line)) - if greater_version is None or version > greater_version: - greater_version = version - except ValueError: - pass - return greater_version - - -def mono_root_try_find_mono_version(mono_root): - from compat import decode_utf8 - - mono_bin = os.path.join(mono_root, 'bin') - if os.path.isfile(os.path.join(mono_bin, 'mono')): - mono_binary = os.path.join(mono_bin, 'mono') - elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): - mono_binary = os.path.join(mono_bin, 'mono.exe') - else: - return None - output = subprocess.check_output([mono_binary, '--version']) - first_line = decode_utf8(output.splitlines()[0]) - try: - return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) - except (ValueError, IndexError): - return None +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 5f97069485..3c9644127c 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1424,6 +1424,34 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) { p_properties->push_back(E->value()); } + + // Call _get_property_list + + ERR_FAIL_COND(!script.is_valid()); + + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL(mono_object); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); + + if (method) { + MonoObject *ret = method->invoke(mono_object); + + if (ret) { + Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); + for (int i = 0, size = array.size(); i < size; i++) + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + return; + } + + break; + } + + top = top->get_parent_class(); + } } Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { @@ -3027,6 +3055,7 @@ CSharpLanguage::StringNameCache::StringNameCache() { _signal_callback = StaticCString::create("_signal_callback"); _set = StaticCString::create("_set"); _get = StaticCString::create("_get"); + _get_property_list = StaticCString::create("_get_property_list"); _notification = StaticCString::create("_notification"); _script_source = StaticCString::create("script/source"); dotctor = StaticCString::create(".ctor"); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 99877a4379..050527d52b 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -298,6 +298,7 @@ class CSharpLanguage : public ScriptLanguage { StringName _signal_callback; StringName _set; StringName _get; + StringName _get_property_list; StringName _notification; StringName _script_source; StringName dotctor; // .ctor diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 84e2303cf6..fe7ced060d 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -100,7 +100,7 @@ #define BINDINGS_GENERATOR_VERSION UINT32_C(8) -const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; +const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); bool BindingsGenerator::verbose_output = false; @@ -2520,7 +2520,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; - default: {} + default: { + } } if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 921b9f987b..9d42528927 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -502,11 +502,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String settings_hint_str = "Disabled"; -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) settings_hint_str += ",MonoDevelop,Visual Studio Code"; -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code"; -#elif UNIX_ENABLED +#elif defined(UNIX_ENABLED) settings_hint_str += ",MonoDevelop,Visual Studio Code"; #endif diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index cf0d2aec84..d9523c384c 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -81,15 +81,15 @@ public: enum ExternalEditor { EDITOR_NONE, -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) //EDITOR_VISUALSTUDIO, // TODO EDITOR_MONODEVELOP, EDITOR_VSCODE -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) EDITOR_VISUALSTUDIO_MAC, EDITOR_MONODEVELOP, EDITOR_VSCODE -#elif UNIX_ENABLED +#elif defined(UNIX_ENABLED) EDITOR_MONODEVELOP, EDITOR_VSCODE #endif diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index 5f5de12959..a064278237 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -261,6 +261,16 @@ namespace Godot return (real_t)Math.Sinh(s); } + public static real_t SmoothStep(real_t from, real_t to, real_t weight) + { + if (IsEqualApprox(from, to)) + { + return from; + } + real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + return x * x * (3 - 2 * x); + } + public static real_t Sqrt(real_t s) { return (real_t)Math.Sqrt(s); diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 73a3252fdb..908162ec45 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -132,6 +132,11 @@ namespace Godot (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } + public Vector2 DirectionTo(Vector2 b) + { + return new Vector2(b.x - x, b.y - y).Normalized(); + } + public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index f6ff27989d..0c96d346a9 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -126,6 +126,11 @@ namespace Godot ); } + public Vector3 DirectionTo(Vector3 b) + { + return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); + } + public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index bba7df2c6a..48bea4addf 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -212,7 +212,7 @@ void GDMono::initialize() { String config_dir; #ifdef TOOLS_ENABLED -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) mono_reg_info = MonoRegUtils::find_mono(); if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { @@ -222,7 +222,7 @@ void GDMono::initialize() { if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { config_dir = mono_reg_info.config_dir; } -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) const char *c_assembly_rootdir = mono_assembly_getrootdir(); const char *c_config_dir = mono_get_config_dir(); diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index 276db8830b..488cc2619a 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -39,7 +39,7 @@ #error Platform or compiler not supported #endif -#ifdef __GNUC__ +#if defined(__GNUC__) #ifdef HAVE_GCC___THREAD #define _THREAD_LOCAL_(m_t) __thread m_t @@ -47,7 +47,7 @@ #define USE_CUSTOM_THREAD_LOCAL #endif -#elif _MSC_VER +#elif defined(_MSC_VER) #ifdef HAVE_DECLSPEC_THREAD #define _THREAD_LOCAL_(m_t) __declspec(thread) m_t diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index d89839f06a..4f38357aa1 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -119,7 +119,8 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres case Image::FORMAT_RGBA5551: { dc = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub new file mode 100644 index 0000000000..11cd5f4743 --- /dev/null +++ b/modules/vhacd/SCsub @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_vhacd = env_modules.Clone() + +# Thirdparty source files + +thirdparty_dir = "#thirdparty/vhacd/" + +thirdparty_sources = [ +"src/vhacdManifoldMesh.cpp", +"src/FloatMath.cpp", +"src/vhacdMesh.cpp", +"src/vhacdICHull.cpp", +"src/vhacdVolume.cpp", +"src/VHACD-ASYNC.cpp", +"src/btAlignedAllocator.cpp", +"src/vhacdRaycastMesh.cpp", +"src/VHACD.cpp", +"src/btConvexHullComputer.cpp" +] + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_vhacd.Append(CPPPATH=[thirdparty_dir+"/inc"]) +env_vhacd.Append(CPPFLAGS=["-DGODOT_ENET"]) + +# upstream uses c++11 +if not env.msvc: + env_vhacd.Append(CCFLAGS="-std=c++11") + +env_thirdparty = env_vhacd.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +env_vhacd.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/vhacd/config.py b/modules/vhacd/config.py new file mode 100644 index 0000000000..9ced70d2fb --- /dev/null +++ b/modules/vhacd/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp new file mode 100644 index 0000000000..076a1738ab --- /dev/null +++ b/modules/vhacd/register_types.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "scene/resources/mesh.h" +#include "thirdparty/vhacd/public/VHACD.h" + +static Vector<Vector<Face3> > convex_decompose(const Vector<Face3> &p_faces) { + + Vector<float> 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; + } + } + + VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); + VHACD::IVHACD::Parameters params; + decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params); + + int hull_count = decomposer->GetNConvexHulls(); + + Vector<Vector<Face3> > ret; + + 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; + 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]; + } + } + triangles.write[j] = f; + } + ret.push_back(triangles); + } + + decomposer->Clean(); + decomposer->Release(); + + return ret; +} + +void register_vhacd_types() { + Mesh::convex_composition_function = convex_decompose; +} + +void unregister_vhacd_types() { + Mesh::convex_composition_function = NULL; +} diff --git a/modules/vhacd/register_types.h b/modules/vhacd/register_types.h new file mode 100644 index 0000000000..cb948faf44 --- /dev/null +++ b/modules/vhacd/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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. */ +/*************************************************************************/ + +void register_vhacd_types(); +void unregister_vhacd_types(); diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index dd14d60327..104a90059e 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -204,7 +204,14 @@ <constant name="COLORN" value="62" enum="BuiltinFunc"> Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc. </constant> - <constant name="FUNC_MAX" value="63" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc"> + Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [code]MATH_LERP[/code], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: + [codeblock] + var t = clamp((weight - from) / (to - from), 0.0, 1.0) + return t * t * (3.0 - 2.0 * t) + [/codeblock] + </constant> + <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> The maximum value the [member function] property can have. </constant> </constants> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 0d379205e4..d207656705 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -102,6 +102,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "var2bytes", "bytes2var", "color_named", + "smoothstep", }; VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String &p_string) { @@ -204,6 +205,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { return 2; case MATH_LERP: case MATH_INVERSE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -337,6 +339,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const else return PropertyInfo(Variant::REAL, "ostop"); } break; + case MATH_SMOOTHSTEP: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "weight"); + } break; case MATH_DECTIME: { if (p_idx == 0) return PropertyInfo(Variant::REAL, "value"); @@ -569,6 +579,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: { t = Variant::REAL; @@ -899,6 +910,12 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(4); *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); } break; + case VisualScriptBuiltinFunc::MATH_SMOOTHSTEP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case VisualScriptBuiltinFunc::MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -1270,7 +1287,8 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in *r_return = String(color); } break; - default: {} + default: { + } } } @@ -1379,6 +1397,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(VAR_TO_BYTES); BIND_ENUM_CONSTANT(BYTES_TO_VAR); BIND_ENUM_CONSTANT(COLORN); + BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -1433,6 +1452,7 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 2d8454ddd4..50854c16b1 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -101,6 +101,7 @@ public: VAR_TO_BYTES, BYTES_TO_VAR, COLORN, + MATH_SMOOTHSTEP, FUNC_MAX }; diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index a76e4bc36f..772092fabe 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -1032,7 +1032,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; - default: {}; + default: { + }; } if (op == Variant::OP_MAX) { //stop appending stuff diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index bb0435a679..8fa7d2c0d4 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1562,7 +1562,8 @@ public: case VisualScriptPropertySet::ASSIGN_OP_BIT_XOR: { value = Variant::evaluate(Variant::OP_BIT_XOR, value, p_argument); } break; - default: {} + default: { + } } if (index != StringName()) { |