feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,577 @@
/**************************************************************************/
/* collada.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef COLLADA_H
#define COLLADA_H
#include "core/config/project_settings.h"
#include "core/io/xml_parser.h"
#include "core/templates/rb_map.h"
#include "scene/resources/material.h"
class Collada {
public:
enum ImportFlags {
IMPORT_FLAG_SCENE = 1,
IMPORT_FLAG_ANIMATION = 2
};
struct Image {
String path;
};
struct Material {
String name;
String instance_effect;
};
struct Effect {
String name;
HashMap<String, Variant> params;
struct Channel {
int uv_idx = 0;
String texture;
Color color;
Channel() {}
};
Channel diffuse, specular, emission, bump;
float shininess = 40;
bool found_double_sided = false;
bool double_sided = true;
bool unshaded = false;
String get_texture_path(const String &p_source, Collada &p_state) const;
Effect() {
diffuse.color = Color(1, 1, 1, 1);
}
};
struct CameraData {
enum Mode {
MODE_PERSPECTIVE,
MODE_ORTHOGONAL
};
Mode mode = MODE_PERSPECTIVE;
union {
struct {
float x_fov = 0;
float y_fov = 0;
} perspective;
struct {
float x_mag = 0;
float y_mag = 0;
} orthogonal;
};
float aspect = 1;
float z_near = 0.05;
float z_far = 4000;
CameraData() {}
};
struct LightData {
enum Mode {
MODE_AMBIENT,
MODE_DIRECTIONAL,
MODE_OMNI,
MODE_SPOT
};
Mode mode = MODE_AMBIENT;
Color color = Color(1, 1, 1, 1);
float constant_att = 0;
float linear_att = 0;
float quad_att = 0;
float spot_angle = 45;
float spot_exp = 1;
LightData() {}
};
struct MeshData {
String name;
struct Source {
Vector<float> array;
int stride = 0;
};
HashMap<String, Source> sources;
struct Vertices {
HashMap<String, String> sources;
};
HashMap<String, Vertices> vertices;
struct Primitives {
struct SourceRef {
String source;
int offset = 0;
};
String material;
HashMap<String, SourceRef> sources;
Vector<float> polygons;
Vector<float> indices;
int count = 0;
int vertex_size = 0;
};
Vector<Primitives> primitives;
bool found_double_sided = false;
bool double_sided = true;
MeshData() {}
};
struct CurveData {
String name;
bool closed = false;
struct Source {
Vector<String> sarray;
Vector<float> array;
int stride = 0;
};
HashMap<String, Source> sources;
HashMap<String, String> control_vertices;
CurveData() {}
};
struct SkinControllerData {
String base;
bool use_idrefs = false;
Transform3D bind_shape;
struct Source {
Vector<String> sarray; //maybe for names
Vector<float> array;
int stride = 1;
Source() {}
};
HashMap<String, Source> sources;
struct Joints {
HashMap<String, String> sources;
} joints;
struct Weights {
struct SourceRef {
String source;
int offset = 0;
};
String material;
HashMap<String, SourceRef> sources;
Vector<float> sets;
Vector<float> indices;
int count = 0;
} weights;
HashMap<String, Transform3D> bone_rest_map;
SkinControllerData() {}
};
struct MorphControllerData {
String mesh;
String mode;
struct Source {
int stride = 1;
Vector<String> sarray; //maybe for names
Vector<float> array;
Source() {}
};
HashMap<String, Source> sources;
HashMap<String, String> targets;
MorphControllerData() {}
};
struct Vertex {
int idx = 0;
Vector3 vertex;
Vector3 normal;
Vector3 uv;
Vector3 uv2;
Plane tangent;
Color color;
int uid = 0;
struct Weight {
int bone_idx = 0;
float weight = 0;
bool operator<(const Weight w) const { return weight > w.weight; } //heaviest first
};
Vector<Weight> weights;
void fix_weights() {
weights.sort();
if (weights.size() > 4) {
//cap to 4 and make weights add up 1
weights.resize(4);
float total = 0;
for (int i = 0; i < 4; i++) {
total += weights[i].weight;
}
if (total) {
for (int i = 0; i < 4; i++) {
weights.write[i].weight /= total;
}
}
}
}
void fix_unit_scale(const Collada &p_state);
bool operator<(const Vertex &p_vert) const {
if (uid == p_vert.uid) {
if (vertex == p_vert.vertex) {
if (normal == p_vert.normal) {
if (uv == p_vert.uv) {
if (uv2 == p_vert.uv2) {
if (!weights.is_empty() || !p_vert.weights.is_empty()) {
if (weights.size() == p_vert.weights.size()) {
for (int i = 0; i < weights.size(); i++) {
if (weights[i].bone_idx != p_vert.weights[i].bone_idx) {
return weights[i].bone_idx < p_vert.weights[i].bone_idx;
}
if (weights[i].weight != p_vert.weights[i].weight) {
return weights[i].weight < p_vert.weights[i].weight;
}
}
} else {
return weights.size() < p_vert.weights.size();
}
}
return (color < p_vert.color);
} else {
return (uv2 < p_vert.uv2);
}
} else {
return (uv < p_vert.uv);
}
} else {
return (normal < p_vert.normal);
}
} else {
return vertex < p_vert.vertex;
}
} else {
return uid < p_vert.uid;
}
}
Vertex() {}
};
struct Node {
enum Type {
TYPE_NODE,
TYPE_JOINT,
TYPE_SKELETON, //this bone is not collada, it's added afterwards as optimization
TYPE_LIGHT,
TYPE_CAMERA,
TYPE_GEOMETRY
};
struct XForm {
enum Op {
OP_ROTATE,
OP_SCALE,
OP_TRANSLATE,
OP_MATRIX,
OP_VISIBILITY
};
String id;
Op op = OP_ROTATE;
Vector<float> data;
};
Type type = TYPE_NODE;
String name;
String id;
String empty_draw_type;
bool noname = false;
Vector<XForm> xform_list;
Transform3D default_transform;
Transform3D post_transform;
Vector<Node *> children;
Node *parent = nullptr;
Transform3D compute_transform(const Collada &p_state) const;
Transform3D get_global_transform() const;
Transform3D get_transform() const;
bool ignore_anim = false;
Node() {}
virtual ~Node() {
for (int i = 0; i < children.size(); i++) {
memdelete(children[i]);
}
};
};
struct NodeSkeleton : public Node {
NodeSkeleton() { type = TYPE_SKELETON; }
};
struct NodeJoint : public Node {
NodeSkeleton *owner = nullptr;
String sid;
NodeJoint() {
type = TYPE_JOINT;
}
};
struct NodeGeometry : public Node {
bool controller = false;
String source;
struct Material {
String target;
};
HashMap<String, Material> material_map;
Vector<String> skeletons;
NodeGeometry() { type = TYPE_GEOMETRY; }
};
struct NodeCamera : public Node {
String camera;
NodeCamera() { type = TYPE_CAMERA; }
};
struct NodeLight : public Node {
String light;
NodeLight() { type = TYPE_LIGHT; }
};
struct VisualScene {
String name;
Vector<Node *> root_nodes;
~VisualScene() {
for (int i = 0; i < root_nodes.size(); i++) {
memdelete(root_nodes[i]);
}
}
};
struct AnimationClip {
String name;
float begin = 0;
float end = 1;
Vector<String> tracks;
AnimationClip() {}
};
struct AnimationTrack {
String id;
String target;
String param;
String component;
bool property = false;
enum InterpolationType {
INTERP_LINEAR,
INTERP_BEZIER
};
struct Key {
enum Type {
TYPE_FLOAT,
TYPE_MATRIX
};
float time = 0;
Vector<float> data;
Point2 in_tangent;
Point2 out_tangent;
InterpolationType interp_type = INTERP_LINEAR;
Key() {}
};
Vector<float> get_value_at_time(float p_time) const;
Vector<Key> keys;
AnimationTrack() {}
};
/****************/
/* IMPORT STATE */
/****************/
struct State {
int import_flags = 0;
float unit_scale = 1.0;
Vector3::Axis up_axis = Vector3::AXIS_Y;
bool z_up = false;
struct Version {
int major = 0, minor = 0, rev = 0;
bool operator<(const Version &p_ver) const { return (major == p_ver.major) ? ((minor == p_ver.minor) ? (rev < p_ver.rev) : minor < p_ver.minor) : major < p_ver.major; }
Version(int p_major = 0, int p_minor = 0, int p_rev = 0) {
major = p_major;
minor = p_minor;
rev = p_rev;
}
} version;
HashMap<String, CameraData> camera_data_map;
HashMap<String, MeshData> mesh_data_map;
HashMap<String, LightData> light_data_map;
HashMap<String, CurveData> curve_data_map;
HashMap<String, String> mesh_name_map;
HashMap<String, String> morph_name_map;
HashMap<String, String> morph_ownership_map;
HashMap<String, SkinControllerData> skin_controller_data_map;
HashMap<String, MorphControllerData> morph_controller_data_map;
HashMap<String, Image> image_map;
HashMap<String, Material> material_map;
HashMap<String, Effect> effect_map;
HashMap<String, VisualScene> visual_scene_map;
HashMap<String, Node *> scene_map;
HashSet<String> idref_joints;
HashMap<String, String> sid_to_node_map;
//RBMap<String,NodeJoint*> bone_map;
HashMap<String, Transform3D> bone_rest_map;
String local_path;
String root_visual_scene;
String root_physics_scene;
Vector<AnimationClip> animation_clips;
Vector<AnimationTrack> animation_tracks;
HashMap<String, Vector<int>> referenced_tracks;
HashMap<String, Vector<int>> by_id_tracks;
float animation_length = 0;
State() {}
} state;
Error load(const String &p_path, int p_flags = 0);
Collada();
Transform3D fix_transform(const Transform3D &p_transform);
Transform3D get_root_transform() const;
int get_uv_channel(const String &p_name);
private: // private stuff
HashMap<String, int> channel_map;
void _parse_asset(XMLParser &p_parser);
void _parse_image(XMLParser &p_parser);
void _parse_material(XMLParser &p_parser);
void _parse_effect_material(XMLParser &p_parser, Effect &p_effect, String &p_id);
void _parse_effect(XMLParser &p_parser);
void _parse_camera(XMLParser &p_parser);
void _parse_light(XMLParser &p_parser);
void _parse_animation_clip(XMLParser &p_parser);
void _parse_mesh_geometry(XMLParser &p_parser, const String &p_id, const String &p_name);
void _parse_curve_geometry(XMLParser &p_parser, const String &p_id, const String &p_name);
void _parse_skin_controller(XMLParser &p_parser, const String &p_id);
void _parse_morph_controller(XMLParser &p_parser, const String &p_id);
void _parse_controller(XMLParser &p_parser);
Node *_parse_visual_instance_geometry(XMLParser &p_parser);
Node *_parse_visual_instance_camera(XMLParser &p_parser);
Node *_parse_visual_instance_light(XMLParser &p_parser);
Node *_parse_visual_node_instance_data(XMLParser &p_parser);
Node *_parse_visual_scene_node(XMLParser &p_parser);
void _parse_visual_scene(XMLParser &p_parser);
void _parse_animation(XMLParser &p_parser);
void _parse_scene(XMLParser &p_parser);
void _parse_library(XMLParser &p_parser);
Variant _parse_param(XMLParser &p_parser);
Vector<float> _read_float_array(XMLParser &p_parser);
Vector<String> _read_string_array(XMLParser &p_parser);
Transform3D _read_transform(XMLParser &p_parser);
String _read_empty_draw_type(XMLParser &p_parser);
void _joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner);
void _create_skeletons(Collada::Node **p_node, NodeSkeleton *p_skeleton = nullptr);
void _find_morph_nodes(VisualScene *p_vscene, Node *p_node);
bool _remove_node(Node *p_parent, Node *p_node);
void _remove_node(VisualScene *p_vscene, Node *p_node);
void _merge_skeletons2(VisualScene *p_vscene);
void _merge_skeletons(VisualScene *p_vscene, Node *p_node);
bool _optimize_skeletons(VisualScene *p_vscene, Node *p_node);
bool _move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, List<Node *> *p_mgeom);
void _optimize();
};
#endif // COLLADA_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
/**************************************************************************/
/* editor_import_collada.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_IMPORT_COLLADA_H
#define EDITOR_IMPORT_COLLADA_H
#include "editor/import/3d/resource_importer_scene.h"
class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter {
GDCLASS(EditorSceneFormatImporterCollada, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override;
EditorSceneFormatImporterCollada();
};
#endif // EDITOR_IMPORT_COLLADA_H

View file

@ -0,0 +1,251 @@
/**************************************************************************/
/* post_import_plugin_skeleton_renamer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "post_import_plugin_skeleton_renamer.h"
#include "editor/import/3d/scene_import_settings.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/bone_map.h"
void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name"), "GeneralSkeleton"));
}
}
void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, const HashMap<String, String> &p_rename_map) {
// Prepare objects.
Object *map = p_options["retarget/bone_map"].get_validated_object();
if (!map || !bool(p_options["retarget/bone_renamer/rename_bones"])) {
return;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
if (skeleton) {
// Rename bones in Skeleton3D.
int len = skeleton->get_bone_count();
for (int i = 0; i < len; i++) {
String current_bone_name = skeleton->get_bone_name(i);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);
if (new_bone_name) {
skeleton->set_bone_name(i, new_bone_name->value);
}
}
}
// Rename bones in Skin.
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
while (nodes.size()) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
Ref<Skin> skin = mi->get_skin();
if (skin.is_valid()) {
Node *node = mi->get_node(mi->get_skeleton_path());
if (node) {
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
if (mesh_skeleton && node == skeleton) {
int len = skin->get_bind_count();
for (int i = 0; i < len; i++) {
String current_bone_name = skin->get_bind_name(i);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);
if (new_bone_name) {
skin->set_bind_name(i, new_bone_name->value);
}
}
}
}
}
}
}
// Rename bones in AnimationPlayer.
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int len = anim->get_track_count();
for (int i = 0; i < len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
continue;
}
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
if (node) {
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (track_skeleton && track_skeleton == skeleton) {
String current_bone_name = anim->track_get_path(i).get_subname(0);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);
if (new_bone_name) {
String new_track_path = track_path + ":" + new_bone_name->value;
anim->track_set_path(i, new_track_path);
}
}
}
}
}
}
}
// Rename bones in all Nodes by calling method.
{
Dictionary rename_map_dict;
for (HashMap<String, String>::ConstIterator E = p_rename_map.begin(); E; ++E) {
rename_map_dict[E->key] = E->value;
}
TypedArray<Node> nodes = p_base_scene->find_children("*", "BoneAttachment3D");
while (nodes.size()) {
BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(nodes.pop_back());
if (attachment) {
attachment->notify_skeleton_bones_renamed(p_base_scene, skeleton, rename_map_dict);
}
}
}
}
void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
// Prepare objects.
Object *map = p_options["retarget/bone_map"].get_validated_object();
if (!map || !bool(p_options["retarget/bone_renamer/rename_bones"])) {
return;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
int len = skeleton->get_bone_count();
// First, prepare main rename map.
HashMap<String, String> main_rename_map;
for (int i = 0; i < len; i++) {
String bone_name = skeleton->get_bone_name(i);
String target_name = bone_map->find_profile_bone_name(bone_name);
if (target_name.is_empty()) {
continue;
}
main_rename_map.insert(bone_name, target_name);
}
// Preprocess of renaming bones to avoid to conflict with original bone name.
HashMap<String, String> pre_rename_map; // HashMap<skeleton bone name, target(profile) bone name>
{
Vector<String> solved_name_stack;
for (int i = 0; i < len; i++) {
String bone_name = skeleton->get_bone_name(i);
String target_name = bone_map->find_profile_bone_name(bone_name);
if (target_name.is_empty() || bone_name == target_name || skeleton->find_bone(target_name) == -1) {
continue; // No conflicting.
}
// Solve conflicting.
Ref<SkeletonProfile> profile = bone_map->get_profile();
String solved_name = target_name;
for (int j = 2; skeleton->find_bone(solved_name) >= 0 || profile->find_bone(solved_name) >= 0 || solved_name_stack.has(solved_name); j++) {
solved_name = target_name + itos(j);
}
solved_name_stack.push_back(solved_name);
pre_rename_map.insert(target_name, solved_name);
}
_internal_process(p_category, p_base_scene, p_node, p_resource, p_options, pre_rename_map);
}
// Main process of renaming bones.
{
// Apply pre-renaming result to prepared main rename map.
Vector<String> remove_queue;
for (HashMap<String, String>::Iterator E = main_rename_map.begin(); E; ++E) {
if (pre_rename_map.has(E->key)) {
remove_queue.push_back(E->key);
}
}
for (int i = 0; i < remove_queue.size(); i++) {
main_rename_map.insert(pre_rename_map[remove_queue[i]], main_rename_map[remove_queue[i]]);
main_rename_map.erase(remove_queue[i]);
}
_internal_process(p_category, p_base_scene, p_node, p_resource, p_options, main_rename_map);
}
// Make unique skeleton.
if (bool(p_options["retarget/bone_renamer/unique_node/make_unique"])) {
String unique_name = String(p_options["retarget/bone_renamer/unique_node/skeleton_name"]);
ERR_FAIL_COND_MSG(unique_name.is_empty(), "Skeleton unique name cannot be empty.");
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
for (int i = 0; i < track_len; i++) {
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *orig_node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
while (node) {
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (track_skeleton && track_skeleton == skeleton) {
if (node == orig_node) {
if (anim->track_get_path(i).get_subname_count() > 0) {
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + String(":") + anim->track_get_path(i).get_concatenated_subnames());
} else {
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name);
}
} else {
if (anim->track_get_path(i).get_subname_count() > 0) {
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node) + String(":") + anim->track_get_path(i).get_concatenated_subnames());
} else {
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node));
}
}
break;
}
node = node->get_parent();
}
}
}
}
skeleton->set_name(unique_name);
skeleton->set_unique_name_in_owner(true);
}
}
}
PostImportPluginSkeletonRenamer::PostImportPluginSkeletonRenamer() {
}

View file

@ -0,0 +1,48 @@
/**************************************************************************/
/* post_import_plugin_skeleton_renamer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef POST_IMPORT_PLUGIN_SKELETON_RENAMER_H
#define POST_IMPORT_PLUGIN_SKELETON_RENAMER_H
#include "resource_importer_scene.h"
class PostImportPluginSkeletonRenamer : public EditorScenePostImportPlugin {
GDCLASS(PostImportPluginSkeletonRenamer, EditorScenePostImportPlugin);
public:
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override;
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override;
void _internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, const HashMap<String, String> &p_rename_map);
PostImportPluginSkeletonRenamer();
};
#endif // POST_IMPORT_PLUGIN_SKELETON_RENAMER_H

View file

@ -0,0 +1,777 @@
/**************************************************************************/
/* post_import_plugin_skeleton_rest_fixer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "post_import_plugin_skeleton_rest_fixer.h"
#include "editor/import/3d/scene_import_settings.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/bone_map.h"
void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/keep_global_rest_on_leftovers"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
// get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
// r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::STRING_NAME, PROPERTY_HINT_ENUM, "Hips,Spine,Chest")), Array()));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter", PROPERTY_HINT_ARRAY_TYPE, "StringName"), Array()));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold"), 15));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/base_height_adjustment", PROPERTY_HINT_RANGE, "-1,1,0.01"), 0.0));
}
}
Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
if (p_option.begins_with("retarget/rest_fixer/fix_silhouette/")) {
if (!bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
if (!p_option.ends_with("enable")) {
return false;
}
}
} else if (p_option == "retarget/rest_fixer/keep_global_rest_on_leftovers") {
return bool(p_options["retarget/rest_fixer/overwrite_axis"]);
}
}
return true;
}
void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
// Prepare objects.
Object *map = p_options["retarget/bone_map"].get_validated_object();
if (!map) {
return;
}
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
Ref<SkeletonProfile> profile = bone_map->get_profile();
if (!profile.is_valid()) {
return;
}
Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node);
if (!src_skeleton) {
return;
}
bool is_renamed = bool(p_options["retarget/bone_renamer/rename_bones"]);
Array filter = p_options["retarget/rest_fixer/fix_silhouette/filter"];
bool is_rest_changed = false;
// Build profile skeleton.
Skeleton3D *prof_skeleton = memnew(Skeleton3D);
{
int prof_bone_len = profile->get_bone_size();
// Add single bones.
for (int i = 0; i < prof_bone_len; i++) {
prof_skeleton->add_bone(profile->get_bone_name(i));
prof_skeleton->set_bone_rest(i, profile->get_reference_pose(i));
}
// Set parents.
for (int i = 0; i < prof_bone_len; i++) {
int parent = profile->find_bone(profile->get_bone_parent(i));
if (parent >= 0) {
prof_skeleton->set_bone_parent(i, parent);
}
}
}
// Get global transform.
Transform3D global_transform;
if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) {
Node *pr = src_skeleton;
while (pr) {
Node3D *pr3d = Object::cast_to<Node3D>(pr);
if (pr3d) {
global_transform = pr3d->get_transform() * global_transform;
pr3d->set_transform(Transform3D());
}
pr = pr->get_parent();
}
global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin.
}
// Apply node transforms.
if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) {
Vector3 scl = global_transform.basis.get_scale_global();
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
for (int i = 0; i < bones_to_process.size(); i++) {
src_skeleton->set_bone_rest(bones_to_process[i], global_transform.orthonormalized() * src_skeleton->get_bone_rest(bones_to_process[i]));
src_skeleton->set_bone_pose_position(bones_to_process[i], global_transform.orthonormalized().xform(src_skeleton->get_bone_pose_position(bones_to_process[i])));
src_skeleton->set_bone_pose_rotation(bones_to_process[i], global_transform.basis.get_rotation_quaternion() * src_skeleton->get_bone_pose_rotation(bones_to_process[i]));
src_skeleton->set_bone_pose_scale(bones_to_process[i], (global_transform.orthonormalized().basis * Basis().scaled(src_skeleton->get_bone_pose_scale((bones_to_process[i])))).get_scale());
}
while (bones_to_process.size() > 0) {
int src_idx = bones_to_process[0];
bones_to_process.erase(src_idx);
Vector<int> src_children = src_skeleton->get_bone_children(src_idx);
for (int i = 0; i < src_children.size(); i++) {
bones_to_process.push_back(src_children[i]);
}
src_skeleton->set_bone_rest(src_idx, Transform3D(src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin * scl));
src_skeleton->set_bone_pose_position(src_idx, src_skeleton->get_bone_pose_position(src_idx) * scl);
}
// Fix animation.
bones_to_process = src_skeleton->get_parentless_bones();
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
for (int i = 0; i < track_len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
continue;
}
if (anim->track_is_compressed(i)) {
continue; // Shouldn't occur in internal_process().
}
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (!track_skeleton || track_skeleton != src_skeleton) {
continue;
}
StringName bn = anim->track_get_path(i).get_subname(0);
if (!bn) {
continue;
}
int bone_idx = src_skeleton->find_bone(bn);
int key_len = anim->track_get_key_count(i);
if (anim->track_get_type(i) == Animation::TYPE_POSITION_3D) {
if (bones_to_process.has(bone_idx)) {
for (int j = 0; j < key_len; j++) {
Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, global_transform.basis.xform(ps) + global_transform.origin);
}
} else {
for (int j = 0; j < key_len; j++) {
Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, ps * scl);
}
}
} else if (bones_to_process.has(bone_idx)) {
if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
for (int j = 0; j < key_len; j++) {
Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, global_transform.basis.get_rotation_quaternion() * qt);
}
} else {
for (int j = 0; j < key_len; j++) {
Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j)));
anim->track_set_key_value(i, j, (global_transform.orthonormalized().basis * sc).get_scale());
}
}
}
}
}
}
}
is_rest_changed = true;
}
// Complement Rotation track for compatibility between different rests.
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
// Detect does the animation have skeleton's TRS track.
String track_path;
bool found_skeleton = false;
for (int i = 0; i < track_len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
continue;
}
track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
if (node) {
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (track_skeleton && track_skeleton == src_skeleton) {
found_skeleton = true;
break;
}
}
}
if (!found_skeleton) {
continue;
}
// Search and insert rot track if it doesn't exist.
for (int prof_idx = 0; prof_idx < prof_skeleton->get_bone_count(); prof_idx++) {
String bone_name = is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx)));
if (bone_name.is_empty()) {
continue;
}
int src_idx = src_skeleton->find_bone(bone_name);
if (src_idx == -1) {
continue;
}
String insert_path = track_path + ":" + bone_name;
int rot_track = anim->find_track(insert_path, Animation::TYPE_ROTATION_3D);
if (rot_track == -1) {
int track = anim->add_track(Animation::TYPE_ROTATION_3D);
anim->track_set_path(track, insert_path);
anim->track_set_imported(track, true);
anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion());
}
}
}
}
}
// Fix silhouette.
Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis.
silhouette_diff.resize(src_skeleton->get_bone_count());
Transform3D *silhouette_diff_w = silhouette_diff.ptrw();
LocalVector<Transform3D> pre_silhouette_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
pre_silhouette_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
Vector<int> bones_to_process = prof_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {
int prof_idx = bones_to_process[0];
bones_to_process.erase(prof_idx);
Vector<int> prof_children = prof_skeleton->get_bone_children(prof_idx);
for (int i = 0; i < prof_children.size(); i++) {
bones_to_process.push_back(prof_children[i]);
}
// Calc virtual/looking direction with origins.
bool is_filtered = false;
for (int i = 0; i < filter.size(); i++) {
if (String(filter[i]) == prof_skeleton->get_bone_name(prof_idx)) {
is_filtered = true;
break;
}
}
if (is_filtered) {
continue;
}
int src_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx))));
if (src_idx < 0 || profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_END) {
continue;
}
Vector3 prof_tail;
Vector3 src_tail;
if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_AVERAGE_CHILDREN) {
PackedInt32Array prof_bone_children = prof_skeleton->get_bone_children(prof_idx);
int children_size = prof_bone_children.size();
if (children_size == 0) {
continue;
}
bool exist_all_children = true;
for (int i = 0; i < children_size; i++) {
int prof_child_idx = prof_bone_children[i];
int src_child_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_child_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_child_idx))));
if (src_child_idx < 0) {
exist_all_children = false;
break;
}
prof_tail = prof_tail + prof_skeleton->get_bone_global_rest(prof_child_idx).origin;
src_tail = src_tail + src_skeleton->get_bone_global_rest(src_child_idx).origin;
}
if (!exist_all_children) {
continue;
}
prof_tail = prof_tail / children_size;
src_tail = src_tail / children_size;
}
if (profile->get_tail_direction(prof_idx) == SkeletonProfile::TAIL_DIRECTION_SPECIFIC_CHILD) {
int prof_tail_idx = prof_skeleton->find_bone(profile->get_bone_tail(prof_idx));
if (prof_tail_idx < 0) {
continue;
}
int src_tail_idx = src_skeleton->find_bone(is_renamed ? prof_skeleton->get_bone_name(prof_tail_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_tail_idx))));
if (src_tail_idx < 0) {
continue;
}
prof_tail = prof_skeleton->get_bone_global_rest(prof_tail_idx).origin;
src_tail = src_skeleton->get_bone_global_rest(src_tail_idx).origin;
}
Vector3 prof_head = prof_skeleton->get_bone_global_rest(prof_idx).origin;
Vector3 src_head = src_skeleton->get_bone_global_rest(src_idx).origin;
Vector3 prof_dir = prof_tail - prof_head;
Vector3 src_dir = src_tail - src_head;
// Rotate rest.
if (Math::abs(Math::rad_to_deg(src_dir.angle_to(prof_dir))) > float(p_options["retarget/rest_fixer/fix_silhouette/threshold"])) {
// Get rotation difference.
Vector3 up_vec; // Need to rotate other than roll axis.
switch (Vector3(abs(src_dir.x), abs(src_dir.y), abs(src_dir.z)).min_axis_index()) {
case Vector3::AXIS_X: {
up_vec = Vector3(1, 0, 0);
} break;
case Vector3::AXIS_Y: {
up_vec = Vector3(0, 1, 0);
} break;
case Vector3::AXIS_Z: {
up_vec = Vector3(0, 0, 1);
} break;
}
Basis src_b;
src_b = src_b.looking_at(src_dir, up_vec);
Basis prof_b;
prof_b = src_b.looking_at(prof_dir, up_vec);
if (prof_b.is_equal_approx(Basis())) {
continue; // May not need to rotate.
}
Basis diff_b = prof_b * src_b.inverse();
// Apply rotation difference as global transform to skeleton.
Basis src_pg;
int src_parent = src_skeleton->get_bone_parent(src_idx);
if (src_parent >= 0) {
src_pg = src_skeleton->get_bone_global_rest(src_parent).basis;
}
Transform3D fixed_rest = Transform3D(src_pg.inverse() * diff_b * src_pg * src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin);
src_skeleton->set_bone_rest(src_idx, fixed_rest);
}
}
// Adjust scale base bone height.
float base_adjustment = float(p_options["retarget/rest_fixer/fix_silhouette/base_height_adjustment"]);
if (!Math::is_zero_approx(base_adjustment)) {
StringName scale_base_bone_name = profile->get_scale_base_bone();
int src_bone_idx = src_skeleton->find_bone(scale_base_bone_name);
Transform3D src_rest = src_skeleton->get_bone_rest(src_bone_idx);
src_skeleton->set_bone_rest(src_bone_idx, Transform3D(src_rest.basis, Vector3(src_rest.origin.x, src_rest.origin.y + base_adjustment, src_rest.origin.z)));
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
for (int i = 0; i < track_len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) {
continue;
}
if (anim->track_is_compressed(i)) {
continue; // Shouldn't occur in internal_process().
}
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (!track_skeleton || track_skeleton != src_skeleton) {
continue;
}
StringName bn = anim->track_get_path(i).get_concatenated_subnames();
if (bn != scale_base_bone_name) {
continue;
}
int key_len = anim->track_get_key_count(i);
for (int j = 0; j < key_len; j++) {
Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j));
pos.y += base_adjustment;
anim->track_set_key_value(i, j, pos);
}
}
}
}
}
// For skin modification in overwrite rest.
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
silhouette_diff_w[i] = pre_silhouette_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).affine_inverse();
}
is_rest_changed = true;
}
// Set motion scale to Skeleton if normalize position tracks.
if (bool(p_options["retarget/rest_fixer/normalize_position_tracks"])) {
int src_bone_idx = src_skeleton->find_bone(profile->get_scale_base_bone());
if (src_bone_idx >= 0) {
real_t motion_scale = abs(src_skeleton->get_bone_global_rest(src_bone_idx).origin.y);
if (motion_scale > 0) {
src_skeleton->set_motion_scale(motion_scale);
}
}
}
// Overwrite axis.
if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) {
LocalVector<Transform3D> old_skeleton_rest;
LocalVector<Transform3D> old_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
old_skeleton_rest.push_back(src_skeleton->get_bone_rest(i));
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
bool keep_global_rest_leftovers = bool(p_options["retarget/rest_fixer/keep_global_rest_on_leftovers"]);
// Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants.
Vector<int> keep_bone_rest;
if (keep_global_rest_leftovers) {
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {
int src_idx = bones_to_process[0];
bones_to_process.erase(src_idx);
Vector<int> src_children = src_skeleton->get_bone_children(src_idx);
for (const int &src_child : src_children) {
bones_to_process.push_back(src_child);
}
StringName src_bone_name = is_renamed ? StringName(src_skeleton->get_bone_name(src_idx)) : bone_map->find_profile_bone_name(src_skeleton->get_bone_name(src_idx));
if (src_bone_name != StringName() && !profile->has_bone(src_bone_name)) {
// Scan descendants for mapped bones.
bool found_mapped = false;
Vector<int> decendants_to_process = src_skeleton->get_bone_children(src_idx);
while (decendants_to_process.size() > 0) {
int desc_idx = decendants_to_process[0];
decendants_to_process.erase(desc_idx);
Vector<int> desc_children = src_skeleton->get_bone_children(desc_idx);
for (const int &desc_child : desc_children) {
decendants_to_process.push_back(desc_child);
}
StringName desc_bone_name = is_renamed ? StringName(src_skeleton->get_bone_name(desc_idx)) : bone_map->find_profile_bone_name(src_skeleton->get_bone_name(desc_idx));
if (desc_bone_name != StringName() && profile->has_bone(desc_bone_name)) {
found_mapped = true;
break;
}
}
if (!found_mapped) {
keep_bone_rest.push_back(src_idx); // No mapped descendants. Add to whitelist.
}
}
}
}
Vector<Basis> diffs;
diffs.resize(src_skeleton->get_bone_count());
Basis *diffs_w = diffs.ptrw();
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {
int src_idx = bones_to_process[0];
bones_to_process.erase(src_idx);
Vector<int> src_children = src_skeleton->get_bone_children(src_idx);
for (int i = 0; i < src_children.size(); i++) {
bones_to_process.push_back(src_children[i]);
}
Basis tgt_rot;
StringName src_bone_name = is_renamed ? StringName(src_skeleton->get_bone_name(src_idx)) : bone_map->find_profile_bone_name(src_skeleton->get_bone_name(src_idx));
if (src_bone_name != StringName()) {
Basis src_pg;
int src_parent_idx = src_skeleton->get_bone_parent(src_idx);
if (src_parent_idx >= 0) {
src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis;
}
int prof_idx = profile->find_bone(src_bone_name);
if (prof_idx >= 0) {
tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
} else if (keep_global_rest_leftovers && keep_bone_rest.has(src_idx)) {
tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone without mapped children keeps global rest.
}
}
if (src_skeleton->get_bone_parent(src_idx) >= 0) {
diffs_w[src_idx] = tgt_rot.inverse() * diffs[src_skeleton->get_bone_parent(src_idx)] * src_skeleton->get_bone_rest(src_idx).basis;
} else {
diffs_w[src_idx] = tgt_rot.inverse() * src_skeleton->get_bone_rest(src_idx).basis;
}
Basis diff;
if (src_skeleton->get_bone_parent(src_idx) >= 0) {
diff = diffs[src_skeleton->get_bone_parent(src_idx)];
}
src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin)));
}
// Fix animation.
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
ERR_CONTINUE(!ap);
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
for (int i = 0; i < track_len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
continue;
}
if (anim->track_is_compressed(i)) {
continue; // Shouldn't occur in internal_process().
}
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (!track_skeleton || track_skeleton != src_skeleton) {
continue;
}
StringName bn = anim->track_get_path(i).get_subname(0);
if (!bn) {
continue;
}
int bone_idx = src_skeleton->find_bone(bn);
Transform3D old_rest = old_skeleton_rest[bone_idx];
Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx);
Transform3D old_pg;
Transform3D new_pg;
int parent_idx = src_skeleton->get_bone_parent(bone_idx);
if (parent_idx >= 0) {
old_pg = old_skeleton_global_rest[parent_idx];
new_pg = src_skeleton->get_bone_global_rest(parent_idx);
}
int key_len = anim->track_get_key_count(i);
if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
Quaternion old_rest_q = old_rest.basis.get_rotation_quaternion();
Quaternion new_rest_q = new_rest.basis.get_rotation_quaternion();
Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion();
Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion();
for (int j = 0; j < key_len; j++) {
Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, new_pg_q.inverse() * old_pg_q * qt * old_rest_q.inverse() * old_pg_q.inverse() * new_pg_q * new_rest_q);
}
} else if (anim->track_get_type(i) == Animation::TYPE_SCALE_3D) {
Basis old_rest_b_inv = old_rest.basis.inverse();
Basis new_rest_b = new_rest.basis;
Basis old_pg_b = old_pg.basis;
Basis new_pg_b = new_pg.basis;
Basis old_pg_b_inv = old_pg.basis.inverse();
Basis new_pg_b_inv = new_pg.basis.inverse();
for (int j = 0; j < key_len; j++) {
Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j)));
anim->track_set_key_value(i, j, (new_pg_b_inv * old_pg_b * sc * old_rest_b_inv * old_pg_b_inv * new_pg_b * new_rest_b).get_scale());
}
} else {
Vector3 old_rest_o = old_rest.origin;
Vector3 new_rest_o = new_rest.origin;
Basis old_pg_b = old_pg.basis;
Basis new_pg_b = new_pg.basis;
for (int j = 0; j < key_len; j++) {
Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, new_pg_b.xform_inv(old_pg_b.xform(ps - old_rest_o)) + new_rest_o);
}
}
}
}
}
}
if (p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") && !bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) {
// If Reset All Bone Poses After Import is disabled, preserve the original bone pose, adjusted for the new bone rolls.
for (int bone_idx = 0; bone_idx < src_skeleton->get_bone_count(); bone_idx++) {
Transform3D old_rest = old_skeleton_rest[bone_idx];
Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx);
Transform3D old_pg;
Transform3D new_pg;
int parent_idx = src_skeleton->get_bone_parent(bone_idx);
if (parent_idx >= 0) {
old_pg = old_skeleton_global_rest[parent_idx];
new_pg = src_skeleton->get_bone_global_rest(parent_idx);
}
Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion();
Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion();
Quaternion qt = src_skeleton->get_bone_pose_rotation(bone_idx);
src_skeleton->set_bone_pose_rotation(bone_idx, new_pg_q.inverse() * old_pg_q * qt * old_rest.basis.get_rotation_quaternion().inverse() * old_pg_q.inverse() * new_pg_q * new_rest.basis.get_rotation_quaternion());
Basis sc = Basis().scaled(src_skeleton->get_bone_pose_scale(bone_idx));
src_skeleton->set_bone_pose_scale(bone_idx, (new_pg.basis.inverse() * old_pg.basis * sc * old_rest.basis.inverse() * old_pg.basis.inverse() * new_pg.basis * new_rest.basis).get_scale());
Vector3 ps = src_skeleton->get_bone_pose_position(bone_idx);
src_skeleton->set_bone_pose_position(bone_idx, new_pg_q.xform_inv(old_pg_q.xform(ps - old_rest.origin)) + new_rest.origin);
}
}
is_rest_changed = true;
}
// Scale position tracks by motion scale if normalize position tracks.
if (bool(p_options["retarget/rest_fixer/normalize_position_tracks"])) {
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
for (int i = 0; i < track_len; i++) {
if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) {
continue;
}
if (anim->track_is_compressed(i)) {
continue; // Shouldn't occur in internal_process().
}
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
ERR_CONTINUE(!node);
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (!track_skeleton || track_skeleton != src_skeleton) {
continue;
}
real_t mlt = 1 / src_skeleton->get_motion_scale();
int key_len = anim->track_get_key_count(i);
for (int j = 0; j < key_len; j++) {
Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j));
anim->track_set_key_value(i, j, pos * mlt);
}
}
}
}
}
if (is_rest_changed) {
// Fix skin.
{
HashSet<Ref<Skin>> mutated_skins;
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
while (nodes.size()) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
ERR_CONTINUE(!mi);
Ref<Skin> skin = mi->get_skin();
if (skin.is_null()) {
continue;
}
if (mutated_skins.has(skin)) {
continue;
}
mutated_skins.insert(skin);
Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node);
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
if (!mesh_skeleton || mesh_skeleton != src_skeleton) {
continue;
}
int skin_len = skin->get_bind_count();
for (int i = 0; i < skin_len; i++) {
StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) {
Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
adjust_transform.scale(global_transform.basis.get_scale_global());
skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i));
}
}
}
nodes = src_skeleton->get_children();
while (nodes.size()) {
BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(nodes.pop_back());
if (attachment == nullptr) {
continue;
}
int bone_idx = attachment->get_bone_idx();
if (bone_idx == -1) {
bone_idx = src_skeleton->find_bone(attachment->get_bone_name());
}
ERR_CONTINUE(bone_idx < 0 || bone_idx >= src_skeleton->get_bone_count());
Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
adjust_transform.scale(global_transform.basis.get_scale_global());
TypedArray<Node> child_nodes = attachment->get_children();
while (child_nodes.size()) {
Node3D *child = Object::cast_to<Node3D>(child_nodes.pop_back());
if (child == nullptr) {
continue;
}
child->set_transform(adjust_transform * child->get_transform());
}
}
}
if (!p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") || bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) {
// Init skeleton pose to new rest.
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
Transform3D fixed_rest = src_skeleton->get_bone_rest(i);
src_skeleton->set_bone_pose_position(i, fixed_rest.origin);
src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
}
}
}
memdelete(prof_skeleton);
}
}
PostImportPluginSkeletonRestFixer::PostImportPluginSkeletonRestFixer() {
}

View file

@ -0,0 +1,47 @@
/**************************************************************************/
/* post_import_plugin_skeleton_rest_fixer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H
#define POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H
#include "resource_importer_scene.h"
class PostImportPluginSkeletonRestFixer : public EditorScenePostImportPlugin {
GDCLASS(PostImportPluginSkeletonRestFixer, EditorScenePostImportPlugin);
public:
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override;
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override;
PostImportPluginSkeletonRestFixer();
};
#endif // POST_IMPORT_PLUGIN_SKELETON_REST_FIXER_H

View file

@ -0,0 +1,127 @@
/**************************************************************************/
/* post_import_plugin_skeleton_track_organizer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "post_import_plugin_skeleton_track_organizer.h"
#include "editor/import/3d/scene_import_settings.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/bone_map.h"
void PostImportPluginSkeletonTrackOrganizer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/except_bone_transform"), false));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unimportant_positions"), true));
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unmapped_bones"), false));
}
}
void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) {
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
// Prepare objects.
Object *map = p_options["retarget/bone_map"].get_validated_object();
if (!map) {
return;
}
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
Ref<SkeletonProfile> profile = bone_map->get_profile();
if (!profile.is_valid()) {
return;
}
Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node);
if (!src_skeleton) {
return;
}
bool remove_except_bone = bool(p_options["retarget/remove_tracks/except_bone_transform"]);
bool remove_positions = bool(p_options["retarget/remove_tracks/unimportant_positions"]);
bool remove_unmapped_bones = bool(p_options["retarget/remove_tracks/unmapped_bones"]);
if (!remove_positions && !remove_unmapped_bones) {
return;
}
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
while (nodes.size()) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
int track_len = anim->get_track_count();
Vector<int> remove_indices;
for (int i = 0; i < track_len; i++) {
String track_path = String(anim->track_get_path(i).get_concatenated_names());
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
if (!node) {
if (remove_except_bone) {
remove_indices.push_back(i);
}
continue;
}
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (track_skeleton && track_skeleton == src_skeleton) {
if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) {
if (remove_except_bone) {
remove_indices.push_back(i);
}
continue;
}
StringName bn = anim->track_get_path(i).get_subname(0);
if (bn) {
int prof_idx = profile->find_bone(bone_map->find_profile_bone_name(bn));
if (remove_unmapped_bones && prof_idx < 0) {
remove_indices.push_back(i);
continue;
}
if (remove_positions && anim->track_get_type(i) == Animation::TYPE_POSITION_3D && prof_idx >= 0) {
StringName prof_bn = profile->get_bone_name(prof_idx);
if (prof_bn == profile->get_root_bone() || prof_bn == profile->get_scale_base_bone()) {
continue;
}
remove_indices.push_back(i);
}
}
}
if (remove_except_bone) {
remove_indices.push_back(i);
}
}
remove_indices.reverse();
for (int i = 0; i < remove_indices.size(); i++) {
anim->remove_track(remove_indices[i]);
}
}
}
}
}
PostImportPluginSkeletonTrackOrganizer::PostImportPluginSkeletonTrackOrganizer() {
}

View file

@ -0,0 +1,46 @@
/**************************************************************************/
/* post_import_plugin_skeleton_track_organizer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef POST_IMPORT_PLUGIN_SKELETON_TRACK_ORGANIZER_H
#define POST_IMPORT_PLUGIN_SKELETON_TRACK_ORGANIZER_H
#include "resource_importer_scene.h"
class PostImportPluginSkeletonTrackOrganizer : public EditorScenePostImportPlugin {
GDCLASS(PostImportPluginSkeletonTrackOrganizer, EditorScenePostImportPlugin);
public:
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override;
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override;
PostImportPluginSkeletonTrackOrganizer();
};
#endif // POST_IMPORT_PLUGIN_SKELETON_TRACK_ORGANIZER_H

View file

@ -0,0 +1,614 @@
/**************************************************************************/
/* resource_importer_obj.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "resource_importer_obj.h"
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/3d/importer_mesh.h"
#include "scene/resources/mesh.h"
#include "scene/resources/surface_tool.h"
uint32_t EditorOBJImporter::get_import_flags() const {
return IMPORT_SCENE;
}
static Error _parse_material_library(const String &p_path, HashMap<String, Ref<StandardMaterial3D>> &material_map, List<String> *r_missing_deps) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path));
Ref<StandardMaterial3D> current;
String current_name;
String base_path = p_path.get_base_dir();
while (true) {
String l = f->get_line().strip_edges();
if (l.begins_with("newmtl ")) {
//vertex
current_name = l.replace("newmtl", "").strip_edges();
current.instantiate();
current->set_name(current_name);
material_map[current_name] = current;
} else if (l.begins_with("Ka ")) {
//uv
WARN_PRINT("OBJ: Ambient light for material '" + current_name + "' is ignored in PBR");
} else if (l.begins_with("Kd ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
Color c = current->get_albedo();
c.r = v[1].to_float();
c.g = v[2].to_float();
c.b = v[3].to_float();
current->set_albedo(c);
} else if (l.begins_with("Ks ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
float r = v[1].to_float();
float g = v[2].to_float();
float b = v[3].to_float();
float metalness = MAX(r, MAX(g, b));
current->set_metallic(metalness);
} else if (l.begins_with("Ns ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
float s = v[1].to_float();
current->set_metallic((1000.0 - s) / 1000.0);
} else if (l.begins_with("d ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
float d = v[1].to_float();
Color c = current->get_albedo();
c.a = d;
current->set_albedo(c);
if (c.a < 0.99) {
current->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
}
} else if (l.begins_with("Tr ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
float d = v[1].to_float();
Color c = current->get_albedo();
c.a = 1.0 - d;
current->set_albedo(c);
if (c.a < 0.99) {
current->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
}
} else if (l.begins_with("map_Ka ")) {
//uv
WARN_PRINT("OBJ: Ambient light texture for material '" + current_name + "' is ignored in PBR");
} else if (l.begins_with("map_Kd ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Kd", "").replace("\\", "/").strip_edges();
String path;
if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.path_join(p);
}
Ref<Texture2D> texture = ResourceLoader::load(path);
if (texture.is_valid()) {
current->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, texture);
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}
} else if (l.begins_with("map_Ks ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Ks", "").replace("\\", "/").strip_edges();
String path;
if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.path_join(p);
}
Ref<Texture2D> texture = ResourceLoader::load(path);
if (texture.is_valid()) {
current->set_texture(StandardMaterial3D::TEXTURE_METALLIC, texture);
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}
} else if (l.begins_with("map_Ns ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Ns", "").replace("\\", "/").strip_edges();
String path;
if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.path_join(p);
}
Ref<Texture2D> texture = ResourceLoader::load(path);
if (texture.is_valid()) {
current->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, texture);
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}
} else if (l.begins_with("map_bump ")) {
//normal
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_bump", "").replace("\\", "/").strip_edges();
String path = base_path.path_join(p);
Ref<Texture2D> texture = ResourceLoader::load(path);
if (texture.is_valid()) {
current->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true);
current->set_texture(StandardMaterial3D::TEXTURE_NORMAL, texture);
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}
} else if (f->eof_reached()) {
break;
}
}
return OK;
}
static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
// Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree)
// This should only match, if it's indeed a COFF file header
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
const int first_bytes = f->get_16();
static const Vector<int> coff_header_machines{
0x0, // IMAGE_FILE_MACHINE_UNKNOWN
0x8664, // IMAGE_FILE_MACHINE_AMD64
0x1c0, // IMAGE_FILE_MACHINE_ARM
0x14c, // IMAGE_FILE_MACHINE_I386
0x200, // IMAGE_FILE_MACHINE_IA64
};
ERR_FAIL_COND_V_MSG(coff_header_machines.has(first_bytes), ERR_FILE_CORRUPT, vformat("Couldn't read OBJ file '%s', it seems to be binary, corrupted, or empty.", p_path));
f->seek(0);
Ref<ImporterMesh> mesh;
mesh.instantiate();
bool generate_tangents = p_generate_tangents;
Vector3 scale_mesh = p_scale_mesh;
Vector3 offset_mesh = p_offset_mesh;
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
Vector<Color> colors;
const String default_name = "Mesh";
String name = default_name;
HashMap<String, HashMap<String, Ref<StandardMaterial3D>>> material_map;
Ref<SurfaceTool> surf_tool = memnew(SurfaceTool);
surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
String current_material_library;
String current_material;
String current_group;
uint32_t smooth_group = 0;
bool smoothing = true;
const uint32_t no_smoothing_smooth_group = (uint32_t)-1;
while (true) {
String l = f->get_line().strip_edges();
while (l.length() && l[l.length() - 1] == '\\') {
String add = f->get_line().strip_edges();
l += add;
if (add.is_empty()) {
break;
}
}
if (l.begins_with("v ")) {
//vertex
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_FILE_CORRUPT);
Vector3 vtx;
vtx.x = v[1].to_float() * scale_mesh.x + offset_mesh.x;
vtx.y = v[2].to_float() * scale_mesh.y + offset_mesh.y;
vtx.z = v[3].to_float() * scale_mesh.z + offset_mesh.z;
vertices.push_back(vtx);
//vertex color
if (v.size() >= 7) {
while (colors.size() < vertices.size() - 1) {
colors.push_back(Color(1.0, 1.0, 1.0));
}
Color c;
c.r = v[4].to_float();
c.g = v[5].to_float();
c.b = v[6].to_float();
colors.push_back(c);
} else if (!colors.is_empty()) {
colors.push_back(Color(1.0, 1.0, 1.0));
}
} else if (l.begins_with("vt ")) {
//uv
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 3, ERR_FILE_CORRUPT);
Vector2 uv;
uv.x = v[1].to_float();
uv.y = 1.0 - v[2].to_float();
uvs.push_back(uv);
} else if (l.begins_with("vn ")) {
//normal
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_FILE_CORRUPT);
Vector3 nrm;
nrm.x = v[1].to_float();
nrm.y = v[2].to_float();
nrm.z = v[3].to_float();
normals.push_back(nrm);
} else if (l.begins_with("f ")) {
//vertex
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_FILE_CORRUPT);
//not very fast, could be sped up
Vector<String> face[3];
face[0] = v[1].split("/");
face[1] = v[2].split("/");
ERR_FAIL_COND_V(face[0].is_empty(), ERR_FILE_CORRUPT);
ERR_FAIL_COND_V(face[0].size() != face[1].size(), ERR_FILE_CORRUPT);
for (int i = 2; i < v.size() - 1; i++) {
face[2] = v[i + 1].split("/");
ERR_FAIL_COND_V(face[0].size() != face[2].size(), ERR_FILE_CORRUPT);
for (int j = 0; j < 3; j++) {
int idx = j;
if (idx < 2) {
idx = 1 ^ idx;
}
if (face[idx].size() == 3) {
int norm = face[idx][2].to_int() - 1;
if (norm < 0) {
norm += normals.size() + 1;
}
ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT);
surf_tool->set_normal(normals[norm]);
if (generate_tangents && uvs.is_empty()) {
// We can't generate tangents without UVs, so create dummy tangents.
Vector3 tan = Vector3(normals[norm].z, -normals[norm].x, normals[norm].y).cross(normals[norm].normalized()).normalized();
surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0));
}
} else {
// No normals, use a dummy tangent since normals and tangents will be generated.
if (generate_tangents && uvs.is_empty()) {
// We can't generate tangents without UVs, so create dummy tangents.
surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0));
}
}
if (face[idx].size() >= 2 && !face[idx][1].is_empty()) {
int uv = face[idx][1].to_int() - 1;
if (uv < 0) {
uv += uvs.size() + 1;
}
ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT);
surf_tool->set_uv(uvs[uv]);
}
int vtx = face[idx][0].to_int() - 1;
if (vtx < 0) {
vtx += vertices.size() + 1;
}
ERR_FAIL_INDEX_V(vtx, vertices.size(), ERR_FILE_CORRUPT);
Vector3 vertex = vertices[vtx];
if (!colors.is_empty()) {
surf_tool->set_color(colors[vtx]);
}
surf_tool->set_smooth_group(smoothing ? smooth_group : no_smoothing_smooth_group);
surf_tool->add_vertex(vertex);
}
face[1] = face[2];
}
} else if (l.begins_with("s ")) { //smoothing
String what = l.substr(2, l.length()).strip_edges();
bool do_smooth;
if (what == "off") {
do_smooth = false;
} else {
do_smooth = true;
}
if (do_smooth != smoothing) {
smoothing = do_smooth;
if (smoothing) {
smooth_group++;
}
}
} else if (/*l.begins_with("g ") ||*/ l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh
uint64_t mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
if (p_disable_compression) {
mesh_flags = 0;
} else {
bool is_mesh_2d = true;
// Disable compression if all z equals 0 (the mesh is 2D).
for (int i = 0; i < vertices.size(); i++) {
if (!Math::is_zero_approx(vertices[i].z)) {
is_mesh_2d = false;
break;
}
}
if (is_mesh_2d) {
mesh_flags = 0;
}
}
//groups are too annoying
if (surf_tool->get_vertex_array().size()) {
//another group going on, commit it
if (normals.size() == 0) {
surf_tool->generate_normals();
}
if (generate_tangents && uvs.size()) {
surf_tool->generate_tangents();
}
surf_tool->index();
print_verbose("OBJ: Current material library " + current_material_library + " has " + itos(material_map.has(current_material_library)));
print_verbose("OBJ: Current material " + current_material + " has " + itos(material_map.has(current_material_library) && material_map[current_material_library].has(current_material)));
Ref<StandardMaterial3D> material;
if (material_map.has(current_material_library) && material_map[current_material_library].has(current_material)) {
material = material_map[current_material_library][current_material];
if (!colors.is_empty()) {
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
}
surf_tool->set_material(material);
}
Array array = surf_tool->commit_to_arrays();
if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents) {
// Compression is enabled, so let's validate that the normals and tangents are correct.
Vector<Vector3> norms = array[Mesh::ARRAY_NORMAL];
Vector<float> tangents = array[Mesh::ARRAY_TANGENT];
for (int vert = 0; vert < norms.size(); vert++) {
Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]);
if (abs(tan.dot(norms[vert])) > 0.0001) {
// Tangent is not perpendicular to the normal, so we can't use compression.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
}
}
mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray<Array>(), Dictionary(), material, name, mesh_flags);
print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1));
if (!current_material.is_empty()) {
if (mesh->get_surface_count() >= 1) {
mesh->set_surface_name(mesh->get_surface_count() - 1, current_material.get_basename());
}
} else if (!current_group.is_empty()) {
if (mesh->get_surface_count() >= 1) {
mesh->set_surface_name(mesh->get_surface_count() - 1, current_group);
}
}
surf_tool->clear();
surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
}
if (l.begins_with("o ") || f->eof_reached()) {
if (!p_single_mesh) {
if (mesh->get_surface_count() > 0) {
mesh->set_name(name);
r_meshes.push_back(mesh);
mesh.instantiate();
}
name = default_name;
current_group = "";
current_material = "";
}
}
if (f->eof_reached()) {
break;
}
if (l.begins_with("o ")) {
name = l.substr(2, l.length()).strip_edges();
}
if (l.begins_with("usemtl ")) {
current_material = l.replace("usemtl", "").strip_edges();
}
if (l.begins_with("g ")) {
current_group = l.substr(2, l.length()).strip_edges();
}
} else if (l.begins_with("mtllib ")) { //parse material
current_material_library = l.replace("mtllib", "").strip_edges();
if (!material_map.has(current_material_library)) {
HashMap<String, Ref<StandardMaterial3D>> lib;
String lib_path = current_material_library;
if (lib_path.is_relative_path()) {
lib_path = p_path.get_base_dir().path_join(current_material_library);
}
Error err = _parse_material_library(lib_path, lib, r_missing_deps);
if (err == OK) {
material_map[current_material_library] = lib;
}
}
}
}
if (p_single_mesh && mesh->get_surface_count() > 0) {
r_meshes.push_back(mesh);
}
return OK;
}
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
List<Ref<ImporterMesh>> meshes;
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps);
if (err != OK) {
if (r_err) {
*r_err = err;
}
return nullptr;
}
Node3D *scene = memnew(Node3D);
for (Ref<ImporterMesh> m : meshes) {
ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D);
mi->set_mesh(m);
mi->set_name(m->get_name());
scene->add_child(mi, true);
mi->set_owner(scene);
}
if (r_err) {
*r_err = OK;
}
return scene;
}
void EditorOBJImporter::get_extensions(List<String> *r_extensions) const {
r_extensions->push_back("obj");
}
EditorOBJImporter::EditorOBJImporter() {
}
////////////////////////////////////////////////////
String ResourceImporterOBJ::get_importer_name() const {
return "wavefront_obj";
}
String ResourceImporterOBJ::get_visible_name() const {
return "OBJ as Mesh";
}
void ResourceImporterOBJ::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("obj");
}
String ResourceImporterOBJ::get_save_extension() const {
return "mesh";
}
String ResourceImporterOBJ::get_resource_type() const {
return "Mesh";
}
int ResourceImporterOBJ::get_format_version() const {
return 1;
}
int ResourceImporterOBJ::get_preset_count() const {
return 0;
}
String ResourceImporterOBJ::get_preset_name(int p_idx) const {
return "";
}
void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0)));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false));
}
bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
List<Ref<ImporterMesh>> meshes;
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);
String save_path = p_save_path + ".mesh";
err = ResourceSaver::save(meshes.front()->get()->get_mesh(), save_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save Mesh to file '" + save_path + "'.");
r_gen_files->push_back(save_path);
return OK;
}
ResourceImporterOBJ::ResourceImporterOBJ() {
}

View file

@ -0,0 +1,72 @@
/**************************************************************************/
/* resource_importer_obj.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef RESOURCE_IMPORTER_OBJ_H
#define RESOURCE_IMPORTER_OBJ_H
#include "resource_importer_scene.h"
class EditorOBJImporter : public EditorSceneFormatImporter {
GDCLASS(EditorOBJImporter, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
EditorOBJImporter();
};
class ResourceImporterOBJ : public ResourceImporter {
GDCLASS(ResourceImporterOBJ, ResourceImporter);
public:
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;
virtual int get_format_version() const override;
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
// Threaded import can currently cause deadlocks, see GH-48265.
virtual bool can_import_threaded() const override { return false; }
ResourceImporterOBJ();
};
#endif // RESOURCE_IMPORTER_OBJ_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,525 @@
/**************************************************************************/
/* resource_importer_scene.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef RESOURCE_IMPORTER_SCENE_H
#define RESOURCE_IMPORTER_SCENE_H
#include "core/error/error_macros.h"
#include "core/io/resource_importer.h"
#include "core/variant/dictionary.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/resources/3d/box_shape_3d.h"
#include "scene/resources/3d/capsule_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/importer_mesh.h"
#include "scene/resources/3d/skin.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/animation.h"
#include "scene/resources/mesh.h"
class Material;
class AnimationPlayer;
class ImporterMesh;
class EditorSceneFormatImporter : public RefCounted {
GDCLASS(EditorSceneFormatImporter, RefCounted);
protected:
static void _bind_methods();
Node *import_scene_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options);
Ref<Animation> import_animation_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options);
GDVIRTUAL0RC(uint32_t, _get_import_flags)
GDVIRTUAL0RC(Vector<String>, _get_extensions)
GDVIRTUAL3R(Object *, _import_scene, String, uint32_t, Dictionary)
GDVIRTUAL1(_get_import_options, String)
GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String)
public:
enum ImportFlags {
IMPORT_SCENE = 1,
IMPORT_ANIMATION = 2,
IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
IMPORT_DISCARD_MESHES_AND_MATERIALS = 32, //used for optimizing animation import
IMPORT_FORCE_DISABLE_MESH_COMPRESSION = 64,
};
virtual uint32_t get_import_flags() const;
virtual void get_extensions(List<String> *r_extensions) const;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr);
virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options);
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options);
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {}
EditorSceneFormatImporter() {}
};
class EditorScenePostImport : public RefCounted {
GDCLASS(EditorScenePostImport, RefCounted);
String source_file;
protected:
static void _bind_methods();
GDVIRTUAL1R(Object *, _post_import, Node *)
public:
String get_source_file() const;
virtual Node *post_import(Node *p_scene);
virtual void init(const String &p_source_file);
EditorScenePostImport();
};
class EditorScenePostImportPlugin : public RefCounted {
GDCLASS(EditorScenePostImportPlugin, RefCounted);
public:
enum InternalImportCategory {
INTERNAL_IMPORT_CATEGORY_NODE,
INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
INTERNAL_IMPORT_CATEGORY_MESH,
INTERNAL_IMPORT_CATEGORY_MATERIAL,
INTERNAL_IMPORT_CATEGORY_ANIMATION,
INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE,
INTERNAL_IMPORT_CATEGORY_MAX
};
private:
mutable const HashMap<StringName, Variant> *current_options = nullptr;
mutable const Dictionary *current_options_dict = nullptr;
List<ResourceImporter::ImportOption> *current_option_list = nullptr;
InternalImportCategory current_category = INTERNAL_IMPORT_CATEGORY_MAX;
protected:
GDVIRTUAL1(_get_internal_import_options, int)
GDVIRTUAL3RC(Variant, _get_internal_option_visibility, int, bool, String)
GDVIRTUAL2RC(Variant, _get_internal_option_update_view_required, int, String)
GDVIRTUAL4(_internal_process, int, Node *, Node *, Ref<Resource>)
GDVIRTUAL1(_get_import_options, String)
GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String)
GDVIRTUAL1(_pre_process, Node *)
GDVIRTUAL1(_post_process, Node *)
static void _bind_methods();
public:
Variant get_option_value(const StringName &p_name) const;
void add_import_option(const String &p_name, const Variant &p_default_value);
void add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT);
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options);
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
virtual Variant get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options);
virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options);
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
virtual void pre_process(Node *p_scene, const HashMap<StringName, Variant> &p_options);
virtual void post_process(Node *p_scene, const HashMap<StringName, Variant> &p_options);
EditorScenePostImportPlugin() {}
};
VARIANT_ENUM_CAST(EditorScenePostImportPlugin::InternalImportCategory)
class ResourceImporterScene : public ResourceImporter {
GDCLASS(ResourceImporterScene, ResourceImporter);
static Vector<Ref<EditorSceneFormatImporter>> scene_importers;
static Vector<Ref<EditorScenePostImportPlugin>> post_importer_plugins;
static ResourceImporterScene *scene_singleton;
static ResourceImporterScene *animation_singleton;
enum LightBakeMode {
LIGHT_BAKE_DISABLED,
LIGHT_BAKE_STATIC,
LIGHT_BAKE_STATIC_LIGHTMAPS,
LIGHT_BAKE_DYNAMIC,
};
enum MeshPhysicsMode {
MESH_PHYSICS_DISABLED,
MESH_PHYSICS_MESH_AND_STATIC_COLLIDER,
MESH_PHYSICS_RIGID_BODY_AND_MESH,
MESH_PHYSICS_STATIC_COLLIDER_ONLY,
MESH_PHYSICS_AREA_ONLY,
};
enum NavMeshMode {
NAVMESH_DISABLED,
NAVMESH_MESH_AND_NAVMESH,
NAVMESH_NAVMESH_ONLY,
};
enum OccluderMode {
OCCLUDER_DISABLED,
OCCLUDER_MESH_AND_OCCLUDER,
OCCLUDER_OCCLUDER_ONLY,
};
enum MeshOverride {
MESH_OVERRIDE_DEFAULT,
MESH_OVERRIDE_ENABLE,
MESH_OVERRIDE_DISABLE,
};
enum BodyType {
BODY_TYPE_STATIC,
BODY_TYPE_DYNAMIC,
BODY_TYPE_AREA
};
enum ShapeType {
SHAPE_TYPE_DECOMPOSE_CONVEX,
SHAPE_TYPE_SIMPLE_CONVEX,
SHAPE_TYPE_TRIMESH,
SHAPE_TYPE_BOX,
SHAPE_TYPE_SPHERE,
SHAPE_TYPE_CYLINDER,
SHAPE_TYPE_CAPSULE,
SHAPE_TYPE_AUTOMATIC,
};
static Error _check_resource_save_paths(const Dictionary &p_data);
Array _get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node);
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
Node *_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
void _copy_meta(Object *p_src_object, Object *p_dst_object);
enum AnimationImportTracks {
ANIMATION_IMPORT_TRACKS_IF_PRESENT,
ANIMATION_IMPORT_TRACKS_IF_PRESENT_FOR_ALL,
ANIMATION_IMPORT_TRACKS_NEVER,
};
enum TrackChannel {
TRACK_CHANNEL_POSITION,
TRACK_CHANNEL_ROTATION,
TRACK_CHANNEL_SCALE,
TRACK_CHANNEL_BLEND_SHAPE,
TRACK_CHANNEL_MAX
};
void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions);
bool animation_importer = false;
public:
static ResourceImporterScene *get_scene_singleton() { return scene_singleton; }
static ResourceImporterScene *get_animation_singleton() { return animation_singleton; }
static void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority = false);
static void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin);
const Vector<Ref<EditorSceneFormatImporter>> &get_scene_importers() const { return scene_importers; }
static void add_scene_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority = false);
static void remove_scene_importer(Ref<EditorSceneFormatImporter> p_importer);
static void get_scene_importer_extensions(List<String> *p_extensions);
static void clean_up_importer_plugins();
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;
virtual int get_format_version() const override;
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
enum InternalImportCategory {
INTERNAL_IMPORT_CATEGORY_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE,
INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
INTERNAL_IMPORT_CATEGORY_MESH = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MESH,
INTERNAL_IMPORT_CATEGORY_MATERIAL = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL,
INTERNAL_IMPORT_CATEGORY_ANIMATION = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION,
INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE,
INTERNAL_IMPORT_CATEGORY_MAX = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MAX
};
void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
bool get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
bool get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const override;
// Import scenes *after* everything else (such as textures).
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
void _pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const;
Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames);
Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps);
Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale);
Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, const String &p_save_to_path, bool p_keep_custom_tracks);
void _create_slices(AnimationPlayer *ap, Ref<Animation> anim, const Array &p_clips, bool p_bake_all);
void _optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error);
void _compress_animations(AnimationPlayer *anim, int p_page_size_kb);
Node *pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options);
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
virtual bool can_import_threaded() const override { return false; }
ResourceImporterScene(bool p_animation_import = false, bool p_singleton = false);
~ResourceImporterScene();
template <typename M>
static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale);
template <typename M>
static Transform3D get_collision_shapes_transform(const M &p_options);
};
class EditorSceneFormatImporterESCN : public EditorSceneFormatImporter {
GDCLASS(EditorSceneFormatImporterESCN, EditorSceneFormatImporter);
public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
};
template <typename M>
Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale) {
ERR_FAIL_COND_V(p_mesh.is_null(), Vector<Ref<Shape3D>>());
ShapeType generate_shape_type = SHAPE_TYPE_AUTOMATIC;
if (p_options.has(SNAME("physics/shape_type"))) {
generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
}
if (generate_shape_type == SHAPE_TYPE_AUTOMATIC) {
BodyType body_type = BODY_TYPE_STATIC;
if (p_options.has(SNAME("physics/body_type"))) {
body_type = (BodyType)p_options[SNAME("physics/body_type")].operator int();
}
generate_shape_type = body_type == BODY_TYPE_DYNAMIC ? SHAPE_TYPE_DECOMPOSE_CONVEX : SHAPE_TYPE_TRIMESH;
}
if (generate_shape_type == SHAPE_TYPE_DECOMPOSE_CONVEX) {
Ref<MeshConvexDecompositionSettings> decomposition_settings = Ref<MeshConvexDecompositionSettings>();
decomposition_settings.instantiate();
bool advanced = false;
if (p_options.has(SNAME("decomposition/advanced"))) {
advanced = p_options[SNAME("decomposition/advanced")];
}
if (advanced) {
if (p_options.has(SNAME("decomposition/max_concavity"))) {
decomposition_settings->set_max_concavity(p_options[SNAME("decomposition/max_concavity")]);
}
if (p_options.has(SNAME("decomposition/symmetry_planes_clipping_bias"))) {
decomposition_settings->set_symmetry_planes_clipping_bias(p_options[SNAME("decomposition/symmetry_planes_clipping_bias")]);
}
if (p_options.has(SNAME("decomposition/revolution_axes_clipping_bias"))) {
decomposition_settings->set_revolution_axes_clipping_bias(p_options[SNAME("decomposition/revolution_axes_clipping_bias")]);
}
if (p_options.has(SNAME("decomposition/min_volume_per_convex_hull"))) {
decomposition_settings->set_min_volume_per_convex_hull(p_options[SNAME("decomposition/min_volume_per_convex_hull")]);
}
if (p_options.has(SNAME("decomposition/resolution"))) {
decomposition_settings->set_resolution(p_options[SNAME("decomposition/resolution")]);
}
if (p_options.has(SNAME("decomposition/max_num_vertices_per_convex_hull"))) {
decomposition_settings->set_max_num_vertices_per_convex_hull(p_options[SNAME("decomposition/max_num_vertices_per_convex_hull")]);
}
if (p_options.has(SNAME("decomposition/plane_downsampling"))) {
decomposition_settings->set_plane_downsampling(p_options[SNAME("decomposition/plane_downsampling")]);
}
if (p_options.has(SNAME("decomposition/convexhull_downsampling"))) {
decomposition_settings->set_convex_hull_downsampling(p_options[SNAME("decomposition/convexhull_downsampling")]);
}
if (p_options.has(SNAME("decomposition/normalize_mesh"))) {
decomposition_settings->set_normalize_mesh(p_options[SNAME("decomposition/normalize_mesh")]);
}
if (p_options.has(SNAME("decomposition/mode"))) {
decomposition_settings->set_mode((MeshConvexDecompositionSettings::Mode)p_options[SNAME("decomposition/mode")].operator int());
}
if (p_options.has(SNAME("decomposition/convexhull_approximation"))) {
decomposition_settings->set_convex_hull_approximation(p_options[SNAME("decomposition/convexhull_approximation")]);
}
if (p_options.has(SNAME("decomposition/max_convex_hulls"))) {
decomposition_settings->set_max_convex_hulls(MAX(1, (int)p_options[SNAME("decomposition/max_convex_hulls")]));
}
if (p_options.has(SNAME("decomposition/project_hull_vertices"))) {
decomposition_settings->set_project_hull_vertices(p_options[SNAME("decomposition/project_hull_vertices")]);
}
} else {
int precision_level = 5;
if (p_options.has(SNAME("decomposition/precision"))) {
precision_level = p_options[SNAME("decomposition/precision")];
}
const real_t precision = real_t(precision_level - 1) / 9.0;
decomposition_settings->set_max_concavity(Math::lerp(real_t(1.0), real_t(0.001), precision));
decomposition_settings->set_min_volume_per_convex_hull(Math::lerp(real_t(0.01), real_t(0.0001), precision));
decomposition_settings->set_resolution(Math::lerp(10'000, 100'000, precision));
decomposition_settings->set_max_num_vertices_per_convex_hull(Math::lerp(32, 64, precision));
decomposition_settings->set_plane_downsampling(Math::lerp(3, 16, precision));
decomposition_settings->set_convex_hull_downsampling(Math::lerp(3, 16, precision));
decomposition_settings->set_max_convex_hulls(Math::lerp(1, 32, precision));
}
return p_mesh->convex_decompose(decomposition_settings);
} else if (generate_shape_type == SHAPE_TYPE_SIMPLE_CONVEX) {
Vector<Ref<Shape3D>> shapes;
shapes.push_back(p_mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_TRIMESH) {
Vector<Ref<Shape3D>> shapes;
shapes.push_back(p_mesh->create_trimesh_shape());
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_BOX) {
Ref<BoxShape3D> box;
box.instantiate();
if (p_options.has(SNAME("primitive/size"))) {
box->set_size(p_options[SNAME("primitive/size")].operator Vector3() * p_applied_root_scale);
} else {
box->set_size(Vector3(2, 2, 2) * p_applied_root_scale);
}
Vector<Ref<Shape3D>> shapes;
shapes.push_back(box);
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_SPHERE) {
Ref<SphereShape3D> sphere;
sphere.instantiate();
if (p_options.has(SNAME("primitive/radius"))) {
sphere->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale);
} else {
sphere->set_radius(1.0f * p_applied_root_scale);
}
Vector<Ref<Shape3D>> shapes;
shapes.push_back(sphere);
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_CYLINDER) {
Ref<CylinderShape3D> cylinder;
cylinder.instantiate();
if (p_options.has(SNAME("primitive/height"))) {
cylinder->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale);
} else {
cylinder->set_height(1.0f * p_applied_root_scale);
}
if (p_options.has(SNAME("primitive/radius"))) {
cylinder->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale);
} else {
cylinder->set_radius(1.0f * p_applied_root_scale);
}
Vector<Ref<Shape3D>> shapes;
shapes.push_back(cylinder);
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_CAPSULE) {
Ref<CapsuleShape3D> capsule;
capsule.instantiate();
if (p_options.has(SNAME("primitive/height"))) {
capsule->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale);
} else {
capsule->set_height(1.0f * p_applied_root_scale);
}
if (p_options.has(SNAME("primitive/radius"))) {
capsule->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale);
} else {
capsule->set_radius(1.0f * p_applied_root_scale);
}
Vector<Ref<Shape3D>> shapes;
shapes.push_back(capsule);
return shapes;
}
return Vector<Ref<Shape3D>>();
}
template <typename M>
Transform3D ResourceImporterScene::get_collision_shapes_transform(const M &p_options) {
Transform3D transform;
ShapeType generate_shape_type = SHAPE_TYPE_AUTOMATIC;
if (p_options.has(SNAME("physics/shape_type"))) {
generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
}
if (generate_shape_type == SHAPE_TYPE_AUTOMATIC) {
BodyType body_type = BODY_TYPE_STATIC;
if (p_options.has(SNAME("physics/body_type"))) {
body_type = (BodyType)p_options[SNAME("physics/body_type")].operator int();
}
generate_shape_type = body_type == BODY_TYPE_DYNAMIC ? SHAPE_TYPE_DECOMPOSE_CONVEX : SHAPE_TYPE_TRIMESH;
}
if (generate_shape_type == SHAPE_TYPE_BOX ||
generate_shape_type == SHAPE_TYPE_SPHERE ||
generate_shape_type == SHAPE_TYPE_CYLINDER ||
generate_shape_type == SHAPE_TYPE_CAPSULE) {
if (p_options.has(SNAME("primitive/position"))) {
transform.origin = p_options[SNAME("primitive/position")];
}
if (p_options.has(SNAME("primitive/rotation"))) {
transform.basis = Basis::from_euler(p_options[SNAME("primitive/rotation")].operator Vector3() * (Math_PI / 180.0));
}
}
return transform;
}
#endif // RESOURCE_IMPORTER_SCENE_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,253 @@
/**************************************************************************/
/* scene_import_settings.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_IMPORT_SETTINGS_H
#define SCENE_IMPORT_SETTINGS_H
#include "editor/import/3d/resource_importer_scene.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/slider.h"
#include "scene/gui/split_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/tree.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/sky_material.h"
class EditorFileDialog;
class EditorInspector;
class SceneImportSettingsData;
class SceneImportSettingsDialog : public ConfirmationDialog {
GDCLASS(SceneImportSettingsDialog, ConfirmationDialog)
static SceneImportSettingsDialog *singleton;
enum Actions {
ACTION_EXTRACT_MATERIALS,
ACTION_CHOOSE_MESH_SAVE_PATHS,
ACTION_CHOOSE_ANIMATION_SAVE_PATHS,
};
Node *scene = nullptr;
HSplitContainer *tree_split = nullptr;
HSplitContainer *property_split = nullptr;
TabContainer *data_mode = nullptr;
Tree *scene_tree = nullptr;
Tree *mesh_tree = nullptr;
Tree *material_tree = nullptr;
EditorInspector *inspector = nullptr;
SubViewport *base_viewport = nullptr;
Camera3D *camera = nullptr;
Ref<CameraAttributesPractical> camera_attributes;
Ref<Environment> environment;
Ref<Sky> sky;
Ref<ProceduralSkyMaterial> procedural_sky_material;
bool first_aabb = false;
AABB contents_aabb;
Button *light_1_switch = nullptr;
Button *light_2_switch = nullptr;
Button *light_rotate_switch = nullptr;
struct ThemeCache {
Ref<Texture2D> light_1_icon;
Ref<Texture2D> light_2_icon;
Ref<Texture2D> rotate_icon;
} theme_cache;
DirectionalLight3D *light1 = nullptr;
DirectionalLight3D *light2 = nullptr;
Ref<ArrayMesh> selection_mesh;
MeshInstance3D *node_selected = nullptr;
MeshInstance3D *mesh_preview = nullptr;
Ref<SphereMesh> material_preview;
AnimationPlayer *animation_player = nullptr;
List<Skeleton3D *> skeletons;
PanelContainer *animation_preview = nullptr;
HSlider *animation_slider = nullptr;
Button *animation_play_button = nullptr;
Button *animation_stop_button = nullptr;
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
bool animation_pingpong = false;
bool previous_import_as_skeleton = false;
bool previous_rest_as_reset = false;
Ref<StandardMaterial3D> collider_mat;
float cam_rot_x = 0.0f;
float cam_rot_y = 0.0f;
float cam_zoom = 0.0f;
void _update_scene();
struct MaterialData {
bool has_import_id;
Ref<Material> material;
TreeItem *scene_node = nullptr;
TreeItem *mesh_node = nullptr;
TreeItem *material_node = nullptr;
float cam_rot_x = -Math_PI / 4;
float cam_rot_y = -Math_PI / 4;
float cam_zoom = 1;
HashMap<StringName, Variant> settings;
};
HashMap<String, MaterialData> material_map;
HashMap<Ref<Material>, String> unnamed_material_name_map;
struct MeshData {
bool has_import_id;
Ref<Mesh> mesh;
TreeItem *scene_node = nullptr;
TreeItem *mesh_node = nullptr;
float cam_rot_x = -Math_PI / 4;
float cam_rot_y = -Math_PI / 4;
float cam_zoom = 1;
HashMap<StringName, Variant> settings;
};
HashMap<String, MeshData> mesh_map;
struct AnimationData {
Ref<Animation> animation;
TreeItem *scene_node = nullptr;
HashMap<StringName, Variant> settings;
};
HashMap<String, AnimationData> animation_map;
struct NodeData {
Node *node = nullptr;
TreeItem *scene_node = nullptr;
HashMap<StringName, Variant> settings;
};
HashMap<String, NodeData> node_map;
void _fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent);
void _fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent);
void _fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent);
void _fill_scene(Node *p_node, TreeItem *p_parent_item);
HashSet<Ref<Mesh>> mesh_set;
String selected_type;
String selected_id;
bool selecting = false;
void _update_view_gizmos();
void _update_camera();
void _select(Tree *p_from, const String &p_type, const String &p_id);
void _inspector_property_edited(const String &p_name);
void _reset_bone_transforms();
void _play_animation();
void _stop_current_animation();
void _reset_animation(const String &p_animation_name = "");
void _animation_slider_value_changed(double p_value);
void _animation_finished(const StringName &p_name);
void _material_tree_selected();
void _mesh_tree_selected();
void _scene_tree_selected();
void _cleanup();
void _on_light_1_switch_pressed();
void _on_light_2_switch_pressed();
void _on_light_rotate_switch_pressed();
void _viewport_input(const Ref<InputEvent> &p_input);
HashMap<StringName, Variant> defaults;
SceneImportSettingsData *scene_import_settings_data = nullptr;
void _re_import();
String base_path;
MenuButton *action_menu = nullptr;
ConfirmationDialog *external_paths = nullptr;
Tree *external_path_tree = nullptr;
EditorFileDialog *save_path = nullptr;
OptionButton *external_extension_type = nullptr;
EditorFileDialog *item_save_path = nullptr;
void _menu_callback(int p_id);
void _save_dir_callback(const String &p_path);
int current_action = 0;
Vector<TreeItem *> save_path_items;
TreeItem *save_path_item = nullptr;
void _save_path_changed(const String &p_path);
void _browse_save_callback(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _save_dir_confirm();
Dictionary base_subresource_settings;
void _load_default_subresource_settings(HashMap<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category);
bool editing_animation = false;
bool generate_collider = false;
Timer *update_view_timer = nullptr;
protected:
virtual void _update_theme_item_cache() override;
void _notification(int p_what);
public:
bool is_editing_animation() const { return editing_animation; }
void request_generate_collider();
void update_view();
void open_settings(const String &p_path, bool p_for_animation = false);
static SceneImportSettingsDialog *get_singleton();
Node *get_selected_node();
SceneImportSettingsDialog();
~SceneImportSettingsDialog();
};
#endif // SCENE_IMPORT_SETTINGS_H