editor_mesh_import_plugin.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*************************************************************************/
  2. /* editor_mesh_import_plugin.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "editor_mesh_import_plugin.h"
  31. #include "editor/editor_dir_dialog.h"
  32. #include "editor/editor_file_dialog.h"
  33. #include "editor/editor_node.h"
  34. #include "editor/property_editor.h"
  35. #include "io/marshalls.h"
  36. #include "io/resource_saver.h"
  37. #include "os/file_access.h"
  38. #include "scene/resources/sample.h"
  39. #include "scene/resources/surface_tool.h"
  40. class _EditorMeshImportOptions : public Object {
  41. OBJ_TYPE(_EditorMeshImportOptions, Object);
  42. public:
  43. bool generate_tangents;
  44. bool generate_normals;
  45. bool flip_faces;
  46. bool smooth_shading;
  47. bool weld_vertices;
  48. bool import_material;
  49. bool import_textures;
  50. float weld_tolerance;
  51. bool _set(const StringName &p_name, const Variant &p_value) {
  52. String n = p_name;
  53. if (n == "generate/tangents")
  54. generate_tangents = p_value;
  55. else if (n == "generate/normals")
  56. generate_normals = p_value;
  57. else if (n == "import/materials")
  58. import_material = p_value;
  59. else if (n == "import/textures")
  60. import_textures = p_value;
  61. else if (n == "force/flip_faces")
  62. flip_faces = p_value;
  63. else if (n == "force/smooth_shading")
  64. smooth_shading = p_value;
  65. else if (n == "force/weld_vertices")
  66. weld_vertices = p_value;
  67. else if (n == "force/weld_tolerance")
  68. weld_tolerance = p_value;
  69. else
  70. return false;
  71. return true;
  72. }
  73. bool _get(const StringName &p_name, Variant &r_ret) const {
  74. String n = p_name;
  75. if (n == "generate/tangents")
  76. r_ret = generate_tangents;
  77. else if (n == "generate/normals")
  78. r_ret = generate_normals;
  79. else if (n == "import/materials")
  80. r_ret = import_material;
  81. else if (n == "import/textures")
  82. r_ret = import_textures;
  83. else if (n == "force/flip_faces")
  84. r_ret = flip_faces;
  85. else if (n == "force/smooth_shading")
  86. r_ret = smooth_shading;
  87. else if (n == "force/weld_vertices")
  88. r_ret = weld_vertices;
  89. else if (n == "force/weld_tolerance")
  90. r_ret = weld_tolerance;
  91. else
  92. return false;
  93. return true;
  94. }
  95. void _get_property_list(List<PropertyInfo> *p_list) const {
  96. p_list->push_back(PropertyInfo(Variant::BOOL, "generate/tangents"));
  97. p_list->push_back(PropertyInfo(Variant::BOOL, "generate/normals"));
  98. //not for nowp
  99. //p_list->push_back(PropertyInfo(Variant::BOOL,"import/materials"));
  100. //p_list->push_back(PropertyInfo(Variant::BOOL,"import/textures"));
  101. p_list->push_back(PropertyInfo(Variant::BOOL, "force/flip_faces"));
  102. p_list->push_back(PropertyInfo(Variant::BOOL, "force/smooth_shading"));
  103. p_list->push_back(PropertyInfo(Variant::BOOL, "force/weld_vertices"));
  104. p_list->push_back(PropertyInfo(Variant::REAL, "force/weld_tolerance", PROPERTY_HINT_RANGE, "0.00001,16,0.00001"));
  105. //p_list->push_back(PropertyInfo(Variant::BOOL,"compress/enable"));
  106. //p_list->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192"));
  107. }
  108. static void _bind_methods() {
  109. ADD_SIGNAL(MethodInfo("changed"));
  110. }
  111. _EditorMeshImportOptions() {
  112. generate_tangents = true;
  113. generate_normals = false;
  114. flip_faces = false;
  115. smooth_shading = false;
  116. weld_vertices = true;
  117. weld_tolerance = 0.0001;
  118. import_material = false;
  119. import_textures = false;
  120. }
  121. };
  122. class EditorMeshImportDialog : public ConfirmationDialog {
  123. OBJ_TYPE(EditorMeshImportDialog, ConfirmationDialog);
  124. EditorMeshImportPlugin *plugin;
  125. LineEdit *import_path;
  126. LineEdit *save_path;
  127. EditorFileDialog *file_select;
  128. EditorDirDialog *save_select;
  129. AcceptDialog *error_dialog;
  130. PropertyEditor *option_editor;
  131. _EditorMeshImportOptions *options;
  132. public:
  133. void _choose_files(const Vector<String> &p_path) {
  134. String files;
  135. for (int i = 0; i < p_path.size(); i++) {
  136. if (i > 0)
  137. files += ",";
  138. files += p_path[i];
  139. }
  140. /*
  141. if (p_path.size()) {
  142. String srctex=p_path[0];
  143. String ipath = EditorImportDB::get_singleton()->find_source_path(srctex);
  144. if (ipath!="")
  145. save_path->set_text(ipath.get_base_dir());
  146. }*/
  147. import_path->set_text(files);
  148. }
  149. void _choose_save_dir(const String &p_path) {
  150. save_path->set_text(p_path);
  151. }
  152. void _browse() {
  153. file_select->popup_centered_ratio();
  154. }
  155. void _browse_target() {
  156. save_select->popup_centered_ratio();
  157. }
  158. void popup_import(const String &p_path) {
  159. popup_centered(Size2(400, 400) * EDSCALE);
  160. if (p_path != "") {
  161. Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path);
  162. ERR_FAIL_COND(!rimd.is_valid());
  163. save_path->set_text(p_path.get_base_dir());
  164. List<String> opts;
  165. rimd->get_options(&opts);
  166. for (List<String>::Element *E = opts.front(); E; E = E->next()) {
  167. options->_set(E->get(), rimd->get_option(E->get()));
  168. }
  169. String src = "";
  170. for (int i = 0; i < rimd->get_source_count(); i++) {
  171. if (i > 0)
  172. src += ",";
  173. src += EditorImportPlugin::expand_source_path(rimd->get_source_path(i));
  174. }
  175. import_path->set_text(src);
  176. }
  177. }
  178. void _import() {
  179. Vector<String> meshes = import_path->get_text().split(",");
  180. if (meshes.size() == 0) {
  181. error_dialog->set_text(TTR("No meshes to import!"));
  182. error_dialog->popup_centered_minsize();
  183. return;
  184. }
  185. String dst = save_path->get_text();
  186. if (dst == "") {
  187. error_dialog->set_text(TTR("Save path is empty!"));
  188. error_dialog->popup_centered_minsize();
  189. return;
  190. }
  191. for (int i = 0; i < meshes.size(); i++) {
  192. Ref<ResourceImportMetadata> imd = memnew(ResourceImportMetadata);
  193. List<PropertyInfo> pl;
  194. options->_get_property_list(&pl);
  195. for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
  196. Variant v;
  197. String opt = E->get().name;
  198. options->_get(opt, v);
  199. imd->set_option(opt, v);
  200. }
  201. imd->add_source(EditorImportPlugin::validate_source_path(meshes[i]));
  202. String file_path = dst.plus_file(meshes[i].get_file().basename() + ".msh");
  203. plugin->import(file_path, imd);
  204. }
  205. hide();
  206. }
  207. void _notification(int p_what) {
  208. if (p_what == NOTIFICATION_ENTER_TREE) {
  209. option_editor->edit(options);
  210. }
  211. }
  212. static void _bind_methods() {
  213. ObjectTypeDB::bind_method("_choose_files", &EditorMeshImportDialog::_choose_files);
  214. ObjectTypeDB::bind_method("_choose_save_dir", &EditorMeshImportDialog::_choose_save_dir);
  215. ObjectTypeDB::bind_method("_import", &EditorMeshImportDialog::_import);
  216. ObjectTypeDB::bind_method("_browse", &EditorMeshImportDialog::_browse);
  217. ObjectTypeDB::bind_method("_browse_target", &EditorMeshImportDialog::_browse_target);
  218. }
  219. EditorMeshImportDialog(EditorMeshImportPlugin *p_plugin) {
  220. plugin = p_plugin;
  221. set_title(TTR("Single Mesh Import"));
  222. set_hide_on_ok(false);
  223. VBoxContainer *vbc = memnew(VBoxContainer);
  224. add_child(vbc);
  225. set_child_rect(vbc);
  226. HBoxContainer *hbc = memnew(HBoxContainer);
  227. vbc->add_margin_child(TTR("Source Mesh(es):"), hbc);
  228. import_path = memnew(LineEdit);
  229. import_path->set_h_size_flags(SIZE_EXPAND_FILL);
  230. hbc->add_child(import_path);
  231. Button *import_choose = memnew(Button);
  232. import_choose->set_text(" .. ");
  233. hbc->add_child(import_choose);
  234. import_choose->connect("pressed", this, "_browse");
  235. hbc = memnew(HBoxContainer);
  236. vbc->add_margin_child(TTR("Target Path:"), hbc);
  237. save_path = memnew(LineEdit);
  238. save_path->set_h_size_flags(SIZE_EXPAND_FILL);
  239. hbc->add_child(save_path);
  240. Button *save_choose = memnew(Button);
  241. save_choose->set_text(" .. ");
  242. hbc->add_child(save_choose);
  243. save_choose->connect("pressed", this, "_browse_target");
  244. file_select = memnew(EditorFileDialog);
  245. file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  246. file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES);
  247. file_select->add_filter("*.obj ; Wavefront OBJ");
  248. add_child(file_select);
  249. file_select->connect("files_selected", this, "_choose_files");
  250. save_select = memnew(EditorDirDialog);
  251. add_child(save_select);
  252. save_select->connect("dir_selected", this, "_choose_save_dir");
  253. get_ok()->connect("pressed", this, "_import");
  254. get_ok()->set_text(TTR("Import"));
  255. error_dialog = memnew(AcceptDialog);
  256. add_child(error_dialog);
  257. options = memnew(_EditorMeshImportOptions);
  258. option_editor = memnew(PropertyEditor);
  259. option_editor->hide_top_label();
  260. vbc->add_margin_child(TTR("Options:"), option_editor, true);
  261. }
  262. ~EditorMeshImportDialog() {
  263. memdelete(options);
  264. }
  265. };
  266. String EditorMeshImportPlugin::get_name() const {
  267. return "mesh";
  268. }
  269. String EditorMeshImportPlugin::get_visible_name() const {
  270. return TTR("Mesh");
  271. }
  272. void EditorMeshImportPlugin::import_dialog(const String &p_from) {
  273. dialog->popup_import(p_from);
  274. }
  275. Error EditorMeshImportPlugin::import(const String &p_path, const Ref<ResourceImportMetadata> &p_from) {
  276. ERR_FAIL_COND_V(p_from->get_source_count() != 1, ERR_INVALID_PARAMETER);
  277. Ref<ResourceImportMetadata> from = p_from;
  278. String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0));
  279. FileAccessRef f = FileAccess::open(src_path, FileAccess::READ);
  280. ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
  281. Ref<Mesh> mesh;
  282. Map<String, Ref<Material> > name_map;
  283. if (FileAccess::exists(p_path)) {
  284. mesh = ResourceLoader::load(p_path, "Mesh");
  285. if (mesh.is_valid()) {
  286. for (int i = 0; i < mesh->get_surface_count(); i++) {
  287. if (!mesh->surface_get_material(i).is_valid())
  288. continue;
  289. String name;
  290. if (mesh->surface_get_name(i) != "")
  291. name = mesh->surface_get_name(i);
  292. else
  293. name = vformat(TTR("Surface %d"), i + 1);
  294. name_map[name] = mesh->surface_get_material(i);
  295. }
  296. while (mesh->get_surface_count()) {
  297. mesh->surface_remove(0);
  298. }
  299. }
  300. }
  301. if (!mesh.is_valid())
  302. mesh = Ref<Mesh>(memnew(Mesh));
  303. bool generate_normals = from->get_option("generate/normals");
  304. bool generate_tangents = from->get_option("generate/tangents");
  305. bool flip_faces = from->get_option("force/flip_faces");
  306. bool force_smooth = from->get_option("force/smooth_shading");
  307. bool weld_vertices = from->get_option("force/weld_vertices");
  308. float weld_tolerance = from->get_option("force/weld_tolerance");
  309. Vector<Vector3> vertices;
  310. Vector<Vector3> normals;
  311. Vector<Vector2> uvs;
  312. String name;
  313. Ref<SurfaceTool> surf_tool = memnew(SurfaceTool);
  314. surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
  315. if (force_smooth)
  316. surf_tool->add_smooth_group(true);
  317. int has_index_data = false;
  318. while (true) {
  319. String l = f->get_line().strip_edges();
  320. if (l.begins_with("v ")) {
  321. //vertex
  322. Vector<String> v = l.split(" ", false);
  323. ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
  324. Vector3 vtx;
  325. vtx.x = v[1].to_float();
  326. vtx.y = v[2].to_float();
  327. vtx.z = v[3].to_float();
  328. vertices.push_back(vtx);
  329. } else if (l.begins_with("vt ")) {
  330. //uv
  331. Vector<String> v = l.split(" ", false);
  332. ERR_FAIL_COND_V(v.size() < 3, ERR_INVALID_DATA);
  333. Vector2 uv;
  334. uv.x = v[1].to_float();
  335. uv.y = 1.0 - v[2].to_float();
  336. uvs.push_back(uv);
  337. } else if (l.begins_with("vn ")) {
  338. //normal
  339. Vector<String> v = l.split(" ", false);
  340. ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
  341. Vector3 nrm;
  342. nrm.x = v[1].to_float();
  343. nrm.y = v[2].to_float();
  344. nrm.z = v[3].to_float();
  345. normals.push_back(nrm);
  346. }
  347. if (l.begins_with("f ")) {
  348. //vertex
  349. has_index_data = true;
  350. Vector<String> v = l.split(" ", false);
  351. ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
  352. //not very fast, could be sped up
  353. Vector<String> face[3];
  354. face[0] = v[1].split("/");
  355. face[1] = v[2].split("/");
  356. ERR_FAIL_COND_V(face[0].size() == 0, ERR_PARSE_ERROR);
  357. ERR_FAIL_COND_V(face[0].size() != face[1].size(), ERR_PARSE_ERROR);
  358. for (int i = 2; i < v.size() - 1; i++) {
  359. face[2] = v[i + 1].split("/");
  360. ERR_FAIL_COND_V(face[0].size() != face[2].size(), ERR_PARSE_ERROR);
  361. for (int j = 0; j < 3; j++) {
  362. int idx = j;
  363. if (!flip_faces && idx < 2) {
  364. idx = 1 ^ idx;
  365. }
  366. if (face[idx].size() == 3) {
  367. int norm = face[idx][2].to_int() - 1;
  368. ERR_FAIL_INDEX_V(norm, normals.size(), ERR_PARSE_ERROR);
  369. surf_tool->add_normal(normals[norm]);
  370. }
  371. if (face[idx].size() >= 2 && face[idx][1] != String()) {
  372. int uv = face[idx][1].to_int() - 1;
  373. ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_PARSE_ERROR);
  374. surf_tool->add_uv(uvs[uv]);
  375. }
  376. int vtx = face[idx][0].to_int() - 1;
  377. ERR_FAIL_INDEX_V(vtx, vertices.size(), ERR_PARSE_ERROR);
  378. Vector3 vertex = vertices[vtx];
  379. if (weld_vertices)
  380. vertex = vertex.snapped(weld_tolerance);
  381. surf_tool->add_vertex(vertex);
  382. }
  383. face[1] = face[2];
  384. }
  385. } else if (l.begins_with("s ") && !force_smooth) { //smoothing
  386. String what = l.substr(2, l.length()).strip_edges();
  387. if (what == "off")
  388. surf_tool->add_smooth_group(false);
  389. else
  390. surf_tool->add_smooth_group(true);
  391. } else if (l.begins_with("o ") || f->eof_reached()) { //new surface or done
  392. if (has_index_data) {
  393. //new object/surface
  394. if (generate_normals || force_smooth)
  395. surf_tool->generate_normals();
  396. if (uvs.size() && (normals.size() || generate_normals) && generate_tangents)
  397. surf_tool->generate_tangents();
  398. surf_tool->index();
  399. mesh = surf_tool->commit(mesh);
  400. if (name == "")
  401. name = vformat(TTR("Surface %d"), mesh->get_surface_count() - 1);
  402. mesh->surface_set_name(mesh->get_surface_count() - 1, name);
  403. name = "";
  404. surf_tool->clear();
  405. surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
  406. if (force_smooth)
  407. surf_tool->add_smooth_group(true);
  408. has_index_data = false;
  409. }
  410. if (l.begins_with("o ")) //name
  411. name = l.substr(2, l.length()).strip_edges();
  412. if (f->eof_reached())
  413. break;
  414. }
  415. }
  416. from->set_source_md5(0, FileAccess::get_md5(src_path));
  417. from->set_editor(get_name());
  418. mesh->set_import_metadata(from);
  419. //re-apply materials if exist
  420. for (int i = 0; i < mesh->get_surface_count(); i++) {
  421. String n = mesh->surface_get_name(i);
  422. if (name_map.has(n))
  423. mesh->surface_set_material(i, name_map[n]);
  424. }
  425. Error err = ResourceSaver::save(p_path, mesh);
  426. return err;
  427. }
  428. void EditorMeshImportPlugin::import_from_drop(const Vector<String> &p_drop, const String &p_dest_path) {
  429. Vector<String> files;
  430. for (int i = 0; i < p_drop.size(); i++) {
  431. String ext = p_drop[i].extension().to_lower();
  432. String file = p_drop[i].get_file();
  433. if (ext == "obj") {
  434. files.push_back(p_drop[i]);
  435. }
  436. }
  437. if (files.size()) {
  438. import_dialog();
  439. dialog->_choose_files(files);
  440. dialog->_choose_save_dir(p_dest_path);
  441. }
  442. }
  443. EditorMeshImportPlugin::EditorMeshImportPlugin(EditorNode *p_editor) {
  444. dialog = memnew(EditorMeshImportDialog(this));
  445. p_editor->get_gui_base()->add_child(dialog);
  446. }