feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
2384
engine/editor/import/3d/collada.cpp
Normal file
2384
engine/editor/import/3d/collada.cpp
Normal file
File diff suppressed because it is too large
Load diff
577
engine/editor/import/3d/collada.h
Normal file
577
engine/editor/import/3d/collada.h
Normal 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
|
||||
1867
engine/editor/import/3d/editor_import_collada.cpp
Normal file
1867
engine/editor/import/3d/editor_import_collada.cpp
Normal file
File diff suppressed because it is too large
Load diff
47
engine/editor/import/3d/editor_import_collada.h
Normal file
47
engine/editor/import/3d/editor_import_collada.h
Normal 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
|
||||
251
engine/editor/import/3d/post_import_plugin_skeleton_renamer.cpp
Normal file
251
engine/editor/import/3d/post_import_plugin_skeleton_renamer.cpp
Normal 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() {
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
@ -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
|
||||
614
engine/editor/import/3d/resource_importer_obj.cpp
Normal file
614
engine/editor/import/3d/resource_importer_obj.cpp
Normal 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() {
|
||||
}
|
||||
72
engine/editor/import/3d/resource_importer_obj.h
Normal file
72
engine/editor/import/3d/resource_importer_obj.h
Normal 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
|
||||
3272
engine/editor/import/3d/resource_importer_scene.cpp
Normal file
3272
engine/editor/import/3d/resource_importer_scene.cpp
Normal file
File diff suppressed because it is too large
Load diff
525
engine/editor/import/3d/resource_importer_scene.h
Normal file
525
engine/editor/import/3d/resource_importer_scene.h
Normal 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
|
||||
1856
engine/editor/import/3d/scene_import_settings.cpp
Normal file
1856
engine/editor/import/3d/scene_import_settings.cpp
Normal file
File diff suppressed because it is too large
Load diff
253
engine/editor/import/3d/scene_import_settings.h
Normal file
253
engine/editor/import/3d/scene_import_settings.h
Normal 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
|
||||
6
engine/editor/import/SCsub
Normal file
6
engine/editor/import/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
env.add_source_files(env.editor_sources, "3d/*.cpp")
|
||||
414
engine/editor/import/atlas_import_failed.xpm
Normal file
414
engine/editor/import/atlas_import_failed.xpm
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/* XPM */
|
||||
static const char * atlas_import_failed_xpm[] = {
|
||||
"128 128 283 2",
|
||||
" c None",
|
||||
". c #FFFFFF",
|
||||
"+ c #FFDADA",
|
||||
"@ c #FF0000",
|
||||
"# c #FFD8D8",
|
||||
"$ c #FFF7F7",
|
||||
"% c #FF2E2E",
|
||||
"& c #FFD4D4",
|
||||
"* c #FFD6D6",
|
||||
"= c #FFE3E3",
|
||||
"- c #FFB3B3",
|
||||
"; c #FFC8C8",
|
||||
"> c #FF3535",
|
||||
", c #FF8D8D",
|
||||
"' c #FF7878",
|
||||
") c #FF6E6E",
|
||||
"! c #FFB5B5",
|
||||
"~ c #FF0D0D",
|
||||
"{ c #FFF0F0",
|
||||
"] c #FFE8E8",
|
||||
"^ c #FFC2C2",
|
||||
"/ c #FFEDED",
|
||||
"( c #FFBBBB",
|
||||
"_ c #FFB9B9",
|
||||
": c #FFA4A4",
|
||||
"< c #FFFEFE",
|
||||
"[ c #FFD9D9",
|
||||
"} c #FF9393",
|
||||
"| c #FF5858",
|
||||
"1 c #FF3232",
|
||||
"2 c #FF7575",
|
||||
"3 c #FFC9C9",
|
||||
"4 c #FFFCFC",
|
||||
"5 c #FFBDBD",
|
||||
"6 c #FF3838",
|
||||
"7 c #FF9494",
|
||||
"8 c #FFE2E2",
|
||||
"9 c #FFD1D1",
|
||||
"0 c #FFDEDE",
|
||||
"a c #FFCACA",
|
||||
"b c #FF6969",
|
||||
"c c #FF8484",
|
||||
"d c #FFEAEA",
|
||||
"e c #FFE9E9",
|
||||
"f c #FF3B3B",
|
||||
"g c #FFC0C0",
|
||||
"h c #FF6868",
|
||||
"i c #FF7373",
|
||||
"j c #FFF6F6",
|
||||
"k c #FFADAD",
|
||||
"l c #FF5D5D",
|
||||
"m c #FF2626",
|
||||
"n c #FF5C5C",
|
||||
"o c #FFABAB",
|
||||
"p c #FFCECE",
|
||||
"q c #FF7070",
|
||||
"r c #FF5555",
|
||||
"s c #FF1C1C",
|
||||
"t c #FFF4F4",
|
||||
"u c #FF8282",
|
||||
"v c #FF6060",
|
||||
"w c #FFE7E7",
|
||||
"x c #FF9D9D",
|
||||
"y c #FF5656",
|
||||
"z c #FF4242",
|
||||
"A c #FF9B9B",
|
||||
"B c #FFD0D0",
|
||||
"C c #FFF8F8",
|
||||
"D c #FF6A6A",
|
||||
"E c #FF5151",
|
||||
"F c #FFFBFB",
|
||||
"G c #FF4949",
|
||||
"H c #FFCDCD",
|
||||
"I c #FFDDDD",
|
||||
"J c #FF9E9E",
|
||||
"K c #FFF9F9",
|
||||
"L c #FFDCDC",
|
||||
"M c #FF8F8F",
|
||||
"N c #FFCBCB",
|
||||
"O c #FFF5F5",
|
||||
"P c #FF4747",
|
||||
"Q c #FF9C9C",
|
||||
"R c #FFEEEE",
|
||||
"S c #FFFAFA",
|
||||
"T c #FF1616",
|
||||
"U c #FF8888",
|
||||
"V c #FFC5C5",
|
||||
"W c #FF2222",
|
||||
"X c #FF4B4B",
|
||||
"Y c #FFB8B8",
|
||||
"Z c #FF7F7F",
|
||||
"` c #FFE6E6",
|
||||
" . c #FF8080",
|
||||
".. c #FFB4B4",
|
||||
"+. c #FFC3C3",
|
||||
"@. c #FFD2D2",
|
||||
"#. c #FFD7D7",
|
||||
"$. c #FFDFDF",
|
||||
"%. c #FFB7B7",
|
||||
"&. c #FFF1F1",
|
||||
"*. c #FF6262",
|
||||
"=. c #FF8A8A",
|
||||
"-. c #FFA9A9",
|
||||
";. c #FFAEAE",
|
||||
">. c #FFAAAA",
|
||||
",. c #FF8B8B",
|
||||
"'. c #FF4F4F",
|
||||
"). c #FFFDFD",
|
||||
"!. c #FFA3A3",
|
||||
"~. c #FF2A2A",
|
||||
"{. c #FFCFCF",
|
||||
"]. c #FF8585",
|
||||
"^. c #FF7676",
|
||||
"/. c #FFD3D3",
|
||||
"(. c #FFD5D5",
|
||||
"_. c #FF8181",
|
||||
":. c #FFC6C6",
|
||||
"<. c #FFDBDB",
|
||||
"[. c #FF9090",
|
||||
"}. c #FFAFAF",
|
||||
"|. c #FFA1A1",
|
||||
"1. c #FFBABA",
|
||||
"2. c #FF6C6C",
|
||||
"3. c #FF5F5F",
|
||||
"4. c #FF3D3D",
|
||||
"5. c #FF9999",
|
||||
"6. c #FFE0E0",
|
||||
"7. c #FF8383",
|
||||
"8. c #FFEFEF",
|
||||
"9. c #FFF3F3",
|
||||
"0. c #FFA8A8",
|
||||
"a. c #FFB6B6",
|
||||
"b. c #FF9F9F",
|
||||
"c. c #FF4545",
|
||||
"d. c #FFE5E5",
|
||||
"e. c #FFE4E4",
|
||||
"f. c #FFC7C7",
|
||||
"g. c #FF6565",
|
||||
"h. c #FFACAC",
|
||||
"i. c #FF5A5A",
|
||||
"j. c #FF7272",
|
||||
"k. c #FF7C7C",
|
||||
"l. c #FFBFBF",
|
||||
"m. c #FF7171",
|
||||
"n. c #FFECEC",
|
||||
"o. c #FF8989",
|
||||
"p. c #FF7777",
|
||||
"q. c #FFC4C4",
|
||||
"r. c #FF9898",
|
||||
"s. c #FF8C8C",
|
||||
"t. c #FF7A7A",
|
||||
"u. c #FF8E8E",
|
||||
"v. c #FFF2F2",
|
||||
"w. c #FF9797",
|
||||
"x. c #FFC1C1",
|
||||
"y. c #FFA6A6",
|
||||
"z. c #FFEBEB",
|
||||
"A. c #FF4040",
|
||||
"B. c #EDEDED",
|
||||
"C. c #000000",
|
||||
"D. c #AAAAAA",
|
||||
"E. c #F6F6F6",
|
||||
"F. c #1C1C1C",
|
||||
"G. c #888888",
|
||||
"H. c #7C7C7C",
|
||||
"I. c #626262",
|
||||
"J. c #B3B3B3",
|
||||
"K. c #2A2A2A",
|
||||
"L. c #959595",
|
||||
"M. c #FDFDFD",
|
||||
"N. c #C5C5C5",
|
||||
"O. c #666666",
|
||||
"P. c #353535",
|
||||
"Q. c #777777",
|
||||
"R. c #DEDEDE",
|
||||
"S. c #6C6C6C",
|
||||
"T. c #F5F5F5",
|
||||
"U. c #ADADAD",
|
||||
"V. c #DDDDDD",
|
||||
"W. c #D8D8D8",
|
||||
"X. c #B4B4B4",
|
||||
"Y. c #FAFAFA",
|
||||
"Z. c #949494",
|
||||
"`. c #3B3B3B",
|
||||
" + c #A8A8A8",
|
||||
".+ c #C8C8C8",
|
||||
"++ c #D4D4D4",
|
||||
"@+ c #B9B9B9",
|
||||
"#+ c #2E2E2E",
|
||||
"$+ c #FEFEFE",
|
||||
"%+ c #BABABA",
|
||||
"&+ c #FCFCFC",
|
||||
"*+ c #B2B2B2",
|
||||
"=+ c #CACACA",
|
||||
"-+ c #696969",
|
||||
";+ c #222222",
|
||||
">+ c #F2F2F2",
|
||||
",+ c #555555",
|
||||
"'+ c #C4C4C4",
|
||||
")+ c #EBEBEB",
|
||||
"!+ c #727272",
|
||||
"~+ c #585858",
|
||||
"{+ c #0D0D0D",
|
||||
"]+ c #B1B1B1",
|
||||
"^+ c #E5E5E5",
|
||||
"/+ c #C0C0C0",
|
||||
"(+ c #8F8F8F",
|
||||
"_+ c #4D4D4D",
|
||||
":+ c #F4F4F4",
|
||||
"<+ c #7D7D7D",
|
||||
"[+ c #E4E4E4",
|
||||
"}+ c #F3F3F3",
|
||||
"|+ c #383838",
|
||||
"1+ c #A9A9A9",
|
||||
"2+ c #D6D6D6",
|
||||
"3+ c #D5D5D5",
|
||||
"4+ c #5F5F5F",
|
||||
"5+ c #C6C6C6",
|
||||
"6+ c #E2E2E2",
|
||||
"7+ c #FBFBFB",
|
||||
"8+ c #404040",
|
||||
"9+ c #909090",
|
||||
"0+ c #EEEEEE",
|
||||
"a+ c #878787",
|
||||
"b+ c #E8E8E8",
|
||||
"c+ c #494949",
|
||||
"d+ c #424242",
|
||||
"e+ c #E6E6E6",
|
||||
"f+ c #CFCFCF",
|
||||
"g+ c #DCDCDC",
|
||||
"h+ c #161616",
|
||||
"i+ c #BBBBBB",
|
||||
"j+ c #CCCCCC",
|
||||
"k+ c #B0B0B0",
|
||||
"l+ c #C7C7C7",
|
||||
"m+ c #858585",
|
||||
"n+ c #F8F8F8",
|
||||
"o+ c #D7D7D7",
|
||||
"p+ c #BDBDBD",
|
||||
"q+ c #ECECEC",
|
||||
"r+ c #939393",
|
||||
"s+ c #A1A1A1",
|
||||
"t+ c #7A7A7A",
|
||||
"u+ c #4B4B4B",
|
||||
"v+ c #E9E9E9",
|
||||
"w+ c #717171",
|
||||
"x+ c #AFAFAF",
|
||||
"y+ c #454545",
|
||||
"z+ c #F9F9F9",
|
||||
"A+ c #DBDBDB",
|
||||
"B+ c #C1C1C1",
|
||||
"C+ c #707070",
|
||||
"D+ c #323232",
|
||||
"E+ c #9D9D9D",
|
||||
"F+ c #D1D1D1",
|
||||
"G+ c #6D6D6D",
|
||||
"H+ c #262626",
|
||||
"I+ c #6E6E6E",
|
||||
"J+ c #808080",
|
||||
"K+ c #BFBFBF",
|
||||
"L+ c #999999",
|
||||
"M+ c #F1F1F1",
|
||||
"N+ c #DADADA",
|
||||
"O+ c #9F9F9F",
|
||||
"P+ c #8B8B8B",
|
||||
"Q+ c #7F7F7F",
|
||||
"R+ c #9E9E9E",
|
||||
"S+ c #F0F0F0",
|
||||
"T+ c #A4A4A4",
|
||||
"U+ c #A5A5A5",
|
||||
"V+ c #CDCDCD",
|
||||
"W+ c #CBCBCB",
|
||||
"X+ c #9B9B9B",
|
||||
"Y+ c #D9D9D9",
|
||||
"Z+ c #A0A0A0",
|
||||
"`+ c #9C9C9C",
|
||||
" @ c #C2C2C2",
|
||||
".@ c #636363",
|
||||
"+@ c #D0D0D0",
|
||||
"@@ c #6A6A6A",
|
||||
"#@ c #898989",
|
||||
"$@ c #C3C3C3",
|
||||
"%@ c #A7A7A7",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . $ % @ & . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . ; @ > , . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . ' ) ! ~ { . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . ] @ ^ / @ ( . . . _ @ @ @ @ @ : . + @ # . . < [ } | 1 2 3 . . . . 4 5 ) 6 | 7 8 . . . . . . . . = @ - . . 9 @ 0 a b > c d . e , f | g . . . . 9 @ 0 a h % i & . . . . j k l m n o j . . . 9 @ 0 p q m r @ @ @ @ @ : . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . k s t . u v < . . _ @ @ @ @ @ : . + @ # . . w @ @ @ @ @ @ 5 . . . k @ @ @ @ @ % . . . . . . . . = @ - . . 9 @ x @ @ @ @ y d z @ @ @ @ * . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h C . . 9 @ x @ @ @ E @ @ @ @ @ : . . . . . . . . . . ",
|
||||
". . . . . . . . . . . F G 7 . . H @ I . . . * @ + . . . . + @ # . . ] J 8 j K 0 h 6 K . . l l = F j L M . . . . . . . . = @ - . . 9 @ % N j O J @ P Q R S & T U . . . 9 @ s ^ O j V W X F . Y @ Z ` S w .@ ... . 9 @ s +.t . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . @.@ #.. . K 6 x . . . * @ + . . . . + @ # . . . . . . . . # @ $.. . ' M . . . . . . . . . . . . . = @ - . . 9 @ %.. . . &.@ *.4 . . . =.r . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . t f h . . 9 @ >.. . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . ,.'.).. . . !.~.j . . * @ + . . . . + @ # . . . {.].P ~ @ @ @ 9 . . 8 r ^.- /.j . . . . . . . . . = @ - . . 9 @ + . . . . @ A . . . . !.~ . . . 9 @ (.. . . . # @ N . 1 _.. . . . . c s . . 9 @ (.. . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . { ~ @ @ @ @ @ @ @ :.. . * @ + . . . . + @ # . . ^ @ @ @ @ @ @ @ H . . . F <._ [.> }.. . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ (.. . . . # @ N . 1 u . . . . . c s . . 9 @ 0 . . . . * @ + . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . 1.@ @ @ @ @ @ @ @ ^.. . #.@ # . . . . + @ # . . 2.r $.$ < . [ @ H . . . . . . . + @ * . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . O f h . . 9 @ 0 . . . . #.@ # . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . < 3.4.K . . . . . 5.@ w . 6.@ ;.F . . . + @ # . . 2.n ` 4 $ + *.@ H . 4 7.@.8.4 9.k @ ^ . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ s ^ O j V W X F . %.@ u w F ] u @ - . . 9 @ 0 . . . . 6.@ ;.F . . . . . . . . . . . . ",
|
||||
". . . . . . . . . L @ 0.. . . . . . L @ o . K > @ @ @ : . + @ # . . a.@ @ @ @ @ b.@ H . F @ @ @ @ @ @ > / . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h $ . . 9 @ 0 . . . . K > @ @ @ : . . . . . . . . . . ",
|
||||
". . . . . . . . . A @ ` . . . . . . < *.c.F . # ^.~.@ : . + @ # . . 4 _ *.% q N d.@ H . . e.: h % l b.e . . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ 0 f.g.~.i /.. . . . j h.n W i.>.O . . . 9 @ 0 . . . . . # ^.~.@ : . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . < [ } | 1 2 3 . . . . + @ # . . + @ # . . . 4 l.m.% G 5.n.. . . . &.Q z c.x O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . w @ @ @ @ @ @ 5 . . . + @ # . . + @ # . . ).o.@ @ @ @ @ c./ . . $ v @ @ @ @ p.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U ] J 8 j K 0 h 6 K . . + @ # . . + @ # . . q.@ } w F O q.s r.. . a.@ s.d S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U . . . . . . # @ $.. . + @ # . . + @ # . . t.| ).. . . . !.> < . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . {.].P ~ @ @ @ 9 . . + @ # . . + @ # . . > @ @ @ @ @ @ @ @ O . % u.. . . . . y k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . ^ @ @ @ @ @ @ @ H . . + @ # . . + @ # . . 1 @ @ @ @ @ @ @ @ v.. % u.. . . . . r k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.r $.$ < . [ @ H . . + @ # . . + @ # . . ^.h < . . . . . . . . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.n ` 4 $ + *.@ H . . + @ # . . + @ # . . +.@ w.e.C S d x.y.. . a.@ s.z.S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . a.@ @ @ @ @ b.@ H . . + @ # . . + @ # . . ).M @ @ @ @ @ @ s.. . $ v @ @ @ @ ^.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 4 _ *.% q N d.@ H . . + @ # . . + @ # . . . ).f.k.6 z ' Y v.. . . &.A A.z A O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.B.C.D.. . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.H.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . '+C.C.)+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . !+~+{+]+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. ^+C./+(+_+&+. I.C.C.C.J.B.C.D.:+C.C.P.<+[+. }+G.|+C.C.>+. . . E.C.H.. . . . B.C.D.B.C.D.. N.O.P.Q.R.. Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1+;+T.2+C.3+. . C.G.. . B.C.D.. . . T.4+Q.. 5+C.X.6+Y.. . . . E.C.C.C.C.H.. B.C.D.B.C.D.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7+8+C.C.C.C.9+. . C.G.. . B.C.D.0+a+8+C.C.|+. b+c+C.C.d+e+. . . E.C.H.. . . . B.C.D.B.C.D.@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . f+C.g+. . J.h+>+. #+Q.$+. B.C.D.i+C.j+0+,+#+. . &+e+k+C.l+. . . E.C.H.. . . . B.C.D.B.C.D.W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . m+h+n+. . o+C.p+. *+`.C.=+B.C.D.q+!+{+C.C.#+. =+C.C.8+r+E.. . . E.C.H.. . . . B.C.D.B.C.D.. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . V.4+C.s+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . t+u+Y.v+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u+w+. . . . . . . . . . . B.C.D.. . . . N.C.W.. . . . . . . . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H.C.C.C.C.C.U.. N.O.P.Q.R.. B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.C.C.K.L.M.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.X.Y.Z.`.E.B.C.D.2+C.x+z+(+C.W.. . . B.C.D.B.C.S.T.U.C.V.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.@+C.C.C.C.C.R.B.C.D.%+C.A+. B+C.W.. . . B.C.D.B.C. +. .+C.++. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.%+&+. . . B.C.D.2+C.k+z+9+C.W.. . . B.C.D.B.C.D.. .+C.++. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.. N.-+;+C.C.Y.B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.D.. .+C.++. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . x+d+C.C.C.W.. '+O.P.C+F+. . .+G+H+C.R.B.C.D.>+I+I.q+. . . :+C.C.P.<+[+. B.C.C.C.K.L.M.. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . 2+C.x+z+(+C.W.W.C.]+Y.L+h+b+N+C.O+z+. . B.C.P+4+Q+T.. . . . . . . T.4+Q.. B.C.S.T.U.C.V.2+C.x+z+(+C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . %+C.A+. B+C.W.@+C.V.. =+C.V+@+C.A+. . . B.C.C.C.R.. . . . . 0+a+8+C.C.|+. B.C. +. .+C.++%+C.A+. B+C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . 2+C.k+z+9+C.W.W.C.*+Y.X+{+b+Y+C.s+z+. . B.C.Z+m+|+V.. . . . i+C.j+0+,+#+. B.C.D.. .+C.++2+C.k+z+9+C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . x+d+C.C.C.W.. @.@D+G++@. . l+@@H+C.R.B.C.D.Y.#@K.2+. . . q+!+{+C.C.#+. B.C.D.. .+C.++. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.C.C.,+. N.O.P.Q.R.. . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.O.M+. W.C.X.Y.Z.`.E.$@C.C.%@B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C. +. . @+C.C.C.C.C.R.. . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . W.C.%+&+. . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . N.-+;+C.C.Y.. . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ",
|
||||
". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "};
|
||||
660
engine/editor/import/audio_stream_import_settings.cpp
Normal file
660
engine/editor/import/audio_stream_import_settings.cpp
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_import_settings.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 "audio_stream_import_settings.h"
|
||||
#include "editor/audio_stream_preview.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
|
||||
AudioStreamImportSettingsDialog *AudioStreamImportSettingsDialog::singleton = nullptr;
|
||||
|
||||
void AudioStreamImportSettingsDialog::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AudioStreamImportSettingsDialog::_preview_changed));
|
||||
connect(SceneStringName(confirmed), callable_mp(this, &AudioStreamImportSettingsDialog::_reimport));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_stop_button->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
|
||||
_preview->set_color(get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor)));
|
||||
color_rect->set_color(get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor)));
|
||||
|
||||
_current_label->begin_bulk_theme_override();
|
||||
_current_label->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts)));
|
||||
_current_label->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)));
|
||||
_current_label->end_bulk_theme_override();
|
||||
|
||||
_duration_label->begin_bulk_theme_override();
|
||||
_duration_label->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts)));
|
||||
_duration_label->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)));
|
||||
_duration_label->end_bulk_theme_override();
|
||||
|
||||
zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore")));
|
||||
zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess")));
|
||||
zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset")));
|
||||
|
||||
_indicator->queue_redraw();
|
||||
_preview->queue_redraw();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
_current = _player->get_playback_position();
|
||||
_indicator->queue_redraw();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
_stop();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_draw_preview() {
|
||||
Rect2 rect = _preview->get_rect();
|
||||
Size2 rect_size = rect.size;
|
||||
int width = rect_size.width;
|
||||
|
||||
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
|
||||
float preview_offset = zoom_bar->get_value();
|
||||
float preview_len = zoom_bar->get_page();
|
||||
|
||||
Ref<Font> beat_font = get_theme_font(SNAME("main"), EditorStringName(EditorFonts));
|
||||
int main_size = get_theme_font_size(SNAME("main_size"), EditorStringName(EditorFonts));
|
||||
Vector<Vector2> points;
|
||||
points.resize(width * 2);
|
||||
Color color_active = get_theme_color(SNAME("contrast_color_2"), EditorStringName(Editor));
|
||||
Color color_inactive = color_active;
|
||||
color_inactive.a *= 0.5;
|
||||
Vector<Color> colors;
|
||||
colors.resize(width);
|
||||
|
||||
float inactive_from = 1e20;
|
||||
float beat_size = 0;
|
||||
int last_beat = 0;
|
||||
if (stream->get_bpm() > 0) {
|
||||
beat_size = 60 / float(stream->get_bpm());
|
||||
int y_ofs = beat_font->get_height(main_size) + 4 * EDSCALE;
|
||||
rect.position.y += y_ofs;
|
||||
rect.size.y -= y_ofs;
|
||||
|
||||
if (stream->get_beat_count() > 0) {
|
||||
last_beat = stream->get_beat_count();
|
||||
inactive_from = last_beat * beat_size;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
float ofs = preview_offset + i * preview_len / rect_size.width;
|
||||
float ofs_n = preview_offset + (i + 1) * preview_len / rect_size.width;
|
||||
float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
|
||||
float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
|
||||
|
||||
int idx = i;
|
||||
points.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y);
|
||||
points.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y);
|
||||
|
||||
colors.write[idx] = ofs > inactive_from ? color_inactive : color_active;
|
||||
}
|
||||
|
||||
if (!points.is_empty()) {
|
||||
RS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), points, colors);
|
||||
}
|
||||
|
||||
if (beat_size) {
|
||||
Color beat_color = Color(1, 1, 1, 1);
|
||||
Color final_beat_color = beat_color;
|
||||
Color bar_color = beat_color;
|
||||
beat_color.a *= 0.4;
|
||||
bar_color.a *= 0.6;
|
||||
|
||||
int prev_beat = 0; // Do not draw beat zero
|
||||
Color color_bg = color_active;
|
||||
color_bg.a *= 0.2;
|
||||
_preview->draw_rect(Rect2(0, 0, rect.size.width, rect.position.y), color_bg);
|
||||
int bar_beats = stream->get_bar_beats();
|
||||
|
||||
int last_text_end_x = 0;
|
||||
for (int i = 0; i < width; i++) {
|
||||
float ofs = preview_offset + i * preview_len / rect_size.width;
|
||||
int beat = int(ofs / beat_size);
|
||||
if (beat != prev_beat) {
|
||||
String text = itos(beat);
|
||||
int text_w = beat_font->get_string_size(text).width;
|
||||
if (i - text_w / 2 > last_text_end_x + 2 * EDSCALE) {
|
||||
int x_ofs = i - text_w / 2;
|
||||
_preview->draw_string(beat_font, Point2(x_ofs, 2 * EDSCALE + beat_font->get_ascent(main_size)), text, HORIZONTAL_ALIGNMENT_LEFT, rect.size.width - x_ofs, Font::DEFAULT_FONT_SIZE, color_active);
|
||||
last_text_end_x = i + text_w / 2;
|
||||
}
|
||||
|
||||
if (beat == last_beat) {
|
||||
_preview->draw_rect(Rect2i(i, rect.position.y, 2, rect.size.height), final_beat_color);
|
||||
// Darken subsequent beats
|
||||
beat_color.a *= 0.3;
|
||||
color_active.a *= 0.3;
|
||||
} else {
|
||||
_preview->draw_rect(Rect2i(i, rect.position.y, 1, rect.size.height), (beat % bar_beats) == 0 ? bar_color : beat_color);
|
||||
}
|
||||
prev_beat = beat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_changed(ObjectID p_which) {
|
||||
if (stream.is_valid() && stream->get_instance_id() == p_which) {
|
||||
_preview->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_in() {
|
||||
if (!stream.is_valid()) {
|
||||
return;
|
||||
}
|
||||
float page_size = zoom_bar->get_page();
|
||||
zoom_bar->set_page(page_size * 0.5);
|
||||
zoom_bar->set_value(zoom_bar->get_value() + page_size * 0.25);
|
||||
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_out() {
|
||||
if (!stream.is_valid()) {
|
||||
return;
|
||||
}
|
||||
float page_size = zoom_bar->get_page();
|
||||
zoom_bar->set_page(MIN(zoom_bar->get_max(), page_size * 2.0));
|
||||
zoom_bar->set_value(zoom_bar->get_value() - page_size * 0.5);
|
||||
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_reset() {
|
||||
if (!stream.is_valid()) {
|
||||
return;
|
||||
}
|
||||
zoom_bar->set_max(stream->get_length());
|
||||
zoom_bar->set_page(zoom_bar->get_max());
|
||||
zoom_bar->set_value(0);
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_offset_changed(double) {
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_audio_changed() {
|
||||
if (!is_visible()) {
|
||||
return;
|
||||
}
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
color_rect->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_play() {
|
||||
if (_player->is_playing()) {
|
||||
// '_pausing' variable indicates that we want to pause the audio player, not stop it. See '_on_finished()'.
|
||||
_pausing = true;
|
||||
_player->stop();
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
} else {
|
||||
_player->play(_current);
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_stop() {
|
||||
_player->stop();
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_on_finished() {
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
if (!_pausing) {
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
} else {
|
||||
_pausing = false;
|
||||
}
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_draw_indicator() {
|
||||
if (!stream.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect2 rect = _preview->get_rect();
|
||||
|
||||
Ref<Font> beat_font = get_theme_font(SNAME("main"), EditorStringName(EditorFonts));
|
||||
int main_size = get_theme_font_size(SNAME("main_size"), EditorStringName(EditorFonts));
|
||||
|
||||
if (stream->get_bpm() > 0) {
|
||||
int y_ofs = beat_font->get_height(main_size) + 4 * EDSCALE;
|
||||
rect.position.y += y_ofs;
|
||||
rect.size.height -= y_ofs;
|
||||
}
|
||||
|
||||
_current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
|
||||
|
||||
float ofs_x = (_current - zoom_bar->get_value()) * rect.size.width / zoom_bar->get_page();
|
||||
if (ofs_x < 0 || ofs_x >= rect.size.width) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
_indicator->draw_line(Point2(ofs_x, rect.position.y), Point2(ofs_x, rect.position.y + rect.size.height), color, Math::round(2 * EDSCALE));
|
||||
_indicator->draw_texture(
|
||||
get_editor_theme_icon(SNAME("TimelineIndicator")),
|
||||
Point2(ofs_x - get_editor_theme_icon(SNAME("TimelineIndicator"))->get_width() * 0.5, rect.position.y),
|
||||
color);
|
||||
|
||||
if (stream->get_bpm() > 0 && _hovering_beat != -1) {
|
||||
// Draw hovered beat.
|
||||
float preview_offset = zoom_bar->get_value();
|
||||
float preview_len = zoom_bar->get_page();
|
||||
float beat_size = 60 / float(stream->get_bpm());
|
||||
int prev_beat = 0;
|
||||
for (int i = 0; i < rect.size.width; i++) {
|
||||
float ofs = preview_offset + i * preview_len / rect.size.width;
|
||||
int beat = int(ofs / beat_size);
|
||||
if (beat != prev_beat) {
|
||||
String text = itos(beat);
|
||||
int text_w = beat_font->get_string_size(text).width;
|
||||
if (i - text_w / 2 > 2 * EDSCALE && beat == _hovering_beat) {
|
||||
int x_ofs = i - text_w / 2;
|
||||
_indicator->draw_string(beat_font, Point2(x_ofs, 2 * EDSCALE + beat_font->get_ascent(main_size)), text, HORIZONTAL_ALIGNMENT_LEFT, rect.size.width - x_ofs, Font::DEFAULT_FONT_SIZE, color);
|
||||
break;
|
||||
}
|
||||
prev_beat = beat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_on_indicator_mouse_exited() {
|
||||
_hovering_beat = -1;
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_on_input_indicator(Ref<InputEvent> p_event) {
|
||||
const Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (stream->get_bpm() > 0) {
|
||||
int main_size = get_theme_font_size(SNAME("main_size"), EditorStringName(EditorFonts));
|
||||
Ref<Font> beat_font = get_theme_font(SNAME("main"), EditorStringName(EditorFonts));
|
||||
int y_ofs = beat_font->get_height(main_size) + 4 * EDSCALE;
|
||||
if ((!_dragging && mb->get_position().y < y_ofs) || _beat_len_dragging) {
|
||||
if (mb->is_pressed()) {
|
||||
_set_beat_len_to(mb->get_position().x);
|
||||
_beat_len_dragging = true;
|
||||
} else {
|
||||
_beat_len_dragging = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mb->is_pressed()) {
|
||||
_seek_to(mb->get_position().x);
|
||||
}
|
||||
_dragging = mb->is_pressed();
|
||||
}
|
||||
|
||||
const Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
if (_dragging) {
|
||||
_seek_to(mm->get_position().x);
|
||||
}
|
||||
if (_beat_len_dragging) {
|
||||
_set_beat_len_to(mm->get_position().x);
|
||||
}
|
||||
if (stream->get_bpm() > 0) {
|
||||
int main_size = get_theme_font_size(SNAME("main_size"), EditorStringName(EditorFonts));
|
||||
Ref<Font> beat_font = get_theme_font(SNAME("main"), EditorStringName(EditorFonts));
|
||||
int y_ofs = beat_font->get_height(main_size) + 4 * EDSCALE;
|
||||
if (mm->get_position().y < y_ofs) {
|
||||
int new_hovering_beat = _get_beat_at_pos(mm->get_position().x);
|
||||
if (new_hovering_beat != _hovering_beat) {
|
||||
_hovering_beat = new_hovering_beat;
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
} else if (_hovering_beat != -1) {
|
||||
_hovering_beat = -1;
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AudioStreamImportSettingsDialog::_get_beat_at_pos(real_t p_x) {
|
||||
float ofs_sec = zoom_bar->get_value() + p_x * zoom_bar->get_page() / _preview->get_size().width;
|
||||
ofs_sec = CLAMP(ofs_sec, 0, stream->get_length());
|
||||
float beat_size = 60 / float(stream->get_bpm());
|
||||
int beat = int(ofs_sec / beat_size + 0.5);
|
||||
|
||||
if (beat * beat_size > stream->get_length() + 0.001) { // Stream may end few audio frames before but may still want to use full loop.
|
||||
beat--;
|
||||
}
|
||||
return beat;
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_set_beat_len_to(real_t p_x) {
|
||||
int beat = _get_beat_at_pos(p_x);
|
||||
if (beat < 1) {
|
||||
beat = 1; // Because 0 is disable.
|
||||
}
|
||||
updating_settings = true;
|
||||
beats_enabled->set_pressed(true);
|
||||
beats_edit->set_value(beat);
|
||||
updating_settings = false;
|
||||
_settings_changed();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_seek_to(real_t p_x) {
|
||||
_current = zoom_bar->get_value() + p_x / _preview->get_rect().size.x * zoom_bar->get_page();
|
||||
_current = CLAMP(_current, 0, stream->get_length());
|
||||
_player->seek(_current);
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::edit(const String &p_path, const String &p_importer, const Ref<AudioStream> &p_stream) {
|
||||
if (!stream.is_null()) {
|
||||
stream->disconnect_changed(callable_mp(this, &AudioStreamImportSettingsDialog::_audio_changed));
|
||||
}
|
||||
|
||||
importer = p_importer;
|
||||
path = p_path;
|
||||
|
||||
stream = p_stream;
|
||||
_player->set_stream(stream);
|
||||
_current = 0;
|
||||
String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s";
|
||||
_duration_label->set_text(text);
|
||||
|
||||
if (!stream.is_null()) {
|
||||
stream->connect_changed(callable_mp(this, &AudioStreamImportSettingsDialog::_audio_changed));
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
color_rect->queue_redraw();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
params.clear();
|
||||
|
||||
if (stream.is_valid()) {
|
||||
Ref<ConfigFile> config_file;
|
||||
config_file.instantiate();
|
||||
Error err = config_file->load(p_path + ".import");
|
||||
updating_settings = true;
|
||||
if (err == OK) {
|
||||
double bpm = config_file->get_value("params", "bpm", 0);
|
||||
int beats = config_file->get_value("params", "beat_count", 0);
|
||||
bpm_edit->set_value(bpm > 0 ? bpm : 120);
|
||||
bpm_enabled->set_pressed(bpm > 0);
|
||||
beats_edit->set_value(beats);
|
||||
beats_enabled->set_pressed(beats > 0);
|
||||
loop->set_pressed(config_file->get_value("params", "loop", false));
|
||||
loop_offset->set_value(config_file->get_value("params", "loop_offset", 0));
|
||||
bar_beats_edit->set_value(config_file->get_value("params", "bar_beats", 4));
|
||||
|
||||
List<String> keys;
|
||||
config_file->get_section_keys("params", &keys);
|
||||
for (const String &K : keys) {
|
||||
params[K] = config_file->get_value("params", K);
|
||||
}
|
||||
} else {
|
||||
bpm_edit->set_value(false);
|
||||
bpm_enabled->set_pressed(false);
|
||||
beats_edit->set_value(0);
|
||||
beats_enabled->set_pressed(false);
|
||||
bar_beats_edit->set_value(4);
|
||||
loop->set_pressed(false);
|
||||
loop_offset->set_value(0);
|
||||
}
|
||||
|
||||
_preview_zoom_reset();
|
||||
updating_settings = false;
|
||||
_settings_changed();
|
||||
|
||||
set_title(vformat(TTR("Audio Stream Importer: %s"), p_path.get_file()));
|
||||
popup_centered();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_settings_changed() {
|
||||
if (updating_settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating_settings = true;
|
||||
stream->call("set_loop", loop->is_pressed());
|
||||
stream->call("set_loop_offset", loop_offset->get_value());
|
||||
if (loop->is_pressed()) {
|
||||
loop_offset->set_editable(true);
|
||||
} else {
|
||||
loop_offset->set_editable(false);
|
||||
}
|
||||
|
||||
if (bpm_enabled->is_pressed()) {
|
||||
stream->call("set_bpm", bpm_edit->get_value());
|
||||
beats_enabled->set_disabled(false);
|
||||
beats_edit->set_editable(true);
|
||||
bar_beats_edit->set_editable(true);
|
||||
double bpm = bpm_edit->get_value();
|
||||
if (bpm > 0) {
|
||||
float beat_size = 60 / float(bpm);
|
||||
int beat_max = int((stream->get_length() + 0.001) / beat_size);
|
||||
int current_beat = beats_edit->get_value();
|
||||
beats_edit->set_max(beat_max);
|
||||
if (current_beat > beat_max) {
|
||||
beats_edit->set_value(beat_max);
|
||||
stream->call("set_beat_count", beat_max);
|
||||
}
|
||||
}
|
||||
stream->call("set_bar_beats", bar_beats_edit->get_value());
|
||||
} else {
|
||||
stream->call("set_bpm", 0);
|
||||
stream->call("set_bar_beats", 4);
|
||||
beats_enabled->set_disabled(true);
|
||||
beats_edit->set_editable(false);
|
||||
bar_beats_edit->set_editable(false);
|
||||
}
|
||||
if (bpm_enabled->is_pressed() && beats_enabled->is_pressed()) {
|
||||
stream->call("set_beat_count", beats_edit->get_value());
|
||||
} else {
|
||||
stream->call("set_beat_count", 0);
|
||||
}
|
||||
|
||||
updating_settings = false;
|
||||
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
color_rect->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_reimport() {
|
||||
params["loop"] = loop->is_pressed();
|
||||
params["loop_offset"] = loop_offset->get_value();
|
||||
params["bpm"] = bpm_enabled->is_pressed() ? double(bpm_edit->get_value()) : double(0);
|
||||
params["beat_count"] = (bpm_enabled->is_pressed() && beats_enabled->is_pressed()) ? int(beats_edit->get_value()) : int(0);
|
||||
params["bar_beats"] = (bpm_enabled->is_pressed()) ? int(bar_beats_edit->get_value()) : int(4);
|
||||
|
||||
EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(path, importer, params);
|
||||
}
|
||||
|
||||
AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
|
||||
get_ok_button()->set_text(TTR("Reimport"));
|
||||
get_cancel_button()->set_text(TTR("Close"));
|
||||
|
||||
VBoxContainer *main_vbox = memnew(VBoxContainer);
|
||||
add_child(main_vbox);
|
||||
|
||||
HBoxContainer *loop_hb = memnew(HBoxContainer);
|
||||
loop_hb->add_theme_constant_override("separation", 4 * EDSCALE);
|
||||
loop = memnew(CheckBox);
|
||||
loop->set_text(TTR("Enable"));
|
||||
loop->set_tooltip_text(TTR("Enable looping."));
|
||||
loop->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
loop_hb->add_child(loop);
|
||||
loop_hb->add_spacer();
|
||||
loop_hb->add_child(memnew(Label(TTR("Offset:"))));
|
||||
loop_offset = memnew(SpinBox);
|
||||
loop_offset->set_max(10000);
|
||||
loop_offset->set_step(0.001);
|
||||
loop_offset->set_suffix("sec");
|
||||
loop_offset->set_tooltip_text(TTR("Loop offset (from beginning). Note that if BPM is set, this setting will be ignored."));
|
||||
loop_offset->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
loop_hb->add_child(loop_offset);
|
||||
main_vbox->add_margin_child(TTR("Loop:"), loop_hb);
|
||||
|
||||
HBoxContainer *interactive_hb = memnew(HBoxContainer);
|
||||
interactive_hb->add_theme_constant_override("separation", 4 * EDSCALE);
|
||||
bpm_enabled = memnew(CheckBox);
|
||||
bpm_enabled->set_text((TTR("BPM:")));
|
||||
bpm_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(bpm_enabled);
|
||||
bpm_edit = memnew(SpinBox);
|
||||
bpm_edit->set_max(400);
|
||||
bpm_edit->set_step(0.01);
|
||||
bpm_edit->set_tooltip_text(TTR("Configure the Beats Per Measure (tempo) used for the interactive streams.\nThis is required in order to configure beat information."));
|
||||
bpm_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(bpm_edit);
|
||||
interactive_hb->add_spacer();
|
||||
beats_enabled = memnew(CheckBox);
|
||||
beats_enabled->set_text(TTR("Beat Count:"));
|
||||
beats_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(beats_enabled);
|
||||
beats_edit = memnew(SpinBox);
|
||||
beats_edit->set_tooltip_text(TTR("Configure the amount of Beats used for music-aware looping. If zero, it will be autodetected from the length.\nIt is recommended to set this value (either manually or by clicking on a beat number in the preview) to ensure looping works properly."));
|
||||
beats_edit->set_max(99999);
|
||||
beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(beats_edit);
|
||||
bar_beats_label = memnew(Label(TTR("Bar Beats:")));
|
||||
interactive_hb->add_child(bar_beats_label);
|
||||
bar_beats_edit = memnew(SpinBox);
|
||||
bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams."));
|
||||
bar_beats_edit->set_min(2);
|
||||
bar_beats_edit->set_max(32);
|
||||
bar_beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(bar_beats_edit);
|
||||
interactive_hb->add_spacer();
|
||||
main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb);
|
||||
|
||||
color_rect = memnew(ColorRect);
|
||||
main_vbox->add_margin_child(TTR("Preview:"), color_rect);
|
||||
|
||||
color_rect->set_custom_minimum_size(Size2(600, 200) * EDSCALE);
|
||||
color_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
_player = memnew(AudioStreamPlayer);
|
||||
_player->connect(SceneStringName(finished), callable_mp(this, &AudioStreamImportSettingsDialog::_on_finished));
|
||||
color_rect->add_child(_player);
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
color_rect->add_child(vbox);
|
||||
vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
_preview = memnew(ColorRect);
|
||||
_preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
_preview->connect(SceneStringName(draw), callable_mp(this, &AudioStreamImportSettingsDialog::_draw_preview));
|
||||
_preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
vbox->add_child(_preview);
|
||||
|
||||
HBoxContainer *zoom_hbox = memnew(HBoxContainer);
|
||||
zoom_bar = memnew(HScrollBar);
|
||||
zoom_in = memnew(Button);
|
||||
zoom_in->set_flat(true);
|
||||
zoom_reset = memnew(Button);
|
||||
zoom_reset->set_flat(true);
|
||||
zoom_out = memnew(Button);
|
||||
zoom_out->set_flat(true);
|
||||
zoom_hbox->add_child(zoom_bar);
|
||||
zoom_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
zoom_bar->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
zoom_hbox->add_child(zoom_out);
|
||||
zoom_hbox->add_child(zoom_reset);
|
||||
zoom_hbox->add_child(zoom_in);
|
||||
zoom_in->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamImportSettingsDialog::_preview_zoom_in));
|
||||
zoom_reset->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamImportSettingsDialog::_preview_zoom_reset));
|
||||
zoom_out->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamImportSettingsDialog::_preview_zoom_out));
|
||||
zoom_bar->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_preview_zoom_offset_changed));
|
||||
vbox->add_child(zoom_hbox);
|
||||
|
||||
_indicator = memnew(Control);
|
||||
_indicator->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
_indicator->connect(SceneStringName(draw), callable_mp(this, &AudioStreamImportSettingsDialog::_draw_indicator));
|
||||
_indicator->connect(SceneStringName(gui_input), callable_mp(this, &AudioStreamImportSettingsDialog::_on_input_indicator));
|
||||
_indicator->connect(SceneStringName(mouse_exited), callable_mp(this, &AudioStreamImportSettingsDialog::_on_indicator_mouse_exited));
|
||||
_preview->add_child(_indicator);
|
||||
|
||||
HBoxContainer *hbox = memnew(HBoxContainer);
|
||||
hbox->add_theme_constant_override("separation", 0);
|
||||
vbox->add_child(hbox);
|
||||
|
||||
_play_button = memnew(Button);
|
||||
_play_button->set_flat(true);
|
||||
hbox->add_child(_play_button);
|
||||
_play_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
_play_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamImportSettingsDialog::_play));
|
||||
|
||||
_stop_button = memnew(Button);
|
||||
_stop_button->set_flat(true);
|
||||
hbox->add_child(_stop_button);
|
||||
_stop_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
_stop_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamImportSettingsDialog::_stop));
|
||||
|
||||
_current_label = memnew(Label);
|
||||
_current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
_current_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
_current_label->set_modulate(Color(1, 1, 1, 0.5));
|
||||
hbox->add_child(_current_label);
|
||||
|
||||
_duration_label = memnew(Label);
|
||||
hbox->add_child(_duration_label);
|
||||
|
||||
singleton = this;
|
||||
}
|
||||
117
engine/editor/import/audio_stream_import_settings.h
Normal file
117
engine/editor/import/audio_stream_import_settings.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_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 AUDIO_STREAM_IMPORT_SETTINGS_H
|
||||
#define AUDIO_STREAM_IMPORT_SETTINGS_H
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/audio/audio_stream_player.h"
|
||||
#include "scene/gui/color_rect.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
class CheckBox;
|
||||
|
||||
class AudioStreamImportSettingsDialog : public ConfirmationDialog {
|
||||
GDCLASS(AudioStreamImportSettingsDialog, ConfirmationDialog);
|
||||
|
||||
CheckBox *bpm_enabled = nullptr;
|
||||
SpinBox *bpm_edit = nullptr;
|
||||
CheckBox *beats_enabled = nullptr;
|
||||
SpinBox *beats_edit = nullptr;
|
||||
Label *bar_beats_label = nullptr;
|
||||
SpinBox *bar_beats_edit = nullptr;
|
||||
CheckBox *loop = nullptr;
|
||||
SpinBox *loop_offset = nullptr;
|
||||
ColorRect *color_rect = nullptr;
|
||||
Ref<AudioStream> stream;
|
||||
AudioStreamPlayer *_player = nullptr;
|
||||
ColorRect *_preview = nullptr;
|
||||
Control *_indicator = nullptr;
|
||||
Label *_current_label = nullptr;
|
||||
Label *_duration_label = nullptr;
|
||||
|
||||
HScrollBar *zoom_bar = nullptr;
|
||||
Button *zoom_in = nullptr;
|
||||
Button *zoom_reset = nullptr;
|
||||
Button *zoom_out = nullptr;
|
||||
|
||||
Button *_play_button = nullptr;
|
||||
Button *_stop_button = nullptr;
|
||||
|
||||
bool updating_settings = false;
|
||||
|
||||
float _current = 0;
|
||||
bool _dragging = false;
|
||||
bool _beat_len_dragging = false;
|
||||
bool _pausing = false;
|
||||
int _hovering_beat = -1;
|
||||
|
||||
HashMap<StringName, Variant> params;
|
||||
String importer;
|
||||
String path;
|
||||
|
||||
void _audio_changed();
|
||||
|
||||
static AudioStreamImportSettingsDialog *singleton;
|
||||
|
||||
void _settings_changed();
|
||||
|
||||
void _reimport();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _preview_changed(ObjectID p_which);
|
||||
void _preview_zoom_in();
|
||||
void _preview_zoom_out();
|
||||
void _preview_zoom_reset();
|
||||
void _preview_zoom_offset_changed(double);
|
||||
|
||||
void _play();
|
||||
void _stop();
|
||||
void _on_finished();
|
||||
void _draw_preview();
|
||||
void _draw_indicator();
|
||||
void _on_input_indicator(Ref<InputEvent> p_event);
|
||||
void _seek_to(real_t p_x);
|
||||
void _set_beat_len_to(real_t p_x);
|
||||
void _on_indicator_mouse_exited();
|
||||
int _get_beat_at_pos(real_t p_x);
|
||||
|
||||
public:
|
||||
void edit(const String &p_path, const String &p_importer, const Ref<AudioStream> &p_stream);
|
||||
|
||||
static AudioStreamImportSettingsDialog *get_singleton() { return singleton; }
|
||||
|
||||
AudioStreamImportSettingsDialog();
|
||||
};
|
||||
|
||||
#endif // AUDIO_STREAM_IMPORT_SETTINGS_H
|
||||
1550
engine/editor/import/dynamic_font_import_settings.cpp
Normal file
1550
engine/editor/import/dynamic_font_import_settings.cpp
Normal file
File diff suppressed because it is too large
Load diff
180
engine/editor/import/dynamic_font_import_settings.h
Normal file
180
engine/editor/import/dynamic_font_import_settings.h
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/**************************************************************************/
|
||||
/* dynamic_font_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 DYNAMIC_FONT_IMPORT_SETTINGS_H
|
||||
#define DYNAMIC_FONT_IMPORT_SETTINGS_H
|
||||
|
||||
#include "editor/import/resource_importer_dynamic_font.h"
|
||||
|
||||
#include "core/templates/rb_set.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
class DynamicFontImportSettingsDialog;
|
||||
|
||||
class DynamicFontImportSettingsData : public RefCounted {
|
||||
GDCLASS(DynamicFontImportSettingsData, RefCounted)
|
||||
friend class DynamicFontImportSettingsDialog;
|
||||
|
||||
HashMap<StringName, Variant> settings;
|
||||
HashMap<StringName, Variant> defaults;
|
||||
List<ResourceImporter::ImportOption> options;
|
||||
DynamicFontImportSettingsDialog *owner = nullptr;
|
||||
|
||||
HashSet<char32_t> selected_chars;
|
||||
HashSet<int32_t> selected_glyphs;
|
||||
|
||||
Ref<FontFile> fd;
|
||||
|
||||
public:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
Ref<FontFile> get_font() const;
|
||||
};
|
||||
|
||||
class EditorFileDialog;
|
||||
class EditorInspector;
|
||||
class EditorLocaleDialog;
|
||||
|
||||
class DynamicFontImportSettingsDialog : public ConfirmationDialog {
|
||||
GDCLASS(DynamicFontImportSettingsDialog, ConfirmationDialog)
|
||||
friend class DynamicFontImportSettingsData;
|
||||
|
||||
enum ItemButton {
|
||||
BUTTON_ADD_VAR,
|
||||
BUTTON_REMOVE_VAR,
|
||||
};
|
||||
|
||||
static DynamicFontImportSettingsDialog *singleton;
|
||||
|
||||
String base_path;
|
||||
|
||||
Ref<DynamicFontImportSettingsData> import_settings_data;
|
||||
List<ResourceImporter::ImportOption> options_variations;
|
||||
List<ResourceImporter::ImportOption> options_general;
|
||||
|
||||
// Root layout
|
||||
Label *label_warn = nullptr;
|
||||
TabContainer *main_pages = nullptr;
|
||||
|
||||
// Page 1 layout: Rendering Options
|
||||
Label *page1_description = nullptr;
|
||||
Label *font_name_label = nullptr;
|
||||
Label *font_preview_label = nullptr;
|
||||
EditorInspector *inspector_general = nullptr;
|
||||
|
||||
void _main_prop_changed(const String &p_edited_property);
|
||||
|
||||
// Page 2 layout: Preload Configurations
|
||||
Label *page2_description = nullptr;
|
||||
Label *label_vars = nullptr;
|
||||
Button *add_var = nullptr;
|
||||
Tree *vars_list = nullptr;
|
||||
TreeItem *vars_list_root = nullptr;
|
||||
EditorInspector *inspector_vars = nullptr;
|
||||
|
||||
void _variation_add();
|
||||
void _variation_selected();
|
||||
void _variation_remove(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
void _variation_changed(const String &p_edited_property);
|
||||
void _variations_validate();
|
||||
|
||||
TabContainer *preload_pages = nullptr;
|
||||
|
||||
Label *label_glyphs = nullptr;
|
||||
void _glyph_clear();
|
||||
void _glyph_update_lbl();
|
||||
|
||||
// Page 2.0 layout: Translations
|
||||
Label *page2_0_description = nullptr;
|
||||
Tree *locale_tree = nullptr;
|
||||
TreeItem *locale_root = nullptr;
|
||||
Button *btn_fill_locales = nullptr;
|
||||
|
||||
void _locale_edited();
|
||||
void _process_locales();
|
||||
|
||||
// Page 2.1 layout: Text to select glyphs
|
||||
Label *page2_1_description = nullptr;
|
||||
TextEdit *text_edit = nullptr;
|
||||
EditorInspector *inspector_text = nullptr;
|
||||
Button *btn_fill = nullptr;
|
||||
|
||||
List<ResourceImporter::ImportOption> options_text;
|
||||
Ref<DynamicFontImportSettingsData> text_settings_data;
|
||||
|
||||
void _change_text_opts();
|
||||
void _glyph_text_selected();
|
||||
|
||||
// Page 2.2 layout: Character map
|
||||
Label *page2_2_description = nullptr;
|
||||
Tree *glyph_table = nullptr;
|
||||
Tree *glyph_tree = nullptr;
|
||||
TreeItem *glyph_root = nullptr;
|
||||
|
||||
void _glyph_selected();
|
||||
void _range_edited();
|
||||
void _range_selected();
|
||||
void _edit_range(int32_t p_start, int32_t p_end);
|
||||
bool _char_update(int32_t p_char);
|
||||
void _range_update(int32_t p_start, int32_t p_end);
|
||||
|
||||
// Common
|
||||
|
||||
void _add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name);
|
||||
|
||||
Ref<FontFile> font_preview;
|
||||
Ref<FontFile> font_main;
|
||||
|
||||
void _re_import();
|
||||
|
||||
String _pad_zeros(const String &p_hex) const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void open_settings(const String &p_path);
|
||||
static DynamicFontImportSettingsDialog *get_singleton();
|
||||
|
||||
DynamicFontImportSettingsDialog();
|
||||
};
|
||||
|
||||
#endif // DYNAMIC_FONT_IMPORT_SETTINGS_H
|
||||
227
engine/editor/import/editor_import_plugin.cpp
Normal file
227
engine/editor/import/editor_import_plugin.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/**************************************************************************/
|
||||
/* editor_import_plugin.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 "editor_import_plugin.h"
|
||||
|
||||
#include "core/object/script_language.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
|
||||
EditorImportPlugin::EditorImportPlugin() {
|
||||
}
|
||||
|
||||
String EditorImportPlugin::get_importer_name() const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_importer_name, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Unimplemented _get_importer_name in add-on.");
|
||||
}
|
||||
|
||||
String EditorImportPlugin::get_visible_name() const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_visible_name, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Unimplemented _get_visible_name in add-on.");
|
||||
}
|
||||
|
||||
void EditorImportPlugin::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
Vector<String> extensions;
|
||||
|
||||
if (GDVIRTUAL_CALL(_get_recognized_extensions, extensions)) {
|
||||
for (int i = 0; i < extensions.size(); i++) {
|
||||
p_extensions->push_back(extensions[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_MSG("Unimplemented _get_recognized_extensions in add-on.");
|
||||
}
|
||||
|
||||
String EditorImportPlugin::get_preset_name(int p_idx) const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_preset_name, p_idx, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Unimplemented _get_preset_name in add-on.");
|
||||
}
|
||||
|
||||
int EditorImportPlugin::get_preset_count() const {
|
||||
int ret;
|
||||
if (GDVIRTUAL_CALL(_get_preset_count, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(-1, "Unimplemented _get_preset_count in add-on.");
|
||||
}
|
||||
|
||||
String EditorImportPlugin::get_save_extension() const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_save_extension, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Unimplemented _get_save_extension in add-on.");
|
||||
}
|
||||
|
||||
String EditorImportPlugin::get_resource_type() const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_resource_type, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Unimplemented _get_resource_type in add-on.");
|
||||
}
|
||||
|
||||
float EditorImportPlugin::get_priority() const {
|
||||
float ret;
|
||||
if (GDVIRTUAL_CALL(_get_priority, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(-1, "Unimplemented _get_priority in add-on.");
|
||||
}
|
||||
|
||||
int EditorImportPlugin::get_import_order() const {
|
||||
int ret;
|
||||
if (GDVIRTUAL_CALL(_get_import_order, ret)) {
|
||||
return ret;
|
||||
}
|
||||
ERR_FAIL_V_MSG(-1, "Unimplemented _get_import_order in add-on.");
|
||||
}
|
||||
|
||||
void EditorImportPlugin::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options, int p_preset) const {
|
||||
Array needed;
|
||||
needed.push_back("name");
|
||||
needed.push_back("default_value");
|
||||
TypedArray<Dictionary> options;
|
||||
if (GDVIRTUAL_CALL(_get_import_options, p_path, p_preset, options)) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
Dictionary d = options[i];
|
||||
ERR_FAIL_COND(!d.has_all(needed));
|
||||
String name = d["name"];
|
||||
Variant default_value = d["default_value"];
|
||||
|
||||
PropertyHint hint = PROPERTY_HINT_NONE;
|
||||
if (d.has("property_hint")) {
|
||||
hint = (PropertyHint)d["property_hint"].operator int64_t();
|
||||
}
|
||||
|
||||
String hint_string;
|
||||
if (d.has("hint_string")) {
|
||||
hint_string = d["hint_string"];
|
||||
}
|
||||
|
||||
uint32_t usage = PROPERTY_USAGE_DEFAULT;
|
||||
if (d.has("usage")) {
|
||||
usage = d["usage"];
|
||||
}
|
||||
|
||||
ImportOption option(PropertyInfo(default_value.get_type(), name, hint, hint_string, usage), default_value);
|
||||
r_options->push_back(option);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_MSG("Unimplemented _get_import_options in add-on.");
|
||||
}
|
||||
|
||||
bool EditorImportPlugin::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
Dictionary d;
|
||||
HashMap<StringName, Variant>::ConstIterator E = p_options.begin();
|
||||
while (E) {
|
||||
d[E->key] = E->value;
|
||||
++E;
|
||||
}
|
||||
bool visible = false;
|
||||
if (GDVIRTUAL_CALL(_get_option_visibility, p_path, p_option, d, visible)) {
|
||||
return visible;
|
||||
}
|
||||
|
||||
ERR_FAIL_V_MSG(false, "Unimplemented _get_option_visibility in add-on.");
|
||||
}
|
||||
|
||||
Error EditorImportPlugin::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) {
|
||||
Dictionary options;
|
||||
TypedArray<String> platform_variants, gen_files;
|
||||
|
||||
HashMap<StringName, Variant>::ConstIterator E = p_options.begin();
|
||||
while (E) {
|
||||
options[E->key] = E->value;
|
||||
++E;
|
||||
}
|
||||
|
||||
Error err = OK;
|
||||
if (GDVIRTUAL_CALL(_import, p_source_file, p_save_path, options, platform_variants, gen_files, err)) {
|
||||
for (int i = 0; i < platform_variants.size(); i++) {
|
||||
r_platform_variants->push_back(platform_variants[i]);
|
||||
}
|
||||
for (int i = 0; i < gen_files.size(); i++) {
|
||||
r_gen_files->push_back(gen_files[i]);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
ERR_FAIL_V_MSG(ERR_METHOD_NOT_FOUND, "Unimplemented _import in add-on.");
|
||||
}
|
||||
|
||||
bool EditorImportPlugin::can_import_threaded() const {
|
||||
bool ret = false;
|
||||
if (GDVIRTUAL_CALL(_can_import_threaded, ret)) {
|
||||
return ret;
|
||||
} else {
|
||||
return ResourceImporter::can_import_threaded();
|
||||
}
|
||||
}
|
||||
|
||||
Error EditorImportPlugin::_append_import_external_resource(const String &p_file, const Dictionary &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
|
||||
HashMap<StringName, Variant> options;
|
||||
List<Variant> keys;
|
||||
p_custom_options.get_key_list(&keys);
|
||||
for (const Variant &K : keys) {
|
||||
options.insert(K, p_custom_options[K]);
|
||||
}
|
||||
return append_import_external_resource(p_file, options, p_custom_importer, p_generator_parameters);
|
||||
}
|
||||
|
||||
Error EditorImportPlugin::append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
|
||||
return EditorFileSystem::get_singleton()->reimport_append(p_file, p_custom_options, p_custom_importer, p_generator_parameters);
|
||||
}
|
||||
|
||||
void EditorImportPlugin::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_get_importer_name)
|
||||
GDVIRTUAL_BIND(_get_visible_name)
|
||||
GDVIRTUAL_BIND(_get_preset_count)
|
||||
GDVIRTUAL_BIND(_get_preset_name, "preset_index")
|
||||
GDVIRTUAL_BIND(_get_recognized_extensions)
|
||||
GDVIRTUAL_BIND(_get_import_options, "path", "preset_index")
|
||||
GDVIRTUAL_BIND(_get_save_extension)
|
||||
GDVIRTUAL_BIND(_get_resource_type)
|
||||
GDVIRTUAL_BIND(_get_priority)
|
||||
GDVIRTUAL_BIND(_get_import_order)
|
||||
GDVIRTUAL_BIND(_get_option_visibility, "path", "option_name", "options")
|
||||
GDVIRTUAL_BIND(_import, "source_file", "save_path", "options", "platform_variants", "gen_files");
|
||||
GDVIRTUAL_BIND(_can_import_threaded);
|
||||
ClassDB::bind_method(D_METHOD("append_import_external_resource", "path", "custom_options", "custom_importer", "generator_parameters"), &EditorImportPlugin::_append_import_external_resource, DEFVAL(Dictionary()), DEFVAL(String()), DEFVAL(Variant()));
|
||||
}
|
||||
77
engine/editor/import/editor_import_plugin.h
Normal file
77
engine/editor/import/editor_import_plugin.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************/
|
||||
/* editor_import_plugin.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_PLUGIN_H
|
||||
#define EDITOR_IMPORT_PLUGIN_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
class EditorImportPlugin : public ResourceImporter {
|
||||
GDCLASS(EditorImportPlugin, ResourceImporter);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL0RC(String, _get_importer_name)
|
||||
GDVIRTUAL0RC(String, _get_visible_name)
|
||||
GDVIRTUAL0RC(int, _get_preset_count)
|
||||
GDVIRTUAL1RC(String, _get_preset_name, int)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
|
||||
GDVIRTUAL2RC(TypedArray<Dictionary>, _get_import_options, String, int)
|
||||
GDVIRTUAL0RC(String, _get_save_extension)
|
||||
GDVIRTUAL0RC(String, _get_resource_type)
|
||||
GDVIRTUAL0RC(float, _get_priority)
|
||||
GDVIRTUAL0RC(int, _get_import_order)
|
||||
GDVIRTUAL3RC(bool, _get_option_visibility, String, StringName, Dictionary)
|
||||
GDVIRTUAL5RC(Error, _import, String, String, Dictionary, TypedArray<String>, TypedArray<String>)
|
||||
GDVIRTUAL0RC(bool, _can_import_threaded)
|
||||
|
||||
Error _append_import_external_resource(const String &p_file, const Dictionary &p_custom_options = Dictionary(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant());
|
||||
|
||||
public:
|
||||
EditorImportPlugin();
|
||||
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_preset_name(int p_idx) const override;
|
||||
virtual int get_preset_count() const override;
|
||||
virtual String get_save_extension() const override;
|
||||
virtual String get_resource_type() const override;
|
||||
virtual float get_priority() const override;
|
||||
virtual int get_import_order() const override;
|
||||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) 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, Variant *r_metadata = nullptr) override;
|
||||
virtual bool can_import_threaded() const override;
|
||||
Error append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant());
|
||||
};
|
||||
|
||||
#endif // EDITOR_IMPORT_PLUGIN_H
|
||||
113
engine/editor/import/resource_importer_bitmask.cpp
Normal file
113
engine/editor/import/resource_importer_bitmask.cpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_bitmask.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_bitmask.h"
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
||||
String ResourceImporterBitMap::get_importer_name() const {
|
||||
return "bitmap";
|
||||
}
|
||||
|
||||
String ResourceImporterBitMap::get_visible_name() const {
|
||||
return "BitMap";
|
||||
}
|
||||
|
||||
void ResourceImporterBitMap::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
|
||||
String ResourceImporterBitMap::get_save_extension() const {
|
||||
return "res";
|
||||
}
|
||||
|
||||
String ResourceImporterBitMap::get_resource_type() const {
|
||||
return "BitMap";
|
||||
}
|
||||
|
||||
bool ResourceImporterBitMap::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterBitMap::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterBitMap::get_preset_name(int p_idx) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceImporterBitMap::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "create_from", PROPERTY_HINT_ENUM, "Black & White,Alpha"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5));
|
||||
}
|
||||
|
||||
Error ResourceImporterBitMap::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) {
|
||||
int create_from = p_options["create_from"];
|
||||
float threshold = p_options["threshold"];
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
Error err = ImageLoader::load_image(p_source_file, image);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int w = image->get_width();
|
||||
int h = image->get_height();
|
||||
|
||||
Ref<BitMap> bitmap;
|
||||
bitmap.instantiate();
|
||||
bitmap->create(Size2(w, h));
|
||||
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
bool bit;
|
||||
Color c = image->get_pixel(j, i);
|
||||
if (create_from == 0) { //b&W
|
||||
bit = c.get_v() > threshold;
|
||||
} else {
|
||||
bit = c.a > threshold;
|
||||
}
|
||||
|
||||
bitmap->set_bit(j, i, bit);
|
||||
}
|
||||
}
|
||||
|
||||
return ResourceSaver::save(bitmap, p_save_path + ".res");
|
||||
}
|
||||
|
||||
ResourceImporterBitMap::ResourceImporterBitMap() {
|
||||
}
|
||||
|
||||
ResourceImporterBitMap::~ResourceImporterBitMap() {
|
||||
}
|
||||
57
engine/editor/import/resource_importer_bitmask.h
Normal file
57
engine/editor/import/resource_importer_bitmask.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_bitmask.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_BITMASK_H
|
||||
#define RESOURCE_IMPORTER_BITMASK_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterBitMap : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterBitMap, 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_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;
|
||||
|
||||
ResourceImporterBitMap();
|
||||
~ResourceImporterBitMap();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_BITMASK_H
|
||||
114
engine/editor/import/resource_importer_bmfont.cpp
Normal file
114
engine/editor/import/resource_importer_bmfont.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_bmfont.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_bmfont.h"
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
|
||||
String ResourceImporterBMFont::get_importer_name() const {
|
||||
return "font_data_bmfont";
|
||||
}
|
||||
|
||||
String ResourceImporterBMFont::get_visible_name() const {
|
||||
return "Font Data (AngelCode BMFont)";
|
||||
}
|
||||
|
||||
void ResourceImporterBMFont::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
if (p_extensions) {
|
||||
p_extensions->push_back("font");
|
||||
p_extensions->push_back("fnt");
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceImporterBMFont::get_save_extension() const {
|
||||
return "fontdata";
|
||||
}
|
||||
|
||||
String ResourceImporterBMFont::get_resource_type() const {
|
||||
return "FontFile";
|
||||
}
|
||||
|
||||
bool ResourceImporterBMFont::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceImporterBMFont::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
|
||||
}
|
||||
|
||||
Error ResourceImporterBMFont::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) {
|
||||
print_verbose("Importing BMFont font from: " + p_source_file);
|
||||
|
||||
Array fallbacks = p_options["fallbacks"];
|
||||
TextServer::FixedSizeScaleMode smode = (TextServer::FixedSizeScaleMode)p_options["scaling_mode"].operator int();
|
||||
|
||||
Ref<FontFile> font;
|
||||
font.instantiate();
|
||||
|
||||
List<String> image_files;
|
||||
Error err = font->_load_bitmap_font(p_source_file, &image_files);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load font to file \"" + p_source_file + "\".");
|
||||
|
||||
// Update import settings for the image files used by font.
|
||||
for (List<String>::Element *E = image_files.front(); E; E = E->next()) {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
|
||||
err = config->load(E->get() + ".import");
|
||||
if (err == OK) {
|
||||
config->clear();
|
||||
config->set_value("remap", "importer", "skip");
|
||||
|
||||
config->save(E->get() + ".import");
|
||||
}
|
||||
}
|
||||
|
||||
font->set_allow_system_fallback(false);
|
||||
font->set_fallbacks(fallbacks);
|
||||
font->set_fixed_size_scale_mode(smode);
|
||||
|
||||
int flg = 0;
|
||||
if ((bool)p_options["compress"]) {
|
||||
flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
|
||||
}
|
||||
|
||||
print_verbose("Saving to: " + p_save_path + ".fontdata");
|
||||
err = ResourceSaver::save(font, p_save_path + ".fontdata", flg);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
|
||||
print_verbose("Done saving to: " + p_save_path + ".fontdata");
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterBMFont::ResourceImporterBMFont() {
|
||||
}
|
||||
56
engine/editor/import/resource_importer_bmfont.h
Normal file
56
engine/editor/import/resource_importer_bmfont.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_bmfont.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_BMFONT_H
|
||||
#define RESOURCE_IMPORTER_BMFONT_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
class ResourceImporterBMFont : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterBMFont, 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 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;
|
||||
|
||||
ResourceImporterBMFont();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_BMFONT_H
|
||||
156
engine/editor/import/resource_importer_csv_translation.cpp
Normal file
156
engine/editor/import/resource_importer_csv_translation.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_csv_translation.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_csv_translation.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/string/optimized_translation.h"
|
||||
#include "core/string/translation.h"
|
||||
|
||||
String ResourceImporterCSVTranslation::get_importer_name() const {
|
||||
return "csv_translation";
|
||||
}
|
||||
|
||||
String ResourceImporterCSVTranslation::get_visible_name() const {
|
||||
return "CSV Translation";
|
||||
}
|
||||
|
||||
void ResourceImporterCSVTranslation::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
p_extensions->push_back("csv");
|
||||
}
|
||||
|
||||
String ResourceImporterCSVTranslation::get_save_extension() const {
|
||||
return ""; //does not save a single resource
|
||||
}
|
||||
|
||||
String ResourceImporterCSVTranslation::get_resource_type() const {
|
||||
return "Translation";
|
||||
}
|
||||
|
||||
bool ResourceImporterCSVTranslation::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterCSVTranslation::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterCSVTranslation::get_preset_name(int p_idx) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
void ResourceImporterCSVTranslation::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "delimiter", PROPERTY_HINT_ENUM, "Comma,Semicolon,Tab"), 0));
|
||||
}
|
||||
|
||||
Error ResourceImporterCSVTranslation::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) {
|
||||
bool compress = p_options["compress"];
|
||||
|
||||
String delimiter;
|
||||
switch ((int)p_options["delimiter"]) {
|
||||
case 0:
|
||||
delimiter = ",";
|
||||
break;
|
||||
case 1:
|
||||
delimiter = ";";
|
||||
break;
|
||||
case 2:
|
||||
delimiter = "\t";
|
||||
break;
|
||||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, "Cannot open file from path '" + p_source_file + "'.");
|
||||
|
||||
Vector<String> line = f->get_csv_line(delimiter);
|
||||
ERR_FAIL_COND_V(line.size() <= 1, ERR_PARSE_ERROR);
|
||||
|
||||
Vector<String> locales;
|
||||
Vector<Ref<Translation>> translations;
|
||||
HashSet<int> skipped_locales;
|
||||
|
||||
for (int i = 1; i < line.size(); i++) {
|
||||
String locale = TranslationServer::get_singleton()->standardize_locale(line[i]);
|
||||
|
||||
if (line[i].left(1) == "_") {
|
||||
skipped_locales.insert(i);
|
||||
continue;
|
||||
} else if (locale.is_empty()) {
|
||||
skipped_locales.insert(i);
|
||||
ERR_CONTINUE_MSG(true, vformat("Error importing CSV translation: Invalid locale format '%s', should be 'language_Script_COUNTRY_VARIANT@extra'. This column will be ignored.", line[i]));
|
||||
}
|
||||
|
||||
locales.push_back(locale);
|
||||
Ref<Translation> translation;
|
||||
translation.instantiate();
|
||||
translation->set_locale(locale);
|
||||
translations.push_back(translation);
|
||||
}
|
||||
|
||||
do {
|
||||
line = f->get_csv_line(delimiter);
|
||||
String key = line[0];
|
||||
if (!key.is_empty()) {
|
||||
ERR_CONTINUE_MSG(line.size() != locales.size() + (int)skipped_locales.size() + 1, vformat("Error importing CSV translation: expected %d locale(s), but the '%s' key has %d locale(s).", locales.size(), key, line.size() - 1));
|
||||
|
||||
int write_index = 0; // Keep track of translations written in case some locales are skipped.
|
||||
for (int i = 1; i < line.size(); i++) {
|
||||
if (skipped_locales.has(i)) {
|
||||
continue;
|
||||
}
|
||||
translations.write[write_index++]->add_message(key, line[i].c_unescape());
|
||||
}
|
||||
}
|
||||
} while (!f->eof_reached());
|
||||
|
||||
for (int i = 0; i < translations.size(); i++) {
|
||||
Ref<Translation> xlt = translations[i];
|
||||
|
||||
if (compress) {
|
||||
Ref<OptimizedTranslation> cxl = memnew(OptimizedTranslation);
|
||||
cxl->generate(xlt);
|
||||
xlt = cxl;
|
||||
}
|
||||
|
||||
String save_path = p_source_file.get_basename() + "." + translations[i]->get_locale() + ".translation";
|
||||
|
||||
ResourceSaver::save(xlt, save_path);
|
||||
if (r_gen_files) {
|
||||
r_gen_files->push_back(save_path);
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterCSVTranslation::ResourceImporterCSVTranslation() {
|
||||
}
|
||||
57
engine/editor/import/resource_importer_csv_translation.h
Normal file
57
engine/editor/import/resource_importer_csv_translation.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_csv_translation.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_CSV_TRANSLATION_H
|
||||
#define RESOURCE_IMPORTER_CSV_TRANSLATION_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterCSVTranslation : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterCSVTranslation, 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_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;
|
||||
|
||||
ResourceImporterCSVTranslation();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_CSV_TRANSLATION_H
|
||||
238
engine/editor/import/resource_importer_dynamic_font.cpp
Normal file
238
engine/editor/import/resource_importer_dynamic_font.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_dynamic_font.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_dynamic_font.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "editor/import/dynamic_font_import_settings.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype.
|
||||
|
||||
String ResourceImporterDynamicFont::get_importer_name() const {
|
||||
return "font_data_dynamic";
|
||||
}
|
||||
|
||||
String ResourceImporterDynamicFont::get_visible_name() const {
|
||||
return "Font Data (Dynamic Font)";
|
||||
}
|
||||
|
||||
void ResourceImporterDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
if (p_extensions) {
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
p_extensions->push_back("ttf");
|
||||
p_extensions->push_back("ttc");
|
||||
p_extensions->push_back("otf");
|
||||
p_extensions->push_back("otc");
|
||||
p_extensions->push_back("woff");
|
||||
p_extensions->push_back("woff2");
|
||||
p_extensions->push_back("pfb");
|
||||
p_extensions->push_back("pfm");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceImporterDynamicFont::get_save_extension() const {
|
||||
return "fontdata";
|
||||
}
|
||||
|
||||
String ResourceImporterDynamicFont::get_resource_type() const {
|
||||
return "FontFile";
|
||||
}
|
||||
|
||||
bool ResourceImporterDynamicFont::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "msdf_pixel_range" && !bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
if (p_option == "msdf_size" && !bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
if (p_option == "antialiasing" && bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
if (p_option == "oversampling" && bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
if (p_option == "subpixel_positioning" && bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterDynamicFont::get_preset_count() const {
|
||||
return PRESET_MAX;
|
||||
}
|
||||
|
||||
String ResourceImporterDynamicFont::get_preset_name(int p_idx) const {
|
||||
switch (p_idx) {
|
||||
case PRESET_DYNAMIC:
|
||||
return TTR("Dynamically rendered TrueType/OpenType font");
|
||||
case PRESET_MSDF:
|
||||
return TTR("Prerendered multichannel(+true) signed distance field");
|
||||
default:
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
bool msdf = p_preset == PRESET_MSDF;
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD Subpixel"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_mipmaps"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "disable_embedded_bitmaps"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), (msdf) ? true : false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Compress", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
|
||||
|
||||
// Hide from the main UI, only for advanced import dialog.
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "preload", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), Array()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "language_support", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), Dictionary()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "script_support", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), Dictionary()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "opentype_features", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), Dictionary()));
|
||||
}
|
||||
|
||||
bool ResourceImporterDynamicFont::has_advanced_options() const {
|
||||
return true;
|
||||
}
|
||||
void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) {
|
||||
DynamicFontImportSettingsDialog::get_singleton()->open_settings(p_path);
|
||||
}
|
||||
|
||||
Error ResourceImporterDynamicFont::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) {
|
||||
print_verbose("Importing dynamic font from: " + p_source_file);
|
||||
|
||||
int antialiasing = p_options["antialiasing"];
|
||||
bool generate_mipmaps = p_options["generate_mipmaps"];
|
||||
bool disable_embedded_bitmaps = p_options["disable_embedded_bitmaps"];
|
||||
bool msdf = p_options["multichannel_signed_distance_field"];
|
||||
int px_range = p_options["msdf_pixel_range"];
|
||||
int px_size = p_options["msdf_size"];
|
||||
Dictionary ot_ov = p_options["opentype_features"];
|
||||
|
||||
bool autohinter = p_options["force_autohinter"];
|
||||
bool allow_system_fallback = p_options["allow_system_fallback"];
|
||||
int hinting = p_options["hinting"];
|
||||
int subpixel_positioning = p_options["subpixel_positioning"];
|
||||
real_t oversampling = p_options["oversampling"];
|
||||
Array fallbacks = p_options["fallbacks"];
|
||||
|
||||
// Load base font data.
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_file);
|
||||
|
||||
// Create font.
|
||||
Ref<FontFile> font;
|
||||
font.instantiate();
|
||||
font->set_data(data);
|
||||
font->set_antialiasing((TextServer::FontAntialiasing)antialiasing);
|
||||
font->set_disable_embedded_bitmaps(disable_embedded_bitmaps);
|
||||
font->set_generate_mipmaps(generate_mipmaps);
|
||||
font->set_multichannel_signed_distance_field(msdf);
|
||||
font->set_msdf_pixel_range(px_range);
|
||||
font->set_msdf_size(px_size);
|
||||
font->set_opentype_feature_overrides(ot_ov);
|
||||
font->set_fixed_size(0);
|
||||
font->set_force_autohinter(autohinter);
|
||||
font->set_allow_system_fallback(allow_system_fallback);
|
||||
font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
|
||||
font->set_hinting((TextServer::Hinting)hinting);
|
||||
font->set_oversampling(oversampling);
|
||||
font->set_fallbacks(fallbacks);
|
||||
|
||||
Dictionary langs = p_options["language_support"];
|
||||
for (int i = 0; i < langs.size(); i++) {
|
||||
String key = langs.get_key_at_index(i);
|
||||
bool enabled = langs.get_value_at_index(i);
|
||||
font->set_language_support_override(key, enabled);
|
||||
}
|
||||
|
||||
Dictionary scripts = p_options["script_support"];
|
||||
for (int i = 0; i < scripts.size(); i++) {
|
||||
String key = scripts.get_key_at_index(i);
|
||||
bool enabled = scripts.get_value_at_index(i);
|
||||
font->set_script_support_override(key, enabled);
|
||||
}
|
||||
|
||||
Array preload_configurations = p_options["preload"];
|
||||
|
||||
for (int i = 0; i < preload_configurations.size(); i++) {
|
||||
Dictionary preload_config = preload_configurations[i];
|
||||
|
||||
Dictionary variation = preload_config.has("variation_opentype") ? preload_config["variation_opentype"].operator Dictionary() : Dictionary();
|
||||
double embolden = preload_config.has("variation_embolden") ? preload_config["variation_embolden"].operator double() : 0;
|
||||
int face_index = preload_config.has("variation_face_index") ? preload_config["variation_face_index"].operator int() : 0;
|
||||
Transform2D transform = preload_config.has("variation_transform") ? preload_config["variation_transform"].operator Transform2D() : Transform2D();
|
||||
Vector2i size = preload_config.has("size") ? preload_config["size"].operator Vector2i() : Vector2i(16, 0);
|
||||
String name = preload_config.has("name") ? preload_config["name"].operator String() : vformat("Configuration %d", i);
|
||||
|
||||
RID conf_rid = font->find_variation(variation, face_index, embolden, transform);
|
||||
|
||||
Array chars = preload_config["chars"];
|
||||
for (int j = 0; j < chars.size(); j++) {
|
||||
char32_t c = chars[j].operator int();
|
||||
TS->font_render_range(conf_rid, size, c, c);
|
||||
}
|
||||
|
||||
Array glyphs = preload_config["glyphs"];
|
||||
for (int j = 0; j < glyphs.size(); j++) {
|
||||
int32_t c = glyphs[j];
|
||||
TS->font_render_glyph(conf_rid, size, c);
|
||||
}
|
||||
}
|
||||
|
||||
int flg = 0;
|
||||
if ((bool)p_options["compress"]) {
|
||||
flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
|
||||
}
|
||||
|
||||
print_verbose("Saving to: " + p_save_path + ".fontdata");
|
||||
Error err = ResourceSaver::save(font, p_save_path + ".fontdata", flg);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
|
||||
print_verbose("Done saving to: " + p_save_path + ".fontdata");
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterDynamicFont::ResourceImporterDynamicFont() {
|
||||
}
|
||||
66
engine/editor/import/resource_importer_dynamic_font.h
Normal file
66
engine/editor/import/resource_importer_dynamic_font.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_dynamic_font.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_DYNAMIC_FONT_H
|
||||
#define RESOURCE_IMPORTER_DYNAMIC_FONT_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterDynamicFont : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterDynamicFont, ResourceImporter);
|
||||
|
||||
enum Presets {
|
||||
PRESET_DYNAMIC,
|
||||
PRESET_MSDF,
|
||||
PRESET_MAX
|
||||
};
|
||||
|
||||
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_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;
|
||||
|
||||
bool has_advanced_options() const override;
|
||||
void show_advanced_options(const String &p_path) 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;
|
||||
|
||||
ResourceImporterDynamicFont();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_DYNAMIC_FONT_H
|
||||
99
engine/editor/import/resource_importer_image.cpp
Normal file
99
engine/editor/import/resource_importer_image.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_image.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_image.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
String ResourceImporterImage::get_importer_name() const {
|
||||
return "image";
|
||||
}
|
||||
|
||||
String ResourceImporterImage::get_visible_name() const {
|
||||
return "Image";
|
||||
}
|
||||
|
||||
void ResourceImporterImage::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
|
||||
String ResourceImporterImage::get_save_extension() const {
|
||||
return "image";
|
||||
}
|
||||
|
||||
String ResourceImporterImage::get_resource_type() const {
|
||||
return "Image";
|
||||
}
|
||||
|
||||
bool ResourceImporterImage::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterImage::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterImage::get_preset_name(int p_idx) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceImporterImage::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
}
|
||||
|
||||
Error ResourceImporterImage::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) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'.");
|
||||
uint64_t len = f->get_length();
|
||||
|
||||
Vector<uint8_t> data;
|
||||
data.resize(len);
|
||||
|
||||
f->get_buffer(data.ptrw(), len);
|
||||
|
||||
f = FileAccess::open(p_save_path + ".image", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, "Cannot create file in path '" + p_save_path + ".image'.");
|
||||
|
||||
//save the header GDIM
|
||||
const uint8_t header[4] = { 'G', 'D', 'I', 'M' };
|
||||
f->store_buffer(header, 4);
|
||||
//SAVE the extension (so it can be recognized by the loader later
|
||||
f->store_pascal_string(p_source_file.get_extension().to_lower());
|
||||
//SAVE the actual image
|
||||
f->store_buffer(data.ptr(), len);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterImage::ResourceImporterImage() {
|
||||
}
|
||||
58
engine/editor/import/resource_importer_image.h
Normal file
58
engine/editor/import/resource_importer_image.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_image.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_IMAGE_H
|
||||
#define RESOURCE_IMPORTER_IMAGE_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterImage : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterImage, 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_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;
|
||||
|
||||
ResourceImporterImage();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_IMAGE_H
|
||||
346
engine/editor/import/resource_importer_imagefont.cpp
Normal file
346
engine/editor/import/resource_importer_imagefont.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_imagefont.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_imagefont.h"
|
||||
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
|
||||
String ResourceImporterImageFont::get_importer_name() const {
|
||||
return "font_data_image";
|
||||
}
|
||||
|
||||
String ResourceImporterImageFont::get_visible_name() const {
|
||||
return "Font Data (Image Font)";
|
||||
}
|
||||
|
||||
void ResourceImporterImageFont::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
if (p_extensions) {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceImporterImageFont::get_save_extension() const {
|
||||
return "fontdata";
|
||||
}
|
||||
|
||||
String ResourceImporterImageFont::get_resource_type() const {
|
||||
return "FontFile";
|
||||
}
|
||||
|
||||
bool ResourceImporterImageFont::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceImporterImageFont::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "character_ranges"), Vector<String>()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "kerning_pairs"), Vector<String>()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rows", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::RECT2I, "image_margin"), Rect2i()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::RECT2I, "character_margin"), Rect2i()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "ascent"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "descent"), 0));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
|
||||
}
|
||||
|
||||
Error ResourceImporterImageFont::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) {
|
||||
print_verbose("Importing image font from: " + p_source_file);
|
||||
|
||||
int columns = p_options["columns"];
|
||||
int rows = p_options["rows"];
|
||||
int ascent = p_options["ascent"];
|
||||
int descent = p_options["descent"];
|
||||
Vector<String> ranges = p_options["character_ranges"];
|
||||
Vector<String> kern = p_options["kerning_pairs"];
|
||||
Array fallbacks = p_options["fallbacks"];
|
||||
Rect2i img_margin = p_options["image_margin"];
|
||||
Rect2i char_margin = p_options["character_margin"];
|
||||
TextServer::FixedSizeScaleMode smode = (TextServer::FixedSizeScaleMode)p_options["scaling_mode"].operator int();
|
||||
|
||||
Ref<Image> img;
|
||||
img.instantiate();
|
||||
Error err = ImageLoader::load_image(p_source_file, img);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat("Can't load font texture: \"%s\".", p_source_file));
|
||||
|
||||
ERR_FAIL_COND_V_MSG(columns <= 0, ERR_FILE_CANT_READ, vformat("Columns (%d) must be positive.", columns));
|
||||
ERR_FAIL_COND_V_MSG(rows <= 0, ERR_FILE_CANT_READ, vformat("Rows (%d) must be positive.", rows));
|
||||
int count = columns * rows;
|
||||
int chr_cell_width = (img->get_width() - img_margin.position.x - img_margin.size.x) / columns;
|
||||
int chr_cell_height = (img->get_height() - img_margin.position.y - img_margin.size.y) / rows;
|
||||
ERR_FAIL_COND_V_MSG(chr_cell_width <= 0 || chr_cell_height <= 0, ERR_FILE_CANT_READ, "Image margin too big.");
|
||||
|
||||
int chr_width = chr_cell_width - char_margin.position.x - char_margin.size.x;
|
||||
int chr_height = chr_cell_height - char_margin.position.y - char_margin.size.y;
|
||||
ERR_FAIL_COND_V_MSG(chr_width <= 0 || chr_height <= 0, ERR_FILE_CANT_READ, "Character margin too big.");
|
||||
|
||||
Ref<FontFile> font;
|
||||
font.instantiate();
|
||||
font->set_antialiasing(TextServer::FONT_ANTIALIASING_NONE);
|
||||
font->set_generate_mipmaps(false);
|
||||
font->set_multichannel_signed_distance_field(false);
|
||||
font->set_fixed_size(chr_height);
|
||||
font->set_subpixel_positioning(TextServer::SUBPIXEL_POSITIONING_DISABLED);
|
||||
font->set_force_autohinter(false);
|
||||
font->set_allow_system_fallback(false);
|
||||
font->set_hinting(TextServer::HINTING_NONE);
|
||||
font->set_oversampling(1.0f);
|
||||
font->set_fallbacks(fallbacks);
|
||||
font->set_texture_image(0, Vector2i(chr_height, 0), 0, img);
|
||||
font->set_fixed_size_scale_mode(smode);
|
||||
|
||||
int32_t pos = 0;
|
||||
for (const String &range : ranges) {
|
||||
int32_t start = -1;
|
||||
int32_t end = -1;
|
||||
int chr_adv = 0;
|
||||
Vector2i chr_off;
|
||||
|
||||
{
|
||||
enum RangeParseStep {
|
||||
STEP_START_BEGIN,
|
||||
STEP_START_READ_HEX,
|
||||
STEP_START_READ_DEC,
|
||||
STEP_END_BEGIN,
|
||||
STEP_END_READ_HEX,
|
||||
STEP_END_READ_DEC,
|
||||
STEP_ADVANCE_BEGIN,
|
||||
STEP_OFF_X_BEGIN,
|
||||
STEP_OFF_Y_BEGIN,
|
||||
STEP_FINISHED,
|
||||
};
|
||||
RangeParseStep step = STEP_START_BEGIN;
|
||||
String token;
|
||||
for (int c = 0; c < range.length(); c++) {
|
||||
switch (step) {
|
||||
case STEP_START_BEGIN:
|
||||
case STEP_END_BEGIN: {
|
||||
// Read range start/end first symbol.
|
||||
if (range[c] == 'U' || range[c] == 'u') {
|
||||
if ((c <= range.length() - 2) && range[c + 1] == '+') {
|
||||
token = String();
|
||||
if (step == STEP_START_BEGIN) {
|
||||
step = STEP_START_READ_HEX;
|
||||
} else {
|
||||
step = STEP_END_READ_HEX;
|
||||
}
|
||||
c++; // Skip "+".
|
||||
continue;
|
||||
}
|
||||
} else if (range[c] == '0' && (c <= range.length() - 2) && range[c + 1] == 'x') {
|
||||
// Read hexadecimal value, start.
|
||||
token = String();
|
||||
if (step == STEP_START_BEGIN) {
|
||||
step = STEP_START_READ_HEX;
|
||||
} else {
|
||||
step = STEP_END_READ_HEX;
|
||||
}
|
||||
c++; // Skip "x".
|
||||
continue;
|
||||
} else if (range[c] == '\'' || range[c] == '\"') {
|
||||
if ((c <= range.length() - 3) && (range[c + 2] == '\'' || range[c + 2] == '\"')) {
|
||||
token = String();
|
||||
if (step == STEP_START_BEGIN) {
|
||||
start = range.unicode_at(c + 1);
|
||||
step = STEP_END_BEGIN;
|
||||
} else {
|
||||
end = range.unicode_at(c + 1);
|
||||
step = STEP_ADVANCE_BEGIN;
|
||||
}
|
||||
c = c + 2; // Skip the rest or token.
|
||||
continue;
|
||||
}
|
||||
} else if (is_digit(range[c])) {
|
||||
// Read decimal value, start.
|
||||
token = String();
|
||||
token += range[c];
|
||||
if (step == STEP_START_BEGIN) {
|
||||
step = STEP_START_READ_DEC;
|
||||
} else {
|
||||
step = STEP_END_READ_DEC;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case STEP_ADVANCE_BEGIN:
|
||||
case STEP_OFF_X_BEGIN:
|
||||
case STEP_OFF_Y_BEGIN: {
|
||||
// Read advance and offset.
|
||||
if (range[c] == ' ') {
|
||||
int next = range.find(" ", c + 1);
|
||||
if (next < c) {
|
||||
next = range.length();
|
||||
}
|
||||
if (step == STEP_OFF_X_BEGIN) {
|
||||
chr_off.x = range.substr(c + 1, next - (c + 1)).to_int();
|
||||
step = STEP_OFF_Y_BEGIN;
|
||||
} else if (step == STEP_OFF_Y_BEGIN) {
|
||||
chr_off.y = range.substr(c + 1, next - (c + 1)).to_int();
|
||||
step = STEP_FINISHED;
|
||||
} else {
|
||||
chr_adv = range.substr(c + 1, next - (c + 1)).to_int();
|
||||
step = STEP_OFF_X_BEGIN;
|
||||
}
|
||||
c = next - 1;
|
||||
continue;
|
||||
}
|
||||
} break;
|
||||
case STEP_START_READ_HEX:
|
||||
case STEP_END_READ_HEX: {
|
||||
// Read hexadecimal value.
|
||||
if (is_hex_digit(range[c])) {
|
||||
token += range[c];
|
||||
} else {
|
||||
if (step == STEP_START_READ_HEX) {
|
||||
start = token.hex_to_int();
|
||||
step = STEP_END_BEGIN;
|
||||
} else {
|
||||
end = token.hex_to_int();
|
||||
step = STEP_ADVANCE_BEGIN;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case STEP_START_READ_DEC:
|
||||
case STEP_END_READ_DEC: {
|
||||
// Read decimal value.
|
||||
if (is_digit(range[c])) {
|
||||
token += range[c];
|
||||
} else {
|
||||
if (step == STEP_START_READ_DEC) {
|
||||
start = token.to_int();
|
||||
step = STEP_END_BEGIN;
|
||||
} else {
|
||||
end = token.to_int();
|
||||
step = STEP_ADVANCE_BEGIN;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
WARN_PRINT(vformat("Invalid character \"%d\" in the range: \"%s\"", c, range));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (step == STEP_START_READ_HEX) {
|
||||
start = token.hex_to_int();
|
||||
} else if (step == STEP_START_READ_DEC) {
|
||||
start = token.to_int();
|
||||
} else if (step == STEP_END_READ_HEX) {
|
||||
end = token.hex_to_int();
|
||||
} else if (step == STEP_END_READ_DEC) {
|
||||
end = token.to_int();
|
||||
}
|
||||
if (end == -1) {
|
||||
end = start;
|
||||
}
|
||||
|
||||
if (start == -1) {
|
||||
WARN_PRINT(vformat("Invalid range: \"%s\"", range));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t idx = MIN(start, end); idx <= MAX(start, end); idx++) {
|
||||
ERR_FAIL_COND_V_MSG(pos >= count, ERR_CANT_CREATE, "Too many characters in range, should be " + itos(columns * rows));
|
||||
int x = pos % columns;
|
||||
int y = pos / columns;
|
||||
font->set_glyph_advance(0, chr_height, idx, Vector2(chr_width + chr_adv, 0));
|
||||
font->set_glyph_offset(0, Vector2i(chr_height, 0), idx, Vector2i(0, -0.5 * chr_height) + chr_off);
|
||||
font->set_glyph_size(0, Vector2i(chr_height, 0), idx, Vector2(chr_width, chr_height));
|
||||
font->set_glyph_uv_rect(0, Vector2i(chr_height, 0), idx, Rect2(img_margin.position.x + chr_cell_width * x + char_margin.position.x, img_margin.position.y + chr_cell_height * y + char_margin.position.y, chr_width, chr_height));
|
||||
font->set_glyph_texture_idx(0, Vector2i(chr_height, 0), idx, 0);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
for (const String &kp : kern) {
|
||||
const Vector<String> &kp_tokens = kp.split(" ");
|
||||
if (kp_tokens.size() != 3) {
|
||||
WARN_PRINT(vformat("Invalid kerning pairs string: \"%s\"", kp));
|
||||
continue;
|
||||
}
|
||||
String from_tokens;
|
||||
for (int i = 0; i < kp_tokens[0].length(); i++) {
|
||||
if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') {
|
||||
char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int();
|
||||
from_tokens += charcode;
|
||||
} else {
|
||||
from_tokens += kp_tokens[0][i];
|
||||
}
|
||||
}
|
||||
String to_tokens;
|
||||
for (int i = 0; i < kp_tokens[1].length(); i++) {
|
||||
if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') {
|
||||
char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int();
|
||||
to_tokens += charcode;
|
||||
} else {
|
||||
to_tokens += kp_tokens[1][i];
|
||||
}
|
||||
}
|
||||
int offset = kp_tokens[2].to_int();
|
||||
|
||||
for (int a = 0; a < from_tokens.length(); a++) {
|
||||
for (int b = 0; b < to_tokens.length(); b++) {
|
||||
font->set_kerning(0, chr_height, Vector2i(from_tokens.unicode_at(a), to_tokens.unicode_at(b)), Vector2(offset, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ascent > 0) {
|
||||
font->set_cache_ascent(0, chr_height, ascent);
|
||||
} else {
|
||||
font->set_cache_ascent(0, chr_height, 0.5 * chr_height);
|
||||
}
|
||||
|
||||
if (descent > 0) {
|
||||
font->set_cache_descent(0, chr_height, descent);
|
||||
} else {
|
||||
font->set_cache_descent(0, chr_height, 0.5 * chr_height);
|
||||
}
|
||||
|
||||
int flg = 0;
|
||||
if ((bool)p_options["compress"]) {
|
||||
flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
|
||||
}
|
||||
|
||||
print_verbose("Saving to: " + p_save_path + ".fontdata");
|
||||
err = ResourceSaver::save(font, p_save_path + ".fontdata", flg);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
|
||||
print_verbose("Done saving to: " + p_save_path + ".fontdata");
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterImageFont::ResourceImporterImageFont() {
|
||||
}
|
||||
56
engine/editor/import/resource_importer_imagefont.h
Normal file
56
engine/editor/import/resource_importer_imagefont.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_imagefont.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_IMAGEFONT_H
|
||||
#define RESOURCE_IMPORTER_IMAGEFONT_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
class ResourceImporterImageFont : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterImageFont, 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 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;
|
||||
|
||||
ResourceImporterImageFont();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_IMAGEFONT_H
|
||||
582
engine/editor/import/resource_importer_layered_texture.cpp
Normal file
582
engine/editor/import/resource_importer_layered_texture.cpp
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_layered_texture.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_layered_texture.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/import/resource_importer_texture.h"
|
||||
#include "editor/import/resource_importer_texture_settings.h"
|
||||
#include "scene/resources/compressed_texture.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
String ResourceImporterLayeredTexture::get_importer_name() const {
|
||||
switch (mode) {
|
||||
case MODE_CUBEMAP: {
|
||||
return "cubemap_texture";
|
||||
} break;
|
||||
case MODE_2D_ARRAY: {
|
||||
return "2d_array_texture";
|
||||
} break;
|
||||
case MODE_CUBEMAP_ARRAY: {
|
||||
return "cubemap_array_texture";
|
||||
} break;
|
||||
case MODE_3D: {
|
||||
return "3d_texture";
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V("");
|
||||
}
|
||||
|
||||
String ResourceImporterLayeredTexture::get_visible_name() const {
|
||||
switch (mode) {
|
||||
case MODE_CUBEMAP: {
|
||||
return "Cubemap";
|
||||
} break;
|
||||
case MODE_2D_ARRAY: {
|
||||
return "Texture2DArray";
|
||||
} break;
|
||||
case MODE_CUBEMAP_ARRAY: {
|
||||
return "CubemapArray";
|
||||
} break;
|
||||
case MODE_3D: {
|
||||
return "Texture3D";
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V("");
|
||||
}
|
||||
|
||||
void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
|
||||
String ResourceImporterLayeredTexture::get_save_extension() const {
|
||||
switch (mode) {
|
||||
case MODE_CUBEMAP: {
|
||||
return "ccube";
|
||||
} break;
|
||||
case MODE_2D_ARRAY: {
|
||||
return "ctexarray";
|
||||
} break;
|
||||
case MODE_CUBEMAP_ARRAY: {
|
||||
return "ccubearray";
|
||||
} break;
|
||||
case MODE_3D: {
|
||||
return "ctex3d";
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
String ResourceImporterLayeredTexture::get_resource_type() const {
|
||||
switch (mode) {
|
||||
case MODE_CUBEMAP: {
|
||||
return "CompressedCubemap";
|
||||
} break;
|
||||
case MODE_2D_ARRAY: {
|
||||
return "CompressedTexture2DArray";
|
||||
} break;
|
||||
case MODE_CUBEMAP_ARRAY: {
|
||||
return "CompressedCubemapArray";
|
||||
} break;
|
||||
case MODE_3D: {
|
||||
return "CompressedTexture3D";
|
||||
} break;
|
||||
}
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
|
||||
bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) {
|
||||
return int(p_options["compress/mode"]) == COMPRESS_LOSSY;
|
||||
}
|
||||
if ((p_option == "compress/high_quality" || p_option == "compress/hdr_compression") && p_options.has("compress/mode")) {
|
||||
return int(p_options["compress/mode"]) == COMPRESS_VRAM_COMPRESSED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterLayeredTexture::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
|
||||
|
||||
if (mode == MODE_2D_ARRAY || mode == MODE_3D) {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), 8));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), 8));
|
||||
}
|
||||
if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/arrangement", PROPERTY_HINT_ENUM, "1x6,2x3,3x2,6x1"), 1));
|
||||
if (mode == MODE_CUBEMAP_ARRAY) {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/layout", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2) {
|
||||
Vector<Ref<Image>> mipmap_images; //for 3D
|
||||
|
||||
if (mode == MODE_3D) {
|
||||
//3D saves in its own way
|
||||
|
||||
for (int i = 0; i < p_images.size(); i++) {
|
||||
if (p_images.write[i]->has_mipmaps()) {
|
||||
p_images.write[i]->clear_mipmaps();
|
||||
}
|
||||
|
||||
if (p_force_po2) {
|
||||
p_images.write[i]->resize_to_po2();
|
||||
}
|
||||
}
|
||||
|
||||
if (p_mipmaps) {
|
||||
Vector<Ref<Image>> parent_images = p_images;
|
||||
//create 3D mipmaps, this is horrible, though not used very often
|
||||
int w = p_images[0]->get_width();
|
||||
int h = p_images[0]->get_height();
|
||||
int d = p_images.size();
|
||||
|
||||
while (w > 1 || h > 1 || d > 1) {
|
||||
Vector<Ref<Image>> mipmaps;
|
||||
int mm_w = MAX(1, w >> 1);
|
||||
int mm_h = MAX(1, h >> 1);
|
||||
int mm_d = MAX(1, d >> 1);
|
||||
|
||||
for (int i = 0; i < mm_d; i++) {
|
||||
Ref<Image> mm = Image::create_empty(mm_w, mm_h, false, p_images[0]->get_format());
|
||||
Vector3 pos;
|
||||
pos.z = float(i) * float(d) / float(mm_d) + 0.5;
|
||||
for (int x = 0; x < mm_w; x++) {
|
||||
for (int y = 0; y < mm_h; y++) {
|
||||
pos.x = float(x) * float(w) / float(mm_w) + 0.5;
|
||||
pos.y = float(y) * float(h) / float(mm_h) + 0.5;
|
||||
|
||||
Vector3i posi = Vector3i(pos);
|
||||
Vector3 fract = pos - Vector3(posi);
|
||||
Vector3i posi_n = posi;
|
||||
if (posi_n.x < w - 1) {
|
||||
posi_n.x++;
|
||||
}
|
||||
if (posi_n.y < h - 1) {
|
||||
posi_n.y++;
|
||||
}
|
||||
if (posi_n.z < d - 1) {
|
||||
posi_n.z++;
|
||||
}
|
||||
|
||||
Color c000 = parent_images[posi.z]->get_pixel(posi.x, posi.y);
|
||||
Color c100 = parent_images[posi.z]->get_pixel(posi_n.x, posi.y);
|
||||
Color c010 = parent_images[posi.z]->get_pixel(posi.x, posi_n.y);
|
||||
Color c110 = parent_images[posi.z]->get_pixel(posi_n.x, posi_n.y);
|
||||
Color c001 = parent_images[posi_n.z]->get_pixel(posi.x, posi.y);
|
||||
Color c101 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi.y);
|
||||
Color c011 = parent_images[posi_n.z]->get_pixel(posi.x, posi_n.y);
|
||||
Color c111 = parent_images[posi_n.z]->get_pixel(posi_n.x, posi_n.y);
|
||||
|
||||
Color cx00 = c000.lerp(c100, fract.x);
|
||||
Color cx01 = c001.lerp(c101, fract.x);
|
||||
Color cx10 = c010.lerp(c110, fract.x);
|
||||
Color cx11 = c011.lerp(c111, fract.x);
|
||||
|
||||
Color cy0 = cx00.lerp(cx10, fract.y);
|
||||
Color cy1 = cx01.lerp(cx11, fract.y);
|
||||
|
||||
Color cz = cy0.lerp(cy1, fract.z);
|
||||
|
||||
mm->set_pixel(x, y, cz);
|
||||
}
|
||||
}
|
||||
|
||||
mipmaps.push_back(mm);
|
||||
}
|
||||
|
||||
w = mm_w;
|
||||
h = mm_h;
|
||||
d = mm_d;
|
||||
|
||||
mipmap_images.append_array(mipmaps);
|
||||
parent_images = mipmaps;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < p_images.size(); i++) {
|
||||
if (p_force_po2) {
|
||||
p_images.write[i]->resize_to_po2();
|
||||
}
|
||||
|
||||
if (p_mipmaps) {
|
||||
p_images.write[i]->generate_mipmaps(p_csource == Image::COMPRESS_SOURCE_NORMAL);
|
||||
} else {
|
||||
p_images.write[i]->clear_mipmaps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE);
|
||||
f->store_8('G');
|
||||
f->store_8('S');
|
||||
f->store_8('T');
|
||||
f->store_8('L');
|
||||
|
||||
f->store_32(CompressedTextureLayered::FORMAT_VERSION);
|
||||
f->store_32(p_images.size()); // For 2d layers or 3d depth.
|
||||
f->store_32(mode);
|
||||
f->store_32(0);
|
||||
|
||||
f->store_32(0);
|
||||
f->store_32(mipmap_images.size()); // Adjust the amount of mipmaps.
|
||||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
|
||||
for (int i = 0; i < p_images.size(); i++) {
|
||||
ResourceImporterTexture::save_to_ctex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mipmap_images.size(); i++) {
|
||||
ResourceImporterTexture::save_to_ctex_format(f, mipmap_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy);
|
||||
}
|
||||
}
|
||||
|
||||
Error ResourceImporterLayeredTexture::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) {
|
||||
int compress_mode = p_options["compress/mode"];
|
||||
float lossy = p_options["compress/lossy_quality"];
|
||||
bool high_quality = p_options["compress/high_quality"];
|
||||
int hdr_compression = p_options["compress/hdr_compression"];
|
||||
bool mipmaps = p_options["mipmaps/generate"];
|
||||
|
||||
int channel_pack = p_options["compress/channel_pack"];
|
||||
int hslices = (p_options.has("slices/horizontal")) ? int(p_options["slices/horizontal"]) : 0;
|
||||
int vslices = (p_options.has("slices/vertical")) ? int(p_options["slices/vertical"]) : 0;
|
||||
int arrangement = (p_options.has("slices/arrangement")) ? int(p_options["slices/arrangement"]) : 0;
|
||||
int layout = (p_options.has("slices/layout")) ? int(p_options["slices/layout"]) : 0;
|
||||
int amount = (p_options.has("slices/amount")) ? int(p_options["slices/amount"]) : 0;
|
||||
|
||||
if (mode == MODE_CUBEMAP || mode == MODE_CUBEMAP_ARRAY) {
|
||||
switch (arrangement) {
|
||||
case CUBEMAP_FORMAT_1X6: {
|
||||
hslices = 1;
|
||||
vslices = 6;
|
||||
} break;
|
||||
case CUBEMAP_FORMAT_2X3: {
|
||||
hslices = 2;
|
||||
vslices = 3;
|
||||
} break;
|
||||
case CUBEMAP_FORMAT_3X2: {
|
||||
hslices = 3;
|
||||
vslices = 2;
|
||||
} break;
|
||||
case CUBEMAP_FORMAT_6X1: {
|
||||
hslices = 6;
|
||||
vslices = 1;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (mode == MODE_CUBEMAP_ARRAY) {
|
||||
if (layout == 0) {
|
||||
hslices *= amount;
|
||||
} else {
|
||||
vslices *= amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
Error err = ImageLoader::load_image(p_source_file, image);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
|
||||
//basis universal does not support float formats, fall back
|
||||
compress_mode = COMPRESS_VRAM_COMPRESSED;
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
|
||||
mipmaps = true;
|
||||
|
||||
//if using video ram, optimize
|
||||
if (channel_pack == 0) {
|
||||
//remove alpha if not needed, so compression is more efficient
|
||||
if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) {
|
||||
image->convert(Image::FORMAT_RGB8);
|
||||
}
|
||||
} else if (image->get_format() < Image::FORMAT_RGBA8) {
|
||||
image->optimize_channels();
|
||||
}
|
||||
}
|
||||
|
||||
Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
|
||||
if (channel_pack == 0) {
|
||||
csource = Image::COMPRESS_SOURCE_SRGB;
|
||||
} else if (channel_pack == 2) {
|
||||
// force normal
|
||||
csource = Image::COMPRESS_SOURCE_NORMAL;
|
||||
}
|
||||
|
||||
Image::UsedChannels used_channels = image->detect_used_channels(csource);
|
||||
|
||||
Vector<Ref<Image>> slices;
|
||||
|
||||
int slice_w = image->get_width() / hslices;
|
||||
int slice_h = image->get_height() / vslices;
|
||||
|
||||
for (int i = 0; i < vslices; i++) {
|
||||
for (int j = 0; j < hslices; j++) {
|
||||
int x = slice_w * j;
|
||||
int y = slice_h * i;
|
||||
Ref<Image> slice = image->get_region(Rect2i(x, y, slice_w, slice_h));
|
||||
ERR_CONTINUE(slice.is_null() || slice->is_empty());
|
||||
if (slice->get_width() != slice_w || slice->get_height() != slice_h) {
|
||||
slice->resize(slice_w, slice_h);
|
||||
}
|
||||
slices.push_back(slice);
|
||||
}
|
||||
}
|
||||
Array formats_imported;
|
||||
Ref<LayeredTextureImport> texture_import;
|
||||
texture_import.instantiate();
|
||||
texture_import->csource = &csource;
|
||||
texture_import->save_path = p_save_path;
|
||||
texture_import->options = p_options;
|
||||
texture_import->platform_variants = r_platform_variants;
|
||||
texture_import->image = image;
|
||||
texture_import->formats_imported = formats_imported;
|
||||
texture_import->slices = &slices;
|
||||
texture_import->compress_mode = compress_mode;
|
||||
texture_import->lossy = lossy;
|
||||
texture_import->hdr_compression = hdr_compression;
|
||||
texture_import->mipmaps = mipmaps;
|
||||
texture_import->used_channels = used_channels;
|
||||
texture_import->high_quality = high_quality;
|
||||
|
||||
_check_compress_ctex(p_source_file, texture_import);
|
||||
if (r_metadata) {
|
||||
Dictionary meta;
|
||||
meta["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
|
||||
if (formats_imported.size()) {
|
||||
meta["imported_formats"] = formats_imported;
|
||||
}
|
||||
*r_metadata = meta;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const char *ResourceImporterLayeredTexture::compression_formats[] = {
|
||||
"s3tc_bptc",
|
||||
"etc2_astc",
|
||||
nullptr
|
||||
};
|
||||
|
||||
String ResourceImporterLayeredTexture::get_import_settings_string() const {
|
||||
String s;
|
||||
|
||||
int index = 0;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
s += String(compression_formats[index]);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const {
|
||||
//will become invalid if formats are missing to import
|
||||
Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
|
||||
|
||||
if (!meta.has("vram_texture")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vram = meta["vram_texture"];
|
||||
if (!vram) {
|
||||
return true; //do not care about non vram
|
||||
}
|
||||
|
||||
Vector<String> formats_imported;
|
||||
if (meta.has("imported_formats")) {
|
||||
formats_imported = meta["imported_formats"];
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
bool valid = true;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
if (ProjectSettings::get_singleton()->has_setting(setting_path)) {
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
if (!formats_imported.has(compression_formats[index])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WARN_PRINT("Setting for imported format not found: " + setting_path);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = nullptr;
|
||||
|
||||
ResourceImporterLayeredTexture::ResourceImporterLayeredTexture(bool p_singleton) {
|
||||
// This should only be set through the EditorNode.
|
||||
if (p_singleton) {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
mode = MODE_CUBEMAP;
|
||||
}
|
||||
|
||||
ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
|
||||
if (singleton == this) {
|
||||
singleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import) {
|
||||
String extension = get_save_extension();
|
||||
ERR_FAIL_NULL(r_texture_import->csource);
|
||||
if (r_texture_import->compress_mode != COMPRESS_VRAM_COMPRESSED) {
|
||||
// Import normally.
|
||||
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
|
||||
return;
|
||||
}
|
||||
// Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
|
||||
// Android, GLES 2.x
|
||||
|
||||
const bool can_s3tc_bptc = ResourceImporterTextureSettings::should_import_s3tc_bptc();
|
||||
const bool can_etc2_astc = ResourceImporterTextureSettings::should_import_etc2_astc();
|
||||
|
||||
// Add list of formats imported
|
||||
if (can_s3tc_bptc) {
|
||||
r_texture_import->formats_imported.push_back("s3tc_bptc");
|
||||
}
|
||||
if (can_etc2_astc) {
|
||||
r_texture_import->formats_imported.push_back("etc2_astc");
|
||||
}
|
||||
|
||||
bool can_compress_hdr = r_texture_import->hdr_compression > 0;
|
||||
ERR_FAIL_NULL(r_texture_import->image);
|
||||
bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995);
|
||||
ERR_FAIL_NULL(r_texture_import->slices);
|
||||
// Can compress hdr, but hdr with alpha is not compressible.
|
||||
bool use_uncompressed = false;
|
||||
|
||||
if (is_hdr) {
|
||||
if (r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA) {
|
||||
if (r_texture_import->hdr_compression == 2) {
|
||||
// The user selected to compress hdr anyway, so force an alpha-less format.
|
||||
if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) {
|
||||
for (int i = 0; i < r_texture_import->slices->size(); i++) {
|
||||
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF);
|
||||
}
|
||||
|
||||
} else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) {
|
||||
for (int i = 0; i < r_texture_import->slices->size(); i++) {
|
||||
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
can_compress_hdr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_compress_hdr) {
|
||||
//default to rgbe
|
||||
if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) {
|
||||
for (int i = 0; i < r_texture_import->slices->size(); i++) {
|
||||
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995);
|
||||
}
|
||||
}
|
||||
use_uncompressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_uncompressed) {
|
||||
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, COMPRESS_VRAM_UNCOMPRESSED, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
|
||||
} else {
|
||||
if (can_s3tc_bptc) {
|
||||
Image::CompressMode image_compress_mode;
|
||||
String image_compress_format;
|
||||
if (r_texture_import->high_quality || is_hdr) {
|
||||
image_compress_mode = Image::COMPRESS_BPTC;
|
||||
image_compress_format = "bptc";
|
||||
} else {
|
||||
image_compress_mode = Image::COMPRESS_S3TC;
|
||||
image_compress_format = "s3tc";
|
||||
}
|
||||
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true);
|
||||
r_texture_import->platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
|
||||
if (can_etc2_astc) {
|
||||
Image::CompressMode image_compress_mode;
|
||||
String image_compress_format;
|
||||
if (r_texture_import->high_quality || is_hdr) {
|
||||
image_compress_mode = Image::COMPRESS_ASTC;
|
||||
image_compress_format = "astc";
|
||||
} else {
|
||||
image_compress_mode = Image::COMPRESS_ETC2;
|
||||
image_compress_format = "etc2";
|
||||
}
|
||||
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true);
|
||||
r_texture_import->platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
126
engine/editor/import/resource_importer_layered_texture.h
Normal file
126
engine/editor/import/resource_importer_layered_texture.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_layered_texture.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_LAYERED_TEXTURE_H
|
||||
#define RESOURCE_IMPORTER_LAYERED_TEXTURE_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class CompressedTexture2D;
|
||||
|
||||
class LayeredTextureImport : public RefCounted {
|
||||
GDCLASS(LayeredTextureImport, RefCounted);
|
||||
|
||||
public:
|
||||
Image::CompressSource *csource = nullptr;
|
||||
String save_path;
|
||||
HashMap<StringName, Variant> options;
|
||||
List<String> *platform_variants = nullptr;
|
||||
Ref<Image> image = nullptr;
|
||||
Array formats_imported;
|
||||
Vector<Ref<Image>> *slices = nullptr;
|
||||
int compress_mode = 0;
|
||||
float lossy = 1.0;
|
||||
int hdr_compression = 0;
|
||||
bool mipmaps = true;
|
||||
bool high_quality = false;
|
||||
Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA;
|
||||
virtual ~LayeredTextureImport() {}
|
||||
};
|
||||
|
||||
class ResourceImporterLayeredTexture : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterLayeredTexture, ResourceImporter);
|
||||
|
||||
public:
|
||||
enum Mode {
|
||||
MODE_2D_ARRAY,
|
||||
MODE_CUBEMAP,
|
||||
MODE_CUBEMAP_ARRAY,
|
||||
MODE_3D,
|
||||
};
|
||||
|
||||
enum CubemapFormat {
|
||||
CUBEMAP_FORMAT_1X6,
|
||||
CUBEMAP_FORMAT_2X3,
|
||||
CUBEMAP_FORMAT_3X2,
|
||||
CUBEMAP_FORMAT_6X1,
|
||||
};
|
||||
|
||||
enum TextureFlags {
|
||||
TEXTURE_FLAGS_MIPMAPS = 1
|
||||
};
|
||||
|
||||
private:
|
||||
Mode mode;
|
||||
static const char *compression_formats[];
|
||||
|
||||
protected:
|
||||
static ResourceImporterLayeredTexture *singleton;
|
||||
|
||||
public:
|
||||
void _check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import);
|
||||
|
||||
static ResourceImporterLayeredTexture *get_singleton() { return singleton; }
|
||||
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;
|
||||
|
||||
enum CompressMode {
|
||||
COMPRESS_LOSSLESS,
|
||||
COMPRESS_LOSSY,
|
||||
COMPRESS_VRAM_COMPRESSED,
|
||||
COMPRESS_VRAM_UNCOMPRESSED,
|
||||
COMPRESS_BASIS_UNIVERSAL
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2);
|
||||
|
||||
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 are_import_settings_valid(const String &p_path) const override;
|
||||
virtual String get_import_settings_string() const override;
|
||||
|
||||
void set_mode(Mode p_mode) { mode = p_mode; }
|
||||
|
||||
ResourceImporterLayeredTexture(bool p_singleton = false);
|
||||
~ResourceImporterLayeredTexture();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H
|
||||
120
engine/editor/import/resource_importer_shader_file.cpp
Normal file
120
engine/editor/import/resource_importer_shader_file.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_shader_file.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_shader_file.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/shader_file_editor_plugin.h"
|
||||
#include "servers/rendering/rendering_device_binds.h"
|
||||
|
||||
String ResourceImporterShaderFile::get_importer_name() const {
|
||||
return "glsl";
|
||||
}
|
||||
|
||||
String ResourceImporterShaderFile::get_visible_name() const {
|
||||
return "GLSL Shader File";
|
||||
}
|
||||
|
||||
void ResourceImporterShaderFile::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
p_extensions->push_back("glsl");
|
||||
}
|
||||
|
||||
String ResourceImporterShaderFile::get_save_extension() const {
|
||||
return "res";
|
||||
}
|
||||
|
||||
String ResourceImporterShaderFile::get_resource_type() const {
|
||||
return "RDShaderFile";
|
||||
}
|
||||
|
||||
int ResourceImporterShaderFile::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterShaderFile::get_preset_name(int p_idx) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceImporterShaderFile::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
}
|
||||
|
||||
bool ResourceImporterShaderFile::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
static String _include_function(const String &p_path, void *userpointer) {
|
||||
Error err;
|
||||
|
||||
String *base_path = (String *)userpointer;
|
||||
|
||||
String include = p_path;
|
||||
if (include.is_relative_path()) {
|
||||
include = base_path->path_join(include);
|
||||
}
|
||||
|
||||
Ref<FileAccess> file_inc = FileAccess::open(include, FileAccess::READ, &err);
|
||||
if (err != OK) {
|
||||
return String();
|
||||
}
|
||||
return file_inc->get_as_utf8_string();
|
||||
}
|
||||
|
||||
Error ResourceImporterShaderFile::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) {
|
||||
/* STEP 1, Read shader code */
|
||||
ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders.");
|
||||
ERR_FAIL_COND_V_EDMSG((DisplayServer::get_singleton()->get_name() == "headless"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when running in headless mode.");
|
||||
|
||||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
|
||||
ERR_FAIL_COND_V(!file.is_valid(), ERR_CANT_OPEN);
|
||||
|
||||
String file_txt = file->get_as_utf8_string();
|
||||
Ref<RDShaderFile> shader_file;
|
||||
shader_file.instantiate();
|
||||
String base_path = p_source_file.get_base_dir();
|
||||
err = shader_file->parse_versions_from_text(file_txt, "", _include_function, &base_path);
|
||||
|
||||
if (err != OK) {
|
||||
if (!ShaderFileEditor::singleton->is_visible_in_tree()) {
|
||||
callable_mp_static(&EditorNode::add_io_error).call_deferred(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file));
|
||||
}
|
||||
}
|
||||
|
||||
ResourceSaver::save(shader_file, p_save_path + ".res");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterShaderFile::ResourceImporterShaderFile() {
|
||||
}
|
||||
57
engine/editor/import/resource_importer_shader_file.h
Normal file
57
engine/editor/import/resource_importer_shader_file.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_shader_file.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_SHADER_FILE_H
|
||||
#define RESOURCE_IMPORTER_SHADER_FILE_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterShaderFile : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterShaderFile, 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_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;
|
||||
|
||||
ResourceImporterShaderFile();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_SHADER_FILE_H
|
||||
816
engine/editor/import/resource_importer_texture.cpp
Normal file
816
engine/editor/import/resource_importer_texture.cpp
Normal file
|
|
@ -0,0 +1,816 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture.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_texture.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/gui/editor_toaster.h"
|
||||
#include "editor/import/resource_importer_texture_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/resources/compressed_texture.h"
|
||||
|
||||
void ResourceImporterTexture::_texture_reimport_roughness(const Ref<CompressedTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) {
|
||||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
singleton->make_flags[path] = MakeInfo();
|
||||
}
|
||||
|
||||
singleton->make_flags[path].flags |= MAKE_ROUGHNESS_FLAG;
|
||||
singleton->make_flags[path].channel_for_roughness = p_channel;
|
||||
singleton->make_flags[path].normal_path_for_roughness = p_normal_path;
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::_texture_reimport_3d(const Ref<CompressedTexture2D> &p_tex) {
|
||||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
singleton->make_flags[path] = MakeInfo();
|
||||
}
|
||||
|
||||
singleton->make_flags[path].flags |= MAKE_3D_FLAG;
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::_texture_reimport_normal(const Ref<CompressedTexture2D> &p_tex) {
|
||||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
singleton->make_flags[path] = MakeInfo();
|
||||
}
|
||||
|
||||
singleton->make_flags[path].flags |= MAKE_NORMAL_FLAG;
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::update_imports() {
|
||||
if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) {
|
||||
return; // do nothing for now
|
||||
}
|
||||
|
||||
MutexLock lock(mutex);
|
||||
Vector<String> to_reimport;
|
||||
{
|
||||
if (make_flags.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const KeyValue<StringName, MakeInfo> &E : make_flags) {
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
String src_path = String(E.key) + ".import";
|
||||
|
||||
Error err = cf->load(src_path);
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (E.value.flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
|
||||
String message = vformat(TTR("%s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded)."), String(E.key));
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "compress/normal_map", 1);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
|
||||
String message = vformat(TTR("%s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s."), String(E.key), E.value.normal_path_for_roughness);
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "roughness/mode", E.value.channel_for_roughness + 2);
|
||||
cf->set_value("params", "roughness/src_normal", E.value.normal_path_for_roughness);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) {
|
||||
const int compress_to = cf->get_value("params", "detect_3d/compress_to");
|
||||
String compress_string;
|
||||
cf->set_value("params", "detect_3d/compress_to", 0);
|
||||
if (compress_to == 1) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_VRAM_COMPRESSED);
|
||||
compress_string = "VRAM Compressed (S3TC/ETC/BPTC)";
|
||||
} else if (compress_to == 2) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_BASIS_UNIVERSAL);
|
||||
compress_string = "Basis Universal";
|
||||
}
|
||||
String message = vformat(TTR("%s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s."), String(E.key), compress_string);
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "mipmaps/generate", true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
cf->save(src_path);
|
||||
to_reimport.push_back(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
make_flags.clear();
|
||||
}
|
||||
|
||||
if (to_reimport.size()) {
|
||||
EditorFileSystem::get_singleton()->reimport_files(to_reimport);
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceImporterTexture::get_importer_name() const {
|
||||
return "texture";
|
||||
}
|
||||
|
||||
String ResourceImporterTexture::get_visible_name() const {
|
||||
return "Texture2D";
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
|
||||
String ResourceImporterTexture::get_save_extension() const {
|
||||
return "ctex";
|
||||
}
|
||||
|
||||
String ResourceImporterTexture::get_resource_type() const {
|
||||
return "CompressedTexture2D";
|
||||
}
|
||||
|
||||
bool ResourceImporterTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "compress/high_quality" || p_option == "compress/hdr_compression") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode != COMPRESS_VRAM_COMPRESSED) {
|
||||
return false;
|
||||
}
|
||||
} else if (p_option == "compress/lossy_quality") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode != COMPRESS_LOSSY) {
|
||||
return false;
|
||||
}
|
||||
} else if (p_option == "compress/hdr_mode") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode < COMPRESS_VRAM_COMPRESSED) {
|
||||
return false;
|
||||
}
|
||||
} else if (p_option == "compress/normal_map") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode == COMPRESS_LOSSLESS) {
|
||||
return false;
|
||||
}
|
||||
} else if (p_option == "mipmaps/limit") {
|
||||
return p_options["mipmaps/generate"];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterTexture::get_preset_count() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
String ResourceImporterTexture::get_preset_name(int p_idx) const {
|
||||
static const char *preset_names[] = {
|
||||
TTRC("2D/3D (Auto-Detect)"),
|
||||
TTRC("2D"),
|
||||
TTRC("3D"),
|
||||
};
|
||||
|
||||
return TTRGET(preset_names[p_idx]);
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false)));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "roughness/mode", PROPERTY_HINT_ENUM, "Detect,Disabled,Red,Green,Blue,Alpha,Gray"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.tga,*.webp"), ""));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0));
|
||||
|
||||
// Do path based customization only if a path was passed.
|
||||
if (p_path.is_empty() || p_path.get_extension() == "svg") {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
|
||||
|
||||
// Editor use only, applies to SVG.
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/scale_with_editor_scale"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/convert_colors_with_editor_theme"), false));
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) {
|
||||
switch (p_compress_mode) {
|
||||
case COMPRESS_LOSSLESS: {
|
||||
bool lossless_force_png = GLOBAL_GET("rendering/textures/lossless_compression/force_png") ||
|
||||
!Image::_webp_mem_loader_func; // WebP module disabled.
|
||||
bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit
|
||||
f->store_32(use_webp ? CompressedTexture2D::DATA_FORMAT_WEBP : CompressedTexture2D::DATA_FORMAT_PNG);
|
||||
f->store_16(p_image->get_width());
|
||||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
|
||||
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
|
||||
Vector<uint8_t> data;
|
||||
if (use_webp) {
|
||||
data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image);
|
||||
} else {
|
||||
data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image);
|
||||
}
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
}
|
||||
|
||||
} break;
|
||||
case COMPRESS_LOSSY: {
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_WEBP);
|
||||
f->store_16(p_image->get_width());
|
||||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
|
||||
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
|
||||
Vector<uint8_t> data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality);
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
}
|
||||
} break;
|
||||
case COMPRESS_VRAM_COMPRESSED: {
|
||||
Ref<Image> image = p_image->duplicate();
|
||||
|
||||
image->compress_from_channels(p_compress_format, p_channels);
|
||||
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE);
|
||||
f->store_16(image->get_width());
|
||||
f->store_16(image->get_height());
|
||||
f->store_32(image->get_mipmap_count());
|
||||
f->store_32(image->get_format());
|
||||
|
||||
Vector<uint8_t> data = image->get_data();
|
||||
int dl = data.size();
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, dl);
|
||||
} break;
|
||||
case COMPRESS_VRAM_UNCOMPRESSED: {
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE);
|
||||
f->store_16(p_image->get_width());
|
||||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
|
||||
Vector<uint8_t> data = p_image->get_data();
|
||||
int dl = data.size();
|
||||
const uint8_t *r = data.ptr();
|
||||
|
||||
f->store_buffer(r, dl);
|
||||
|
||||
} break;
|
||||
case COMPRESS_BASIS_UNIVERSAL: {
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_BASIS_UNIVERSAL);
|
||||
f->store_16(p_image->get_width());
|
||||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
Vector<uint8_t> data = Image::basis_universal_packer(p_image, p_channels);
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND(f.is_null());
|
||||
f->store_8('G');
|
||||
f->store_8('S');
|
||||
f->store_8('T');
|
||||
f->store_8('2'); //godot streamable texture 2D
|
||||
|
||||
//format version
|
||||
f->store_32(CompressedTexture2D::FORMAT_VERSION);
|
||||
//texture may be resized later, so original size must be saved first
|
||||
f->store_32(p_image->get_width());
|
||||
f->store_32(p_image->get_height());
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (p_streamable) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_STREAM;
|
||||
}
|
||||
if (p_mipmaps) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
|
||||
}
|
||||
if (p_detect_3d) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_DETECT_3D;
|
||||
}
|
||||
if (p_detect_roughness) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_DETECT_ROUGNESS;
|
||||
}
|
||||
if (p_detect_normal) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_DETECT_NORMAL;
|
||||
}
|
||||
|
||||
f->store_32(flags);
|
||||
f->store_32(p_limit_mipmap);
|
||||
//reserved for future use
|
||||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
|
||||
if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) {
|
||||
p_compress_mode = COMPRESS_VRAM_UNCOMPRESSED; //these can't go as lossy
|
||||
}
|
||||
|
||||
Ref<Image> image = p_image->duplicate();
|
||||
|
||||
if (p_force_po2_for_compressed && p_mipmaps && ((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED))) {
|
||||
image->resize_to_po2();
|
||||
}
|
||||
|
||||
if (p_mipmaps && (!image->has_mipmaps() || p_force_normal)) {
|
||||
image->generate_mipmaps(p_force_normal);
|
||||
}
|
||||
|
||||
if (!p_mipmaps) {
|
||||
image->clear_mipmaps();
|
||||
}
|
||||
|
||||
if (image->has_mipmaps() && p_normal.is_valid()) {
|
||||
image->generate_mipmap_roughness(p_roughness_channel, p_normal);
|
||||
}
|
||||
|
||||
Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
|
||||
if (p_force_normal) {
|
||||
csource = Image::COMPRESS_SOURCE_NORMAL;
|
||||
} else if (p_srgb_friendly) {
|
||||
csource = Image::COMPRESS_SOURCE_SRGB;
|
||||
}
|
||||
|
||||
Image::UsedChannels used_channels = image->detect_used_channels(csource);
|
||||
|
||||
save_to_ctex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality);
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::_save_editor_meta(const Dictionary &p_metadata, const String &p_to_path) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND(f.is_null());
|
||||
|
||||
f->store_var(p_metadata);
|
||||
}
|
||||
|
||||
Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) const {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), Dictionary(), vformat("Missing required editor-specific import metadata for a texture (please reimport it using the 'Import' tab): '%s'", p_path));
|
||||
|
||||
return f->get_var();
|
||||
}
|
||||
|
||||
Error ResourceImporterTexture::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) {
|
||||
// Parse import options.
|
||||
int32_t loader_flags = ImageFormatLoader::FLAG_NONE;
|
||||
|
||||
// Compression.
|
||||
CompressMode compress_mode = CompressMode(int(p_options["compress/mode"]));
|
||||
const float lossy = p_options["compress/lossy_quality"];
|
||||
const int pack_channels = p_options["compress/channel_pack"];
|
||||
const int normal = p_options["compress/normal_map"];
|
||||
const int hdr_compression = p_options["compress/hdr_compression"];
|
||||
const int high_quality = p_options["compress/high_quality"];
|
||||
|
||||
// Mipmaps.
|
||||
const bool mipmaps = p_options["mipmaps/generate"];
|
||||
const uint32_t mipmap_limit = mipmaps ? uint32_t(p_options["mipmaps/limit"]) : uint32_t(-1);
|
||||
|
||||
// Roughness.
|
||||
const int roughness = p_options["roughness/mode"];
|
||||
const String normal_map = p_options["roughness/src_normal"];
|
||||
|
||||
// Processing.
|
||||
const bool fix_alpha_border = p_options["process/fix_alpha_border"];
|
||||
const bool premult_alpha = p_options["process/premult_alpha"];
|
||||
const bool normal_map_invert_y = p_options["process/normal_map_invert_y"];
|
||||
// Support for texture streaming is not implemented yet.
|
||||
const bool stream = false;
|
||||
const int size_limit = p_options["process/size_limit"];
|
||||
const bool hdr_as_srgb = p_options["process/hdr_as_srgb"];
|
||||
if (hdr_as_srgb) {
|
||||
loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR;
|
||||
}
|
||||
const bool hdr_clamp_exposure = p_options["process/hdr_clamp_exposure"];
|
||||
|
||||
float scale = 1.0;
|
||||
// SVG-specific options.
|
||||
if (p_options.has("svg/scale")) {
|
||||
scale = p_options["svg/scale"];
|
||||
}
|
||||
|
||||
// Editor-specific options.
|
||||
bool use_editor_scale = p_options.has("editor/scale_with_editor_scale") && p_options["editor/scale_with_editor_scale"];
|
||||
bool convert_editor_colors = p_options.has("editor/convert_colors_with_editor_theme") && p_options["editor/convert_colors_with_editor_theme"];
|
||||
|
||||
// Start importing images.
|
||||
List<Ref<Image>> images_imported;
|
||||
|
||||
// Load the normal image.
|
||||
Ref<Image> normal_image;
|
||||
Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R;
|
||||
if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) {
|
||||
normal_image.instantiate();
|
||||
if (ImageLoader::load_image(normal_map, normal_image) == OK) {
|
||||
roughness_channel = Image::RoughnessChannel(roughness - 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the main image.
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
images_imported.push_back(image);
|
||||
|
||||
// Load the editor-only image.
|
||||
Ref<Image> editor_image;
|
||||
bool import_editor_image = use_editor_scale || convert_editor_colors;
|
||||
if (import_editor_image) {
|
||||
float editor_scale = scale;
|
||||
if (use_editor_scale) {
|
||||
editor_scale = scale * EDSCALE;
|
||||
}
|
||||
|
||||
int32_t editor_loader_flags = loader_flags;
|
||||
if (convert_editor_colors) {
|
||||
editor_loader_flags |= ImageFormatLoader::FLAG_CONVERT_COLORS;
|
||||
}
|
||||
|
||||
editor_image.instantiate();
|
||||
err = ImageLoader::load_image(p_source_file, editor_image, nullptr, editor_loader_flags, editor_scale);
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Failed to import an image resource for editor use from '" + p_source_file + "'");
|
||||
} else {
|
||||
images_imported.push_back(editor_image);
|
||||
}
|
||||
}
|
||||
|
||||
for (Ref<Image> &target_image : images_imported) {
|
||||
// Apply the size limit.
|
||||
if (size_limit > 0 && (target_image->get_width() > size_limit || target_image->get_height() > size_limit)) {
|
||||
if (target_image->get_width() >= target_image->get_height()) {
|
||||
int new_width = size_limit;
|
||||
int new_height = target_image->get_height() * new_width / target_image->get_width();
|
||||
|
||||
target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
|
||||
} else {
|
||||
int new_height = size_limit;
|
||||
int new_width = target_image->get_width() * new_height / target_image->get_height();
|
||||
|
||||
target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
|
||||
}
|
||||
|
||||
if (normal == 1) {
|
||||
target_image->normalize();
|
||||
}
|
||||
}
|
||||
|
||||
// Fix alpha border.
|
||||
if (fix_alpha_border) {
|
||||
target_image->fix_alpha_edges();
|
||||
}
|
||||
|
||||
// Premultiply the alpha.
|
||||
if (premult_alpha) {
|
||||
target_image->premultiply_alpha();
|
||||
}
|
||||
|
||||
// Invert the green channel of the image to flip the normal map it contains.
|
||||
if (normal_map_invert_y) {
|
||||
// Inverting the green channel can be used to flip a normal map's direction.
|
||||
// There's no standard when it comes to normal map Y direction, so this is
|
||||
// sometimes needed when using a normal map exported from another program.
|
||||
// See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
|
||||
const int height = target_image->get_height();
|
||||
const int width = target_image->get_width();
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
const Color color = target_image->get_pixel(i, j);
|
||||
target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp HDR exposure.
|
||||
if (hdr_clamp_exposure) {
|
||||
// Clamp HDR exposure following Filament's tonemapping formula.
|
||||
// This can be used to reduce fireflies in environment maps or reduce the influence
|
||||
// of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead).
|
||||
const int height = target_image->get_height();
|
||||
const int width = target_image->get_width();
|
||||
|
||||
// These values are chosen arbitrarily and seem to produce good results with 4,096 samples.
|
||||
const float linear = 4096.0;
|
||||
const float compressed = 16384.0;
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
const Color color = target_image->get_pixel(i, j);
|
||||
const float luma = color.get_luminance();
|
||||
|
||||
Color clamped_color;
|
||||
if (luma <= linear) {
|
||||
clamped_color = color;
|
||||
} else {
|
||||
clamped_color = (color / luma) * ((linear * linear - compressed * luma) / (2 * linear - compressed - luma));
|
||||
}
|
||||
|
||||
target_image->set_pixel(i, j, clamped_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
|
||||
// Basis universal does not support float formats, fallback.
|
||||
compress_mode = COMPRESS_VRAM_COMPRESSED;
|
||||
}
|
||||
|
||||
bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0;
|
||||
bool detect_roughness = roughness == 0;
|
||||
bool detect_normal = normal == 0;
|
||||
bool force_normal = normal == 1;
|
||||
bool srgb_friendly_pack = pack_channels == 0;
|
||||
|
||||
Array formats_imported;
|
||||
|
||||
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
|
||||
// Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
|
||||
// Android, GLES 2.x
|
||||
|
||||
const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
|
||||
const bool can_s3tc_bptc = ResourceImporterTextureSettings::should_import_s3tc_bptc();
|
||||
const bool can_etc2_astc = ResourceImporterTextureSettings::should_import_etc2_astc();
|
||||
|
||||
// Add list of formats imported
|
||||
if (can_s3tc_bptc) {
|
||||
formats_imported.push_back("s3tc_bptc");
|
||||
}
|
||||
if (can_etc2_astc) {
|
||||
formats_imported.push_back("etc2_astc");
|
||||
}
|
||||
|
||||
bool can_compress_hdr = hdr_compression > 0;
|
||||
bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE;
|
||||
bool use_uncompressed = false;
|
||||
|
||||
if (is_hdr) {
|
||||
if (has_alpha) {
|
||||
// Can compress HDR, but HDR with alpha is not compressible.
|
||||
if (hdr_compression == 2) {
|
||||
// But user selected to compress HDR anyway, so force an alpha-less format.
|
||||
if (image->get_format() == Image::FORMAT_RGBAF) {
|
||||
image->convert(Image::FORMAT_RGBF);
|
||||
} else if (image->get_format() == Image::FORMAT_RGBAH) {
|
||||
image->convert(Image::FORMAT_RGBH);
|
||||
}
|
||||
} else {
|
||||
can_compress_hdr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_compress_hdr) {
|
||||
// Fallback to RGBE99995.
|
||||
if (image->get_format() != Image::FORMAT_RGBE9995) {
|
||||
image->convert(Image::FORMAT_RGBE9995);
|
||||
use_uncompressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_uncompressed) {
|
||||
_save_ctex(image, p_save_path + ".ctex", COMPRESS_VRAM_UNCOMPRESSED, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
} else {
|
||||
if (can_s3tc_bptc) {
|
||||
Image::CompressMode image_compress_mode;
|
||||
String image_compress_format;
|
||||
if (high_quality || is_hdr) {
|
||||
image_compress_mode = Image::COMPRESS_BPTC;
|
||||
image_compress_format = "bptc";
|
||||
} else {
|
||||
image_compress_mode = Image::COMPRESS_S3TC;
|
||||
image_compress_format = "s3tc";
|
||||
}
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
r_platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
|
||||
if (can_etc2_astc) {
|
||||
Image::CompressMode image_compress_mode;
|
||||
String image_compress_format;
|
||||
if (high_quality || is_hdr) {
|
||||
image_compress_mode = Image::COMPRESS_ASTC;
|
||||
image_compress_format = "astc";
|
||||
} else {
|
||||
image_compress_mode = Image::COMPRESS_ETC2;
|
||||
image_compress_format = "etc2";
|
||||
}
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
r_platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Import normally.
|
||||
_save_ctex(image, p_save_path + ".ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
}
|
||||
|
||||
if (editor_image.is_valid()) {
|
||||
_save_ctex(editor_image, p_save_path + ".editor.ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
|
||||
// Generate and save editor-specific metadata, which we cannot save to the .import file.
|
||||
Dictionary editor_meta;
|
||||
|
||||
if (use_editor_scale) {
|
||||
editor_meta["editor_scale"] = EDSCALE;
|
||||
}
|
||||
if (convert_editor_colors) {
|
||||
editor_meta["editor_dark_theme"] = EditorThemeManager::is_dark_theme();
|
||||
}
|
||||
|
||||
_save_editor_meta(editor_meta, p_save_path + ".editor.meta");
|
||||
}
|
||||
|
||||
if (r_metadata) {
|
||||
Dictionary meta;
|
||||
meta["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
|
||||
if (formats_imported.size()) {
|
||||
meta["imported_formats"] = formats_imported;
|
||||
}
|
||||
|
||||
if (editor_image.is_valid()) {
|
||||
meta["has_editor_variant"] = true;
|
||||
}
|
||||
|
||||
*r_metadata = meta;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const char *ResourceImporterTexture::compression_formats[] = {
|
||||
"s3tc_bptc",
|
||||
"etc2_astc",
|
||||
nullptr
|
||||
};
|
||||
String ResourceImporterTexture::get_import_settings_string() const {
|
||||
String s;
|
||||
|
||||
int index = 0;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
s += String(compression_formats[index]);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const {
|
||||
Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
|
||||
|
||||
if (meta.has("has_editor_variant")) {
|
||||
String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
|
||||
if (!FileAccess::exists(imported_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String editor_meta_path = imported_path.replace(".editor.ctex", ".editor.meta");
|
||||
Dictionary editor_meta = _load_editor_meta(editor_meta_path);
|
||||
|
||||
if (editor_meta.has("editor_scale") && (float)editor_meta["editor_scale"] != EDSCALE) {
|
||||
return false;
|
||||
}
|
||||
if (editor_meta.has("editor_dark_theme") && (bool)editor_meta["editor_dark_theme"] != EditorThemeManager::is_dark_theme()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!meta.has("vram_texture")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vram = meta["vram_texture"];
|
||||
if (!vram) {
|
||||
return true; // Do not care about non-VRAM.
|
||||
}
|
||||
|
||||
// Will become invalid if formats are missing to import.
|
||||
Vector<String> formats_imported;
|
||||
if (meta.has("imported_formats")) {
|
||||
formats_imported = meta["imported_formats"];
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
bool valid = true;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
if (ProjectSettings::get_singleton()->has_setting(setting_path)) {
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
if (!formats_imported.has(compression_formats[index])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WARN_PRINT("Setting for imported format not found: " + setting_path);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr;
|
||||
|
||||
ResourceImporterTexture::ResourceImporterTexture(bool p_singleton) {
|
||||
// This should only be set through the EditorNode.
|
||||
if (p_singleton) {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
CompressedTexture2D::request_3d_callback = _texture_reimport_3d;
|
||||
CompressedTexture2D::request_roughness_callback = _texture_reimport_roughness;
|
||||
CompressedTexture2D::request_normal_callback = _texture_reimport_normal;
|
||||
}
|
||||
|
||||
ResourceImporterTexture::~ResourceImporterTexture() {
|
||||
if (singleton == this) {
|
||||
singleton = nullptr;
|
||||
}
|
||||
}
|
||||
114
engine/editor/import/resource_importer_texture.h
Normal file
114
engine/editor/import/resource_importer_texture.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture.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_TEXTURE_H
|
||||
#define RESOURCE_IMPORTER_TEXTURE_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "scene/resources/texture.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
class CompressedTexture2D;
|
||||
|
||||
class ResourceImporterTexture : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterTexture, ResourceImporter);
|
||||
|
||||
public:
|
||||
enum CompressMode {
|
||||
COMPRESS_LOSSLESS,
|
||||
COMPRESS_LOSSY,
|
||||
COMPRESS_VRAM_COMPRESSED,
|
||||
COMPRESS_VRAM_UNCOMPRESSED,
|
||||
COMPRESS_BASIS_UNIVERSAL
|
||||
};
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAKE_3D_FLAG = 1,
|
||||
MAKE_ROUGHNESS_FLAG = 2,
|
||||
MAKE_NORMAL_FLAG = 4
|
||||
};
|
||||
|
||||
Mutex mutex;
|
||||
struct MakeInfo {
|
||||
int flags = 0;
|
||||
String normal_path_for_roughness;
|
||||
RS::TextureDetectRoughnessChannel channel_for_roughness = RS::TEXTURE_DETECT_ROUGHNESS_R;
|
||||
};
|
||||
|
||||
HashMap<StringName, MakeInfo> make_flags;
|
||||
|
||||
static void _texture_reimport_roughness(const Ref<CompressedTexture2D> &p_tex, const String &p_normal_path, RenderingServer::TextureDetectRoughnessChannel p_channel);
|
||||
static void _texture_reimport_3d(const Ref<CompressedTexture2D> &p_tex);
|
||||
static void _texture_reimport_normal(const Ref<CompressedTexture2D> &p_tex);
|
||||
|
||||
static ResourceImporterTexture *singleton;
|
||||
static const char *compression_formats[];
|
||||
|
||||
void _save_ctex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel);
|
||||
void _save_editor_meta(const Dictionary &p_metadata, const String &p_to_path);
|
||||
Dictionary _load_editor_meta(const String &p_to_path) const;
|
||||
|
||||
public:
|
||||
static void save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality);
|
||||
|
||||
static ResourceImporterTexture *get_singleton() { return singleton; }
|
||||
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;
|
||||
|
||||
enum Preset {
|
||||
PRESET_DETECT,
|
||||
PRESET_2D,
|
||||
PRESET_3D,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
void update_imports();
|
||||
|
||||
virtual bool are_import_settings_valid(const String &p_path) const override;
|
||||
virtual String get_import_settings_string() const override;
|
||||
|
||||
ResourceImporterTexture(bool p_singleton = false);
|
||||
~ResourceImporterTexture();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_TEXTURE_H
|
||||
409
engine/editor/import/resource_importer_texture_atlas.cpp
Normal file
409
engine/editor/import/resource_importer_texture_atlas.cpp
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture_atlas.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_texture_atlas.h"
|
||||
|
||||
#include "atlas_import_failed.xpm"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "editor/editor_atlas_packer.h"
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/mesh_texture.h"
|
||||
|
||||
String ResourceImporterTextureAtlas::get_importer_name() const {
|
||||
return "texture_atlas";
|
||||
}
|
||||
|
||||
String ResourceImporterTextureAtlas::get_visible_name() const {
|
||||
return "TextureAtlas";
|
||||
}
|
||||
|
||||
void ResourceImporterTextureAtlas::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
ImageLoader::get_recognized_extensions(p_extensions);
|
||||
}
|
||||
|
||||
String ResourceImporterTextureAtlas::get_save_extension() const {
|
||||
return "res";
|
||||
}
|
||||
|
||||
String ResourceImporterTextureAtlas::get_resource_type() const {
|
||||
return "Texture2D";
|
||||
}
|
||||
|
||||
bool ResourceImporterTextureAtlas::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "crop_to_region" && int(p_options["import_mode"]) != IMPORT_MODE_REGION) {
|
||||
return false;
|
||||
} else if (p_option == "trim_alpha_border_from_region" && int(p_options["import_mode"]) != IMPORT_MODE_REGION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterTextureAtlas::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterTextureAtlas::get_preset_name(int p_idx) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceImporterTextureAtlas::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "atlas_file", PROPERTY_HINT_SAVE_FILE, "*.png"), ""));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_mode", PROPERTY_HINT_ENUM, "Region,Mesh2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "crop_to_region"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "trim_alpha_border_from_region"), true));
|
||||
}
|
||||
|
||||
String ResourceImporterTextureAtlas::get_option_group_file() const {
|
||||
return "atlas_file";
|
||||
}
|
||||
|
||||
Error ResourceImporterTextureAtlas::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) {
|
||||
/* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */
|
||||
|
||||
//use an xpm because it's size independent, the editor images are vector and size dependent
|
||||
//it's a simple hack
|
||||
Ref<Image> broken = memnew(Image((const char **)atlas_import_failed_xpm));
|
||||
ResourceSaver::save(ImageTexture::create_from_image(broken), p_save_path + ".tex");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
// FIXME: Rasterization has issues, see https://github.com/godotengine/godot/issues/68350#issuecomment-1305610290
|
||||
static void _plot_triangle(Vector2i *p_vertices, const Vector2i &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) {
|
||||
int width = p_image->get_width();
|
||||
int height = p_image->get_height();
|
||||
int src_width = p_src_image->get_width();
|
||||
int src_height = p_src_image->get_height();
|
||||
|
||||
int x[3];
|
||||
int y[3];
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
x[j] = p_vertices[j].x;
|
||||
y[j] = p_vertices[j].y;
|
||||
}
|
||||
|
||||
// sort the points vertically
|
||||
if (y[1] > y[2]) {
|
||||
SWAP(x[1], x[2]);
|
||||
SWAP(y[1], y[2]);
|
||||
}
|
||||
if (y[0] > y[1]) {
|
||||
SWAP(x[0], x[1]);
|
||||
SWAP(y[0], y[1]);
|
||||
}
|
||||
if (y[1] > y[2]) {
|
||||
SWAP(x[1], x[2]);
|
||||
SWAP(y[1], y[2]);
|
||||
}
|
||||
|
||||
double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1);
|
||||
double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1);
|
||||
double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
|
||||
double xf = x[0];
|
||||
double xt = x[0] + dx_upper; // if y[0] == y[1], special case
|
||||
int max_y = MIN(y[2], p_transposed ? (width - p_offset.x - 1) : (height - p_offset.y - 1));
|
||||
for (int yi = y[0]; yi < max_y; yi++) {
|
||||
if (yi >= 0) {
|
||||
for (int xi = (xf > 0 ? int(xf) : 0); xi < (xt <= src_width ? xt : src_width); xi++) {
|
||||
int px = xi, py = yi;
|
||||
int sx = px, sy = py;
|
||||
sx = CLAMP(sx, 0, src_width - 1);
|
||||
sy = CLAMP(sy, 0, src_height - 1);
|
||||
Color color = p_src_image->get_pixel(sx, sy);
|
||||
if (p_transposed) {
|
||||
SWAP(px, py);
|
||||
}
|
||||
px += p_offset.x;
|
||||
py += p_offset.y;
|
||||
|
||||
//may have been cropped, so don't blit what is not visible?
|
||||
if (px < 0 || px >= width) {
|
||||
continue;
|
||||
}
|
||||
if (py < 0 || py >= height) {
|
||||
continue;
|
||||
}
|
||||
p_image->set_pixel(px, py, color);
|
||||
}
|
||||
|
||||
for (int xi = (xf < src_width ? int(xf) : src_width - 1); xi >= (xt > 0 ? xt : 0); xi--) {
|
||||
int px = xi, py = yi;
|
||||
int sx = px, sy = py;
|
||||
sx = CLAMP(sx, 0, src_width - 1);
|
||||
sy = CLAMP(sy, 0, src_height - 1);
|
||||
Color color = p_src_image->get_pixel(sx, sy);
|
||||
if (p_transposed) {
|
||||
SWAP(px, py);
|
||||
}
|
||||
px += p_offset.x;
|
||||
py += p_offset.y;
|
||||
|
||||
//may have been cropped, so don't blit what is not visible?
|
||||
if (px < 0 || px >= width) {
|
||||
continue;
|
||||
}
|
||||
if (py < 0 || py >= height) {
|
||||
continue;
|
||||
}
|
||||
p_image->set_pixel(px, py, color);
|
||||
}
|
||||
}
|
||||
xf += dx_far;
|
||||
if (yi < y[1]) {
|
||||
xt += dx_upper;
|
||||
} else {
|
||||
xt += dx_low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) {
|
||||
ERR_FAIL_COND_V(p_source_file_options.is_empty(), ERR_BUG); //should never happen
|
||||
|
||||
Vector<EditorAtlasPacker::Chart> charts;
|
||||
Vector<PackData> pack_data_files;
|
||||
|
||||
pack_data_files.resize(p_source_file_options.size());
|
||||
|
||||
int idx = 0;
|
||||
for (const KeyValue<String, HashMap<StringName, Variant>> &E : p_source_file_options) {
|
||||
PackData &pack_data = pack_data_files.write[idx];
|
||||
const String &source = E.key;
|
||||
const HashMap<StringName, Variant> &options = E.value;
|
||||
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
Error err = ImageLoader::load_image(source, image);
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
pack_data.image = image;
|
||||
|
||||
int mode = options["import_mode"];
|
||||
|
||||
if (mode == IMPORT_MODE_REGION) {
|
||||
pack_data.is_mesh = false;
|
||||
pack_data.is_cropped = options["crop_to_region"];
|
||||
|
||||
EditorAtlasPacker::Chart chart;
|
||||
|
||||
Rect2i used_rect = Rect2i(Vector2i(), image->get_size());
|
||||
if (options["trim_alpha_border_from_region"]) {
|
||||
// Clip a region from the image.
|
||||
used_rect = image->get_used_rect();
|
||||
}
|
||||
pack_data.region = used_rect;
|
||||
|
||||
chart.vertices.push_back(used_rect.position);
|
||||
chart.vertices.push_back(used_rect.position + Vector2i(used_rect.size.x, 0));
|
||||
chart.vertices.push_back(used_rect.position + Vector2i(used_rect.size.x, used_rect.size.y));
|
||||
chart.vertices.push_back(used_rect.position + Vector2i(0, used_rect.size.y));
|
||||
EditorAtlasPacker::Chart::Face f;
|
||||
f.vertex[0] = 0;
|
||||
f.vertex[1] = 1;
|
||||
f.vertex[2] = 2;
|
||||
chart.faces.push_back(f);
|
||||
f.vertex[0] = 0;
|
||||
f.vertex[1] = 2;
|
||||
f.vertex[2] = 3;
|
||||
chart.faces.push_back(f);
|
||||
chart.can_transpose = false;
|
||||
pack_data.chart_vertices.push_back(chart.vertices);
|
||||
pack_data.chart_pieces.push_back(charts.size());
|
||||
charts.push_back(chart);
|
||||
|
||||
} else {
|
||||
pack_data.is_mesh = true;
|
||||
|
||||
Ref<BitMap> bit_map;
|
||||
bit_map.instantiate();
|
||||
bit_map->create_from_image_alpha(image);
|
||||
Vector<Vector<Vector2>> polygons = bit_map->clip_opaque_to_polygons(Rect2(Vector2(), image->get_size()));
|
||||
|
||||
for (int j = 0; j < polygons.size(); j++) {
|
||||
EditorAtlasPacker::Chart chart;
|
||||
chart.vertices = polygons[j];
|
||||
chart.can_transpose = true;
|
||||
|
||||
Vector<int> poly = Geometry2D::triangulate_polygon(polygons[j]);
|
||||
for (int i = 0; i < poly.size(); i += 3) {
|
||||
EditorAtlasPacker::Chart::Face f;
|
||||
f.vertex[0] = poly[i + 0];
|
||||
f.vertex[1] = poly[i + 1];
|
||||
f.vertex[2] = poly[i + 2];
|
||||
chart.faces.push_back(f);
|
||||
}
|
||||
|
||||
pack_data.chart_pieces.push_back(charts.size());
|
||||
charts.push_back(chart);
|
||||
|
||||
pack_data.chart_vertices.push_back(polygons[j]);
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
const int max_width = (int)GLOBAL_GET("editor/import/atlas_max_width");
|
||||
|
||||
//pack the charts
|
||||
int atlas_width, atlas_height;
|
||||
EditorAtlasPacker::chart_pack(charts, atlas_width, atlas_height, max_width);
|
||||
|
||||
if (atlas_height > max_width * 2) {
|
||||
WARN_PRINT(vformat(TTR("%s: Atlas texture significantly larger on one axis (%d), consider changing the `editor/import/atlas_max_width` Project Setting to allow a wider texture, making the result more even in size."), p_group_file, atlas_height));
|
||||
}
|
||||
|
||||
//blit the atlas
|
||||
Ref<Image> new_atlas = Image::create_empty(atlas_width, atlas_height, false, Image::FORMAT_RGBA8);
|
||||
|
||||
for (int i = 0; i < pack_data_files.size(); i++) {
|
||||
PackData &pack_data = pack_data_files.write[i];
|
||||
|
||||
for (int j = 0; j < pack_data.chart_pieces.size(); j++) {
|
||||
const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[j]];
|
||||
for (int k = 0; k < chart.faces.size(); k++) {
|
||||
Vector2i positions[3];
|
||||
for (int l = 0; l < 3; l++) {
|
||||
int vertex_idx = chart.faces[k].vertex[l];
|
||||
positions[l] = Vector2i(chart.vertices[vertex_idx]);
|
||||
}
|
||||
|
||||
_plot_triangle(positions, Vector2i(chart.final_offset), chart.transposed, new_atlas, pack_data.image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//save the atlas
|
||||
|
||||
new_atlas->save_png(p_group_file);
|
||||
|
||||
//update cache if existing, else create
|
||||
Ref<Texture2D> cache;
|
||||
cache = ResourceCache::get_ref(p_group_file);
|
||||
if (!cache.is_valid()) {
|
||||
Ref<ImageTexture> res_cache = ImageTexture::create_from_image(new_atlas);
|
||||
res_cache->set_path(p_group_file);
|
||||
cache = res_cache;
|
||||
}
|
||||
|
||||
//save the images
|
||||
idx = 0;
|
||||
for (const KeyValue<String, HashMap<StringName, Variant>> &E : p_source_file_options) {
|
||||
PackData &pack_data = pack_data_files.write[idx];
|
||||
|
||||
Ref<Texture2D> texture;
|
||||
|
||||
if (!pack_data.is_mesh) {
|
||||
Vector2 offset = charts[pack_data.chart_pieces[0]].vertices[0] + charts[pack_data.chart_pieces[0]].final_offset;
|
||||
|
||||
//region
|
||||
Ref<AtlasTexture> atlas_texture;
|
||||
atlas_texture.instantiate();
|
||||
atlas_texture->set_atlas(cache);
|
||||
atlas_texture->set_region(Rect2(offset, pack_data.region.size));
|
||||
|
||||
if (!pack_data.is_cropped) {
|
||||
atlas_texture->set_margin(Rect2(pack_data.region.position, pack_data.image->get_size() - pack_data.region.size));
|
||||
}
|
||||
|
||||
texture = atlas_texture;
|
||||
} else {
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instantiate();
|
||||
|
||||
for (int i = 0; i < pack_data.chart_pieces.size(); i++) {
|
||||
const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[i]];
|
||||
Vector<Vector2> vertices;
|
||||
Vector<int> indices;
|
||||
Vector<Vector2> uvs;
|
||||
int vc = chart.vertices.size();
|
||||
int fc = chart.faces.size();
|
||||
vertices.resize(vc);
|
||||
uvs.resize(vc);
|
||||
indices.resize(fc * 3);
|
||||
|
||||
{
|
||||
Vector2 *vw = vertices.ptrw();
|
||||
int *iw = indices.ptrw();
|
||||
Vector2 *uvw = uvs.ptrw();
|
||||
|
||||
for (int j = 0; j < vc; j++) {
|
||||
vw[j] = chart.vertices[j];
|
||||
Vector2 uv = chart.vertices[j];
|
||||
if (chart.transposed) {
|
||||
SWAP(uv.x, uv.y);
|
||||
}
|
||||
uv += chart.final_offset;
|
||||
uv /= new_atlas->get_size(); //normalize uv to 0-1 range
|
||||
uvw[j] = uv;
|
||||
}
|
||||
|
||||
for (int j = 0; j < fc; j++) {
|
||||
iw[j * 3 + 0] = chart.faces[j].vertex[0];
|
||||
iw[j * 3 + 1] = chart.faces[j].vertex[1];
|
||||
iw[j * 3 + 2] = chart.faces[j].vertex[2];
|
||||
}
|
||||
}
|
||||
|
||||
Array arrays;
|
||||
arrays.resize(Mesh::ARRAY_MAX);
|
||||
arrays[Mesh::ARRAY_VERTEX] = vertices;
|
||||
arrays[Mesh::ARRAY_TEX_UV] = uvs;
|
||||
arrays[Mesh::ARRAY_INDEX] = indices;
|
||||
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
|
||||
}
|
||||
|
||||
Ref<MeshTexture> mesh_texture;
|
||||
mesh_texture.instantiate();
|
||||
mesh_texture->set_base_texture(cache);
|
||||
mesh_texture->set_image_size(pack_data.image->get_size());
|
||||
mesh_texture->set_mesh(mesh);
|
||||
|
||||
texture = mesh_texture;
|
||||
}
|
||||
|
||||
String save_path = p_base_paths[E.key] + ".res";
|
||||
ResourceSaver::save(texture, save_path);
|
||||
idx++;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterTextureAtlas::ResourceImporterTextureAtlas() {
|
||||
}
|
||||
73
engine/editor/import/resource_importer_texture_atlas.h
Normal file
73
engine/editor/import/resource_importer_texture_atlas.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture_atlas.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_TEXTURE_ATLAS_H
|
||||
#define RESOURCE_IMPORTER_TEXTURE_ATLAS_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
class ResourceImporterTextureAtlas : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterTextureAtlas, ResourceImporter);
|
||||
|
||||
struct PackData {
|
||||
Rect2 region;
|
||||
bool is_cropped = false;
|
||||
bool is_mesh = false;
|
||||
Vector<int> chart_pieces; //one for region, many for mesh
|
||||
Vector<Vector<Vector2>> chart_vertices; //for mesh
|
||||
Ref<Image> image;
|
||||
};
|
||||
|
||||
public:
|
||||
enum ImportMode {
|
||||
IMPORT_MODE_REGION,
|
||||
IMPORT_MODE_2D_MESH
|
||||
};
|
||||
|
||||
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_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 String get_option_group_file() 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;
|
||||
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) override;
|
||||
|
||||
ResourceImporterTextureAtlas();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_TEXTURE_ATLAS_H
|
||||
54
engine/editor/import/resource_importer_texture_settings.cpp
Normal file
54
engine/editor/import/resource_importer_texture_settings.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture_settings.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_texture_settings.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
// ResourceImporterTextureSettings contains code used by
|
||||
// multiple texture importers and the export dialog.
|
||||
bool ResourceImporterTextureSettings::should_import_s3tc_bptc() {
|
||||
if (GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc")) {
|
||||
return true;
|
||||
}
|
||||
// If the project settings override is not enabled, import
|
||||
// S3TC/BPTC only when the host operating system needs it.
|
||||
return OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC;
|
||||
}
|
||||
|
||||
bool ResourceImporterTextureSettings::should_import_etc2_astc() {
|
||||
if (GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc")) {
|
||||
return true;
|
||||
}
|
||||
// If the project settings override is not enabled, import
|
||||
// ETC2/ASTC only when the host operating system needs it.
|
||||
return OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC;
|
||||
}
|
||||
41
engine/editor/import/resource_importer_texture_settings.h
Normal file
41
engine/editor/import/resource_importer_texture_settings.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_texture_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 RESOURCE_IMPORTER_TEXTURE_SETTINGS_H
|
||||
#define RESOURCE_IMPORTER_TEXTURE_SETTINGS_H
|
||||
|
||||
// ResourceImporterTextureSettings contains code used by
|
||||
// multiple texture importers and the export dialog.
|
||||
namespace ResourceImporterTextureSettings {
|
||||
bool should_import_s3tc_bptc();
|
||||
bool should_import_etc2_astc();
|
||||
} //namespace ResourceImporterTextureSettings
|
||||
|
||||
#endif // RESOURCE_IMPORTER_TEXTURE_SETTINGS_H
|
||||
550
engine/editor/import/resource_importer_wav.cpp
Normal file
550
engine/editor/import/resource_importer_wav.cpp
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_wav.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_wav.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/resources/audio_stream_wav.h"
|
||||
|
||||
const float TRIM_DB_LIMIT = -50;
|
||||
const int TRIM_FADE_OUT_FRAMES = 500;
|
||||
|
||||
String ResourceImporterWAV::get_importer_name() const {
|
||||
return "wav";
|
||||
}
|
||||
|
||||
String ResourceImporterWAV::get_visible_name() const {
|
||||
return "Microsoft WAV";
|
||||
}
|
||||
|
||||
void ResourceImporterWAV::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
p_extensions->push_back("wav");
|
||||
}
|
||||
|
||||
String ResourceImporterWAV::get_save_extension() const {
|
||||
return "sample";
|
||||
}
|
||||
|
||||
String ResourceImporterWAV::get_resource_type() const {
|
||||
return "AudioStreamWAV";
|
||||
}
|
||||
|
||||
bool ResourceImporterWAV::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "force/max_rate_hz" && !bool(p_options["force/max_rate"])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't show begin/end loop points if loop mode is auto-detected or disabled.
|
||||
if ((int)p_options["edit/loop_mode"] < 2 && (p_option == "edit/loop_begin" || p_option == "edit/loop_end")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ResourceImporterWAV::get_preset_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String ResourceImporterWAV::get_preset_name(int p_idx) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/8_bit"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/mono"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/max_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "force/max_rate_hz", PROPERTY_HINT_RANGE, "11025,192000,1,exp"), 44100));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/trim"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/normalize"), false));
|
||||
// Keep the `edit/loop_mode` enum in sync with AudioStreamWAV::LoopMode (note: +1 offset due to "Detect From WAV").
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_mode", PROPERTY_HINT_ENUM, "Detect From WAV,Disabled,Forward,Ping-Pong,Backward", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_begin"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_end"), -1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Disabled,RAM (Ima-ADPCM),QOA (Quite OK Audio)"), 0));
|
||||
}
|
||||
|
||||
Error ResourceImporterWAV::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) {
|
||||
/* STEP 1, READ WAVE FILE */
|
||||
|
||||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
|
||||
|
||||
/* CHECK RIFF */
|
||||
char riff[5];
|
||||
riff[4] = 0;
|
||||
file->get_buffer((uint8_t *)&riff, 4); //RIFF
|
||||
|
||||
if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes", riff, file->get_length()));
|
||||
}
|
||||
|
||||
/* GET FILESIZE */
|
||||
file->get_32(); // filesize
|
||||
|
||||
/* CHECK WAVE */
|
||||
|
||||
char wave[5];
|
||||
wave[4] = 0;
|
||||
file->get_buffer((uint8_t *)&wave, 4); //WAVE
|
||||
|
||||
if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes", wave, file->get_length()));
|
||||
}
|
||||
|
||||
// Let users override potential loop points from the WAV.
|
||||
// We parse the WAV loop points only with "Detect From WAV" (0).
|
||||
int import_loop_mode = p_options["edit/loop_mode"];
|
||||
|
||||
int format_bits = 0;
|
||||
int format_channels = 0;
|
||||
|
||||
AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED;
|
||||
uint16_t compression_code = 1;
|
||||
bool format_found = false;
|
||||
bool data_found = false;
|
||||
int format_freq = 0;
|
||||
int loop_begin = 0;
|
||||
int loop_end = 0;
|
||||
int frames = 0;
|
||||
|
||||
Vector<float> data;
|
||||
|
||||
while (!file->eof_reached()) {
|
||||
/* chunk */
|
||||
char chunkID[4];
|
||||
file->get_buffer((uint8_t *)&chunkID, 4); //RIFF
|
||||
|
||||
/* chunk size */
|
||||
uint32_t chunksize = file->get_32();
|
||||
uint32_t file_pos = file->get_position(); //save file pos, so we can skip to next chunk safely
|
||||
|
||||
if (file->eof_reached()) {
|
||||
//ERR_PRINT("EOF REACH");
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkID[0] == 'f' && chunkID[1] == 'm' && chunkID[2] == 't' && chunkID[3] == ' ' && !format_found) {
|
||||
/* IS FORMAT CHUNK */
|
||||
|
||||
//Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version.
|
||||
//Consider revision for engine version 3.0
|
||||
compression_code = file->get_16();
|
||||
if (compression_code != 1 && compression_code != 3) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead.");
|
||||
}
|
||||
|
||||
format_channels = file->get_16();
|
||||
if (format_channels != 1 && format_channels != 2) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not stereo or mono).");
|
||||
}
|
||||
|
||||
format_freq = file->get_32(); //sampling rate
|
||||
|
||||
file->get_32(); // average bits/second (unused)
|
||||
file->get_16(); // block align (unused)
|
||||
format_bits = file->get_16(); // bits per sample
|
||||
|
||||
if (format_bits % 8 || format_bits == 0) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32).");
|
||||
}
|
||||
|
||||
if (compression_code == 3 && format_bits % 32) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid amount of bits in the IEEE float sample (should be 32 or 64).");
|
||||
}
|
||||
|
||||
/* Don't need anything else, continue */
|
||||
format_found = true;
|
||||
}
|
||||
|
||||
if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) {
|
||||
/* IS DATA CHUNK */
|
||||
data_found = true;
|
||||
|
||||
if (!format_found) {
|
||||
ERR_PRINT("'data' chunk before 'format' chunk found.");
|
||||
break;
|
||||
}
|
||||
|
||||
frames = chunksize;
|
||||
|
||||
if (format_channels == 0) {
|
||||
ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA);
|
||||
}
|
||||
frames /= format_channels;
|
||||
frames /= (format_bits >> 3);
|
||||
|
||||
/*print_line("chunksize: "+itos(chunksize));
|
||||
print_line("channels: "+itos(format_channels));
|
||||
print_line("bits: "+itos(format_bits));
|
||||
*/
|
||||
|
||||
data.resize(frames * format_channels);
|
||||
|
||||
if (compression_code == 1) {
|
||||
if (format_bits == 8) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
// 8 bit samples are UNSIGNED
|
||||
|
||||
data.write[i] = int8_t(file->get_8() - 128) / 128.f;
|
||||
}
|
||||
} else if (format_bits == 16) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//16 bit SIGNED
|
||||
|
||||
data.write[i] = int16_t(file->get_16()) / 32768.f;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//16+ bits samples are SIGNED
|
||||
// if sample is > 16 bits, just read extra bytes
|
||||
|
||||
uint32_t s = 0;
|
||||
for (int b = 0; b < (format_bits >> 3); b++) {
|
||||
s |= ((uint32_t)file->get_8()) << (b * 8);
|
||||
}
|
||||
s <<= (32 - format_bits);
|
||||
|
||||
data.write[i] = (int32_t(s) >> 16) / 32768.f;
|
||||
}
|
||||
}
|
||||
} else if (compression_code == 3) {
|
||||
if (format_bits == 32) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//32 bit IEEE Float
|
||||
|
||||
data.write[i] = file->get_float();
|
||||
}
|
||||
} else if (format_bits == 64) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//64 bit IEEE Float
|
||||
|
||||
data.write[i] = file->get_double();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file->eof_reached()) {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Premature end of file.");
|
||||
}
|
||||
}
|
||||
|
||||
if (import_loop_mode == 0 && chunkID[0] == 's' && chunkID[1] == 'm' && chunkID[2] == 'p' && chunkID[3] == 'l') {
|
||||
// Loop point info!
|
||||
|
||||
/**
|
||||
* Consider exploring next document:
|
||||
* http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf
|
||||
* Especially on page:
|
||||
* 16 - 17
|
||||
* Timestamp:
|
||||
* 22:38 06.07.2017 GMT
|
||||
**/
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
file->get_32(); // i wish to know why should i do this... no doc!
|
||||
}
|
||||
|
||||
// only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward)
|
||||
// Skip anything else because it's not supported, reserved for future uses or sampler specific
|
||||
// from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table)
|
||||
int loop_type = file->get_32();
|
||||
if (loop_type == 0x00 || loop_type == 0x01 || loop_type == 0x02) {
|
||||
if (loop_type == 0x00) {
|
||||
loop_mode = AudioStreamWAV::LOOP_FORWARD;
|
||||
} else if (loop_type == 0x01) {
|
||||
loop_mode = AudioStreamWAV::LOOP_PINGPONG;
|
||||
} else if (loop_type == 0x02) {
|
||||
loop_mode = AudioStreamWAV::LOOP_BACKWARD;
|
||||
}
|
||||
loop_begin = file->get_32();
|
||||
loop_end = file->get_32();
|
||||
}
|
||||
}
|
||||
// Move to the start of the next chunk. Note that RIFF requires a padding byte for odd
|
||||
// chunk sizes.
|
||||
file->seek(file_pos + chunksize + (chunksize & 1));
|
||||
}
|
||||
|
||||
// STEP 2, APPLY CONVERSIONS
|
||||
|
||||
bool is16 = format_bits != 8;
|
||||
int rate = format_freq;
|
||||
|
||||
/*
|
||||
print_line("Input Sample: ");
|
||||
print_line("\tframes: " + itos(frames));
|
||||
print_line("\tformat_channels: " + itos(format_channels));
|
||||
print_line("\t16bits: " + itos(is16));
|
||||
print_line("\trate: " + itos(rate));
|
||||
print_line("\tloop: " + itos(loop));
|
||||
print_line("\tloop begin: " + itos(loop_begin));
|
||||
print_line("\tloop end: " + itos(loop_end));
|
||||
*/
|
||||
|
||||
//apply frequency limit
|
||||
|
||||
bool limit_rate = p_options["force/max_rate"];
|
||||
int limit_rate_hz = p_options["force/max_rate_hz"];
|
||||
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
|
||||
// resample!
|
||||
int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
|
||||
|
||||
Vector<float> new_data;
|
||||
new_data.resize(new_data_frames * format_channels);
|
||||
for (int c = 0; c < format_channels; c++) {
|
||||
float frac = .0f;
|
||||
int ipos = 0;
|
||||
|
||||
for (int i = 0; i < new_data_frames; i++) {
|
||||
// Cubic interpolation should be enough.
|
||||
|
||||
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
|
||||
float y1 = data[ipos * format_channels + c];
|
||||
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
|
||||
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
|
||||
|
||||
new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, frac);
|
||||
|
||||
// update position and always keep fractional part within ]0...1]
|
||||
// in order to avoid 32bit floating point precision errors
|
||||
|
||||
frac += (float)rate / (float)limit_rate_hz;
|
||||
int tpos = (int)Math::floor(frac);
|
||||
ipos += tpos;
|
||||
frac -= tpos;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_mode) {
|
||||
loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
|
||||
loop_end = (int)(loop_end * (float)new_data_frames / (float)frames);
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
rate = limit_rate_hz;
|
||||
frames = new_data_frames;
|
||||
}
|
||||
|
||||
bool normalize = p_options["edit/normalize"];
|
||||
|
||||
if (normalize) {
|
||||
float max = 0;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
float amp = Math::abs(data[i]);
|
||||
if (amp > max) {
|
||||
max = amp;
|
||||
}
|
||||
}
|
||||
|
||||
if (max > 0) {
|
||||
float mult = 1.0 / max;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data.write[i] *= mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool trim = p_options["edit/trim"];
|
||||
|
||||
if (trim && (loop_mode == AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) {
|
||||
int first = 0;
|
||||
int last = (frames / format_channels) - 1;
|
||||
bool found = false;
|
||||
float limit = Math::db_to_linear(TRIM_DB_LIMIT);
|
||||
|
||||
for (int i = 0; i < data.size() / format_channels; i++) {
|
||||
float ampChannelSum = 0;
|
||||
for (int j = 0; j < format_channels; j++) {
|
||||
ampChannelSum += Math::abs(data[(i * format_channels) + j]);
|
||||
}
|
||||
|
||||
float amp = Math::abs(ampChannelSum / (float)format_channels);
|
||||
|
||||
if (!found && amp > limit) {
|
||||
first = i;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found && amp > limit) {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first < last) {
|
||||
Vector<float> new_data;
|
||||
new_data.resize((last - first) * format_channels);
|
||||
for (int i = first; i < last; i++) {
|
||||
float fadeOutMult = 1;
|
||||
|
||||
if (last - i < TRIM_FADE_OUT_FRAMES) {
|
||||
fadeOutMult = ((float)(last - i - 1) / (float)TRIM_FADE_OUT_FRAMES);
|
||||
}
|
||||
|
||||
for (int j = 0; j < format_channels; j++) {
|
||||
new_data.write[((i - first) * format_channels) + j] = data[(i * format_channels) + j] * fadeOutMult;
|
||||
}
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
frames = data.size() / format_channels;
|
||||
}
|
||||
}
|
||||
|
||||
if (import_loop_mode >= 2) {
|
||||
loop_mode = (AudioStreamWAV::LoopMode)(import_loop_mode - 1);
|
||||
loop_begin = p_options["edit/loop_begin"];
|
||||
loop_end = p_options["edit/loop_end"];
|
||||
// Wrap around to max frames, so `-1` can be used to select the end, etc.
|
||||
if (loop_begin < 0) {
|
||||
loop_begin = CLAMP(loop_begin + frames + 1, 0, frames);
|
||||
}
|
||||
if (loop_end < 0) {
|
||||
loop_end = CLAMP(loop_end + frames + 1, 0, frames);
|
||||
}
|
||||
}
|
||||
|
||||
int compression = p_options["compress/mode"];
|
||||
bool force_mono = p_options["force/mono"];
|
||||
|
||||
if (force_mono && format_channels == 2) {
|
||||
Vector<float> new_data;
|
||||
new_data.resize(data.size() / 2);
|
||||
for (int i = 0; i < frames; i++) {
|
||||
new_data.write[i] = (data[i * 2 + 0] + data[i * 2 + 1]) / 2.0;
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
format_channels = 1;
|
||||
}
|
||||
|
||||
bool force_8_bit = p_options["force/8_bit"];
|
||||
if (force_8_bit) {
|
||||
is16 = false;
|
||||
}
|
||||
|
||||
Vector<uint8_t> pcm_data;
|
||||
AudioStreamWAV::Format dst_format;
|
||||
|
||||
if (compression == 1) {
|
||||
dst_format = AudioStreamWAV::FORMAT_IMA_ADPCM;
|
||||
if (format_channels == 1) {
|
||||
_compress_ima_adpcm(data, pcm_data);
|
||||
} else {
|
||||
//byte interleave
|
||||
Vector<float> left;
|
||||
Vector<float> right;
|
||||
|
||||
int tframes = data.size() / 2;
|
||||
left.resize(tframes);
|
||||
right.resize(tframes);
|
||||
|
||||
for (int i = 0; i < tframes; i++) {
|
||||
left.write[i] = data[i * 2 + 0];
|
||||
right.write[i] = data[i * 2 + 1];
|
||||
}
|
||||
|
||||
Vector<uint8_t> bleft;
|
||||
Vector<uint8_t> bright;
|
||||
|
||||
_compress_ima_adpcm(left, bleft);
|
||||
_compress_ima_adpcm(right, bright);
|
||||
|
||||
int dl = bleft.size();
|
||||
pcm_data.resize(dl * 2);
|
||||
|
||||
uint8_t *w = pcm_data.ptrw();
|
||||
const uint8_t *rl = bleft.ptr();
|
||||
const uint8_t *rr = bright.ptr();
|
||||
|
||||
for (int i = 0; i < dl; i++) {
|
||||
w[i * 2 + 0] = rl[i];
|
||||
w[i * 2 + 1] = rr[i];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dst_format = is16 ? AudioStreamWAV::FORMAT_16_BITS : AudioStreamWAV::FORMAT_8_BITS;
|
||||
bool enforce16 = is16 || compression == 2;
|
||||
pcm_data.resize(data.size() * (enforce16 ? 2 : 1));
|
||||
{
|
||||
uint8_t *w = pcm_data.ptrw();
|
||||
|
||||
int ds = data.size();
|
||||
for (int i = 0; i < ds; i++) {
|
||||
if (enforce16) {
|
||||
int16_t v = CLAMP(data[i] * 32768, -32768, 32767);
|
||||
encode_uint16(v, &w[i * 2]);
|
||||
} else {
|
||||
int8_t v = CLAMP(data[i] * 128, -128, 127);
|
||||
w[i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> dst_data;
|
||||
if (compression == 2) {
|
||||
dst_format = AudioStreamWAV::FORMAT_QOA;
|
||||
qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
|
||||
uint32_t qoa_len = 0;
|
||||
|
||||
desc.samplerate = rate;
|
||||
desc.samples = frames;
|
||||
desc.channels = format_channels;
|
||||
|
||||
void *encoded = qoa_encode((short *)pcm_data.ptrw(), &desc, &qoa_len);
|
||||
dst_data.resize(qoa_len);
|
||||
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
||||
} else {
|
||||
dst_data = pcm_data;
|
||||
}
|
||||
|
||||
Ref<AudioStreamWAV> sample;
|
||||
sample.instantiate();
|
||||
sample->set_data(dst_data);
|
||||
sample->set_format(dst_format);
|
||||
sample->set_mix_rate(rate);
|
||||
sample->set_loop_mode(loop_mode);
|
||||
sample->set_loop_begin(loop_begin);
|
||||
sample->set_loop_end(loop_end);
|
||||
sample->set_stereo(format_channels == 2);
|
||||
|
||||
ResourceSaver::save(sample, p_save_path + ".sample");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ResourceImporterWAV::ResourceImporterWAV() {
|
||||
}
|
||||
148
engine/editor/import/resource_importer_wav.h
Normal file
148
engine/editor/import/resource_importer_wav.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**************************************************************************/
|
||||
/* resource_importer_wav.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_WAV_H
|
||||
#define RESOURCE_IMPORTER_WAV_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
class ResourceImporterWAV : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterWAV, 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_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;
|
||||
|
||||
static void _compress_ima_adpcm(const Vector<float> &p_data, Vector<uint8_t> &dst_data) {
|
||||
static const int16_t _ima_adpcm_step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
static const int8_t _ima_adpcm_index_table[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
int datalen = p_data.size();
|
||||
int datamax = datalen;
|
||||
if (datalen & 1) {
|
||||
datalen++;
|
||||
}
|
||||
|
||||
dst_data.resize(datalen / 2 + 4);
|
||||
uint8_t *w = dst_data.ptrw();
|
||||
|
||||
int i, step_idx = 0, prev = 0;
|
||||
uint8_t *out = w;
|
||||
const float *in = p_data.ptr();
|
||||
|
||||
// Initial value is zero.
|
||||
*(out++) = 0;
|
||||
*(out++) = 0;
|
||||
// Table index initial value.
|
||||
*(out++) = 0;
|
||||
// Unused.
|
||||
*(out++) = 0;
|
||||
|
||||
for (i = 0; i < datalen; i++) {
|
||||
int step, diff, vpdiff, mask;
|
||||
uint8_t nibble;
|
||||
int16_t xm_sample;
|
||||
|
||||
if (i >= datamax) {
|
||||
xm_sample = 0;
|
||||
} else {
|
||||
xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767);
|
||||
}
|
||||
|
||||
diff = (int)xm_sample - prev;
|
||||
|
||||
nibble = 0;
|
||||
step = _ima_adpcm_step_table[step_idx];
|
||||
vpdiff = step >> 3;
|
||||
if (diff < 0) {
|
||||
nibble = 8;
|
||||
diff = -diff;
|
||||
}
|
||||
mask = 4;
|
||||
while (mask) {
|
||||
if (diff >= step) {
|
||||
nibble |= mask;
|
||||
diff -= step;
|
||||
vpdiff += step;
|
||||
}
|
||||
|
||||
step >>= 1;
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
if (nibble & 8) {
|
||||
prev -= vpdiff;
|
||||
} else {
|
||||
prev += vpdiff;
|
||||
}
|
||||
|
||||
prev = CLAMP(prev, -32768, 32767);
|
||||
|
||||
step_idx += _ima_adpcm_index_table[nibble];
|
||||
step_idx = CLAMP(step_idx, 0, 88);
|
||||
|
||||
if (i & 1) {
|
||||
*out |= nibble << 4;
|
||||
out++;
|
||||
} else {
|
||||
*out = nibble;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ResourceImporterWAV();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_IMPORTER_WAV_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue