From 65fb961b8b2c81ce33c9e0e4c0add7b13da0193a Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sat, 9 Dec 2017 14:11:26 -0300 Subject: [PATCH] -Ability to and unwrap lightmap coordinates on import -Added unwrap functionality to Mesh -Ability to display and debug mesh UVs -Added multiline draw, so it's easier and faster to draw UVs -Many fixes to SurfaceTool -Fixes to Thekla Unwrap, but it's a piece of ass and it keeps crashing. Will have to go away --- drivers/gles3/rasterizer_canvas_gles3.cpp | 23 +- editor/import/resource_importer_scene.cpp | 173 ++++++++++----- editor/import/resource_importer_scene.h | 15 +- .../plugins/mesh_instance_editor_plugin.cpp | 145 ++++++++++++ editor/plugins/mesh_instance_editor_plugin.h | 10 + modules/thekla_unwrap/SCsub | 3 +- modules/thekla_unwrap/register_types.cpp | 85 ++++++- scene/2d/canvas_item.cpp | 25 +++ scene/2d/canvas_item.h | 2 + scene/resources/mesh.cpp | 209 +++++++++++++++++- scene/resources/mesh.h | 7 + scene/resources/surface_tool.cpp | 114 +++++++++- scene/resources/surface_tool.h | 3 +- servers/visual/rasterizer.h | 2 + servers/visual/visual_server_canvas.cpp | 85 +++++++ servers/visual/visual_server_canvas.h | 1 + servers/visual/visual_server_raster.h | 1 + servers/visual/visual_server_wrap_mt.h | 1 + servers/visual_server.h | 1 + .../thekla_atlas/nvmesh/param/Atlas.cpp | 4 + thirdparty/thekla_atlas/nvmesh/param/Atlas.h | 3 + .../thekla_atlas/nvmesh/param/AtlasPacker.cpp | 12 +- 22 files changed, 846 insertions(+), 78 deletions(-) diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 0839f930c9df..309497c938e7 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -527,7 +527,9 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1); #ifdef GLES_OVER_GL glEnable(GL_LINE_SMOOTH); - if (pline->lines.size()) { + if (pline->multiline) { + //needs to be different + } else { _draw_generic(GL_LINE_LOOP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); } glDisable(GL_LINE_SMOOTH); @@ -538,7 +540,23 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur if (pline->antialiased) glEnable(GL_LINE_SMOOTH); #endif - _draw_generic(GL_LINE_STRIP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); + + if (pline->multiline) { + int todo = pline->lines.size() / 2; + int max_per_call = data.polygon_buffer_size / (sizeof(real_t) * 4); + int offset = 0; + + while (todo) { + int to_draw = MIN(max_per_call, todo); + _draw_generic(GL_LINES, to_draw * 2, &pline->lines.ptr()[offset], NULL, pline->line_colors.size() == 1 ? pline->line_colors.ptr() : &pline->line_colors.ptr()[offset], pline->line_colors.size() == 1); + todo -= to_draw; + offset += to_draw * 2; + } + + } else { + + _draw_generic(GL_LINES, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); + } #ifdef GLES_OVER_GL if (pline->antialiased) @@ -1705,6 +1723,7 @@ void RasterizerCanvasGLES3::initialize() { glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); //allocate max size glBindBuffer(GL_ARRAY_BUFFER, 0); + data.polygon_buffer_size = poly_size; //quad arrays for (int i = 0; i < 4; i++) { diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 4ef8ec48a0ab..08d2897250cd 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -47,8 +47,6 @@ #include "scene/resources/ray_shape.h" #include "scene/resources/sphere_shape.h" - - uint32_t EditorSceneImporter::get_import_flags() const { if (get_script_instance()) { @@ -60,80 +58,73 @@ uint32_t EditorSceneImporter::get_import_flags() const { void EditorSceneImporter::get_extensions(List *r_extensions) const { if (get_script_instance()) { - Array arr= get_script_instance()->call("_get_extensions"); - for(int i=0;icall("_get_extensions"); + for (int i = 0; i < arr.size(); i++) { r_extensions->push_back(arr[i]); } return; } ERR_FAIL(); - } Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List *r_missing_deps, Error *r_err) { if (get_script_instance()) { - return get_script_instance()->call("_import_scene",p_path,p_flags,p_bake_fps); + return get_script_instance()->call("_import_scene", p_path, p_flags, p_bake_fps); } ERR_FAIL_V(NULL); - } -Ref EditorSceneImporter::import_animation(const String &p_path, uint32_t p_flags,int p_bake_fps) { +Ref EditorSceneImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { if (get_script_instance()) { - return get_script_instance()->call("_import_animation",p_path,p_flags); + return get_script_instance()->call("_import_animation", p_path, p_flags); } ERR_FAIL_V(NULL); - } //for documenters, these functions are useful when an importer calls an external conversion helper (like, fbx2gltf), //and you want to load the resulting file -Node* EditorSceneImporter::import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) { - - return ResourceImporterScene::get_singleton()->import_scene_from_other_importer(this,p_path,p_flags,p_bake_fps); +Node *EditorSceneImporter::import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) { + return ResourceImporterScene::get_singleton()->import_scene_from_other_importer(this, p_path, p_flags, p_bake_fps); } Ref EditorSceneImporter::import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps) { - return ResourceImporterScene::get_singleton()->import_animation_from_other_importer(this,p_path,p_flags,p_bake_fps); - + return ResourceImporterScene::get_singleton()->import_animation_from_other_importer(this, p_path, p_flags, p_bake_fps); } void EditorSceneImporter::_bind_methods() { - ClassDB::bind_method(D_METHOD("import_scene_from_other_importer","path","flags","bake_fps"),&EditorSceneImporter::import_scene_from_other_importer); - ClassDB::bind_method(D_METHOD("import_animation_from_other_importer","path","flags","bake_fps"),&EditorSceneImporter::import_animation_from_other_importer); + ClassDB::bind_method(D_METHOD("import_scene_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_scene_from_other_importer); + ClassDB::bind_method(D_METHOD("import_animation_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_animation_from_other_importer); BIND_VMETHOD(MethodInfo(Variant::INT, "_get_import_flags")); BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_extensions")); - MethodInfo mi = MethodInfo(Variant::OBJECT, "_import_scene",PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps")); - mi.return_val.class_name="Node"; + MethodInfo mi = MethodInfo(Variant::OBJECT, "_import_scene", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps")); + mi.return_val.class_name = "Node"; BIND_VMETHOD(mi); - mi = MethodInfo(Variant::OBJECT, "_import_animation",PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps")); - mi.return_val.class_name="Animation"; + mi = MethodInfo(Variant::OBJECT, "_import_animation", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps")); + mi.return_val.class_name = "Animation"; BIND_VMETHOD(mi); - BIND_CONSTANT( IMPORT_SCENE ); - BIND_CONSTANT( IMPORT_ANIMATION ); - BIND_CONSTANT( IMPORT_ANIMATION_DETECT_LOOP ); - BIND_CONSTANT( IMPORT_ANIMATION_OPTIMIZE ); - BIND_CONSTANT( IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS ); - BIND_CONSTANT( IMPORT_ANIMATION_KEEP_VALUE_TRACKS ); - BIND_CONSTANT( IMPORT_GENERATE_TANGENT_ARRAYS ); - BIND_CONSTANT( IMPORT_FAIL_ON_MISSING_DEPENDENCIES ); - BIND_CONSTANT( IMPORT_MATERIALS_IN_INSTANCES ); - BIND_CONSTANT( IMPORT_USE_COMPRESSION ); - + BIND_CONSTANT(IMPORT_SCENE); + BIND_CONSTANT(IMPORT_ANIMATION); + BIND_CONSTANT(IMPORT_ANIMATION_DETECT_LOOP); + BIND_CONSTANT(IMPORT_ANIMATION_OPTIMIZE); + BIND_CONSTANT(IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS); + BIND_CONSTANT(IMPORT_ANIMATION_KEEP_VALUE_TRACKS); + BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS); + BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES); + BIND_CONSTANT(IMPORT_MATERIALS_IN_INSTANCES); + BIND_CONSTANT(IMPORT_USE_COMPRESSION); } - ///////////////////////////////// void EditorScenePostImport::_bind_methods() { @@ -201,6 +192,10 @@ bool ResourceImporterScene::get_option_visibility(const String &p_option, const return false; } + if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) < 2) { + return false; + } + return true; } @@ -961,6 +956,36 @@ static String _make_extname(const String &p_str) { return ext_name; } +void ResourceImporterScene::_find_meshes(Node *p_node, Map, Transform> &meshes) { + + List pi; + p_node->get_property_list(&pi); + + MeshInstance *mi = Object::cast_to(p_node); + + if (mi) { + + Ref mesh = mi->get_mesh(); + + if (mesh.is_valid() && !meshes.has(mesh)) { + Spatial *s = mi; + while (s->get_parent_spatial()) { + s = s->get_parent_spatial(); + } + + if (s == mi) { + meshes[mesh] = s->get_transform(); + } else { + meshes[mesh] = s->get_transform() * mi->get_relative_transform(s); + } + } + } + for (int i = 0; i < p_node->get_child_count(); i++) { + + _find_meshes(p_node->get_child(i), meshes); + } +} + void ResourceImporterScene::_make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_keep_animations, bool p_make_materials, bool p_keep_materials, bool p_make_meshes, Map, Ref > &p_animations, Map, Ref > &p_materials, Map, Ref > &p_meshes) { List pi; @@ -1140,7 +1165,8 @@ void ResourceImporterScene::get_import_options(List *r_options, in r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/compress"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files"), meshes_out ? 1 : 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.05)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "external_files/store_in_subdir"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); @@ -1171,17 +1197,16 @@ void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_ Node *n = p_node->get_child(i); _replace_owner(n, p_scene, p_new_owner); } - } -Node* ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporter *p_exception,const String &p_path, uint32_t p_flags, int p_bake_fps) { +Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) { Ref importer; String ext = p_path.get_extension().to_lower(); for (Set >::Element *E = importers.front(); E; E = E->next()) { - if (E->get().ptr()==p_exception) + if (E->get().ptr() == p_exception) continue; List extensions; E->get()->get_extensions(&extensions); @@ -1199,42 +1224,41 @@ Node* ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporte break; } - ERR_FAIL_COND_V(!importer.is_valid(),NULL); + ERR_FAIL_COND_V(!importer.is_valid(), NULL); List missing; Error err; - return importer->import_scene(p_path,p_flags,p_bake_fps,&missing,&err); + return importer->import_scene(p_path, p_flags, p_bake_fps, &missing, &err); } -Ref ResourceImporterScene::import_animation_from_other_importer(EditorSceneImporter *p_exception,const String &p_path, uint32_t p_flags, int p_bake_fps) { +Ref ResourceImporterScene::import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps) { + Ref importer; + String ext = p_path.get_extension().to_lower(); - Ref importer; - String ext = p_path.get_extension().to_lower(); + for (Set >::Element *E = importers.front(); E; E = E->next()) { - for (Set >::Element *E = importers.front(); E; E = E->next()) { + if (E->get().ptr() == p_exception) + continue; + List extensions; + E->get()->get_extensions(&extensions); - if (E->get().ptr()==p_exception) - continue; - List extensions; - E->get()->get_extensions(&extensions); + for (List::Element *F = extensions.front(); F; F = F->next()) { - for (List::Element *F = extensions.front(); F; F = F->next()) { + if (F->get().to_lower() == ext) { - if (F->get().to_lower() == ext) { + importer = E->get(); + break; + } + } - importer = E->get(); - break; - } - } + if (importer.is_valid()) + break; + } - if (importer.is_valid()) - break; - } + ERR_FAIL_COND_V(!importer.is_valid(), NULL); - ERR_FAIL_COND_V(!importer.is_valid(),NULL); - - return importer->import_animation(p_path,p_flags,p_bake_fps); + return importer->import_animation(p_path, p_flags, p_bake_fps); } Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files) { @@ -1371,6 +1395,37 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p } } + if (light_bake_mode == 2 /* || generate LOD */) { + + Map, Transform> meshes; + _find_meshes(scene, meshes); + + if (light_bake_mode == 2) { + + float texel_size = p_options["meshes/lightmap_texel_size"]; + texel_size = MAX(0.001, texel_size); + + EditorProgress progress("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size()); + int step = 0; + for (Map, Transform>::Element *E = meshes.front(); E; E = E->next()) { + + Ref mesh = E->key(); + String name = mesh->get_name(); + if (name == "") { //should not happen but.. + name = "Mesh " + itos(step); + } + + progress.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step); + + Error err = mesh->lightmap_unwrap(E->get(), texel_size); + if (err != OK) { + EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry."); + } + step++; + } + } + } + if (external_animations || external_materials || external_meshes) { Map, Ref > anim_map; Map, Ref > mat_map; diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index fc6ce05fa4f0..933585a48c04 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -40,13 +40,13 @@ class Material; class EditorSceneImporter : public Reference { GDCLASS(EditorSceneImporter, Reference); -protected: +protected: static void _bind_methods(); - - Node* import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps); + Node *import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps); Ref import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps); + public: enum ImportFlags { IMPORT_SCENE = 1, @@ -62,11 +62,10 @@ public: }; - virtual uint32_t get_import_flags() const; virtual void get_extensions(List *r_extensions) const; virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List *r_missing_deps, Error *r_err = NULL); - virtual Ref import_animation(const String &p_path, uint32_t p_flags,int p_bake_fps); + virtual Ref import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); EditorSceneImporter() {} }; @@ -136,6 +135,8 @@ public: virtual bool get_option_visibility(const String &p_option, const Map &p_options) const; virtual int get_import_order() const { return 100; } //after everything + void _find_meshes(Node *p_node, Map, Transform> &meshes); + void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_keep_animations, bool p_make_materials, bool p_keep_materials, bool p_make_meshes, Map, Ref > &p_animations, Map, Ref > &p_materials, Map, Ref > &p_meshes); Node *_fix_node(Node *p_node, Node *p_root, Map, Ref > &collision_map, LightBakeMode p_light_bake_mode); @@ -147,8 +148,8 @@ public: virtual Error import(const String &p_source_file, const String &p_save_path, const Map &p_options, List *r_platform_variants, List *r_gen_files = NULL); - Node* import_scene_from_other_importer(EditorSceneImporter *p_exception,const String &p_path, uint32_t p_flags, int p_bake_fps); - Ref import_animation_from_other_importer(EditorSceneImporter *p_exception,const String &p_path, uint32_t p_flags, int p_bake_fps); + Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps); + Ref import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps); ResourceImporterScene(); }; diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 84fc0cecf2fa..9d116349c0fb 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -195,9 +195,141 @@ void MeshInstanceEditor::_menu_option(int p_option) { outline_dialog->popup_centered(Vector2(200, 90)); } break; + case MENU_OPTION_CREATE_UV2: { + + Ref mesh = node->get_mesh(); + if (!mesh.is_valid()) { + err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); + err_dialog->popup_centered_minsize(); + return; + } + + Error err = mesh->lightmap_unwrap(node->get_global_transform()); + if (err != OK) { + err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); + err_dialog->popup_centered_minsize(); + return; + } + + } break; + case MENU_OPTION_DEBUG_UV1: { + Ref mesh = node->get_mesh(); + if (!mesh.is_valid()) { + err_dialog->set_text(TTR("No mesh to debug.")); + err_dialog->popup_centered_minsize(); + return; + } + _create_uv_lines(0); + } break; + case MENU_OPTION_DEBUG_UV2: { + Ref mesh = node->get_mesh(); + if (!mesh.is_valid()) { + err_dialog->set_text(TTR("No mesh to debug.")); + err_dialog->popup_centered_minsize(); + return; + } + _create_uv_lines(1); + } break; } } +struct MeshInstanceEditorEdgeSort { + + Vector2 a; + Vector2 b; + + bool operator<(const MeshInstanceEditorEdgeSort &p_b) const { + if (a == p_b.a) + return b < p_b.b; + else + return a < p_b.a; + } + + MeshInstanceEditorEdgeSort() {} + MeshInstanceEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) { + if (p_a < p_b) { + a = p_a; + b = p_b; + } else { + b = p_a; + a = p_b; + } + } +}; + +void MeshInstanceEditor::_create_uv_lines(int p_layer) { + + Ref mesh = node->get_mesh(); + ERR_FAIL_COND(!mesh.is_valid()); + + Set edges; + uv_lines.clear(); + for (int i = 0; i < mesh->get_surface_count(); i++) { + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) + continue; + Array a = mesh->surface_get_arrays(i); + + PoolVector uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2]; + if (uv.size() == 0) { + err_dialog->set_text(TTR("Model has no UV in this layer")); + err_dialog->popup_centered_minsize(); + return; + } + + PoolVector::Read r = uv.read(); + + PoolVector indices = a[Mesh::ARRAY_INDEX]; + PoolVector::Read ri; + + int ic; + bool use_indices; + + if (indices.size()) { + ic = indices.size(); + ri = indices.read(); + use_indices = true; + } else { + ic = uv.size(); + use_indices = false; + } + + for (int j = 0; j < ic; j += 3) { + + for (int k = 0; k < 3; k++) { + + MeshInstanceEditorEdgeSort edge; + if (use_indices) { + edge.a = r[ri[j + k]]; + edge.b = r[ri[j + ((k + 1) % 3)]]; + } else { + edge.a = r[j + k]; + edge.b = r[j + ((k + 1) % 3)]; + } + + if (edges.has(edge)) + continue; + + uv_lines.push_back(edge.a); + uv_lines.push_back(edge.b); + edges.insert(edge); + } + } + } + + debug_uv_dialog->popup_centered_minsize(); +} + +void MeshInstanceEditor::_debug_uv_draw() { + + if (uv_lines.size() == 0) + return; + + debug_uv->set_clip_contents(true); + debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), Color(0.2, 0.2, 0.0)); + debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); + debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); +} + void MeshInstanceEditor::_create_outline_mesh() { Ref mesh = node->get_mesh(); @@ -244,6 +376,7 @@ void MeshInstanceEditor::_bind_methods() { ClassDB::bind_method("_menu_option", &MeshInstanceEditor::_menu_option); ClassDB::bind_method("_create_outline_mesh", &MeshInstanceEditor::_create_outline_mesh); + ClassDB::bind_method("_debug_uv_draw", &MeshInstanceEditor::_debug_uv_draw); } MeshInstanceEditor::MeshInstanceEditor() { @@ -263,6 +396,10 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Outline Mesh.."), MENU_OPTION_CREATE_OUTLINE_MESH); + options->get_popup()->add_separator(); + options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1); + options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2); + options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2); options->get_popup()->connect("id_pressed", this, "_menu_option"); @@ -286,6 +423,14 @@ MeshInstanceEditor::MeshInstanceEditor() { err_dialog = memnew(AcceptDialog); add_child(err_dialog); + + debug_uv_dialog = memnew(AcceptDialog); + debug_uv_dialog->set_title("UV Channel Debug"); + add_child(debug_uv_dialog); + debug_uv = memnew(Control); + debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE); + debug_uv->connect("draw", this, "_debug_uv_draw"); + debug_uv_dialog->add_child(debug_uv); } void MeshInstanceEditorPlugin::edit(Object *p_object) { diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h index fa851458cebf..68c149f98a25 100644 --- a/editor/plugins/mesh_instance_editor_plugin.h +++ b/editor/plugins/mesh_instance_editor_plugin.h @@ -47,6 +47,9 @@ class MeshInstanceEditor : public Node { MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE, MENU_OPTION_CREATE_NAVMESH, MENU_OPTION_CREATE_OUTLINE_MESH, + MENU_OPTION_CREATE_UV2, + MENU_OPTION_DEBUG_UV1, + MENU_OPTION_DEBUG_UV2, }; MeshInstance *node; @@ -58,11 +61,18 @@ class MeshInstanceEditor : public Node { AcceptDialog *err_dialog; + AcceptDialog *debug_uv_dialog; + Control *debug_uv; + Vector uv_lines; + void _menu_option(int p_option); void _create_outline_mesh(); + void _create_uv_lines(int p_layer); friend class MeshInstanceEditorPlugin; + void _debug_uv_draw(); + protected: void _node_removed(Node *p_node); static void _bind_methods(); diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub index 2b1349a15fe8..1d4b0868489b 100644 --- a/modules/thekla_unwrap/SCsub +++ b/modules/thekla_unwrap/SCsub @@ -47,7 +47,8 @@ if env['builtin_thekla_atlas']: "nvmesh/param/SingleFaceMap.cpp", "nvmesh/param/Util.cpp", "nvmesh/weld/VertexWeld.cpp", - "nvmesh/weld/Snap.cpp" + "nvmesh/weld/Snap.cpp", + "thekla/thekla_atlas.cpp" ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp index 8a9f8341e892..01b834f8cb81 100644 --- a/modules/thekla_unwrap/register_types.cpp +++ b/modules/thekla_unwrap/register_types.cpp @@ -28,7 +28,88 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_types.h" +#include "thirdparty/thekla_atlas/thekla/thekla_atlas.h" +#include +#include +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); -void register_thekla_unwrap_types() {} +bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { -void unregister_thekla_unwrap_types() {} + //set up input mesh + Thekla::Atlas_Input_Mesh input_mesh; + input_mesh.face_array = new Thekla::Atlas_Input_Face[p_index_count / 3]; + for (int i = 0; i < p_index_count / 3; i++) { + input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0]; + input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1]; + input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2]; + printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]); + input_mesh.face_array[i].material_index = p_face_materials[i]; + } + input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count]; + for (int i = 0; i < p_vertex_count; i++) { + input_mesh.vertex_array[i].first_colocal = i; //wtf + for (int j = 0; j < 3; j++) { + input_mesh.vertex_array[i].position[j] = p_vertices[i * 3 + j]; + input_mesh.vertex_array[i].normal[j] = p_normals[i * 3 + j]; + } + input_mesh.vertex_array[i].uv[0] = 0; + input_mesh.vertex_array[i].uv[1] = 0; + printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]); + printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]); + } + input_mesh.face_count = p_index_count / 3; + input_mesh.vertex_count = p_vertex_count; + + //set up options + Thekla::Atlas_Options options; + Thekla::atlas_set_default_options(&options); + options.packer_options.witness.packing_quality = 1; + options.packer_options.witness.texel_area = 1.0 / p_texel_size; + + //generate + Thekla::Atlas_Error err; + Thekla::Atlas_Output_Mesh *output = atlas_generate(&input_mesh, &options, &err); + + delete[] input_mesh.face_array; + delete[] input_mesh.vertex_array; + + if (err != Thekla::Atlas_Error_Success) { + printf("error with atlas\n"); + } else { + *r_vertex = (int *)malloc(sizeof(int) * output->vertex_count); + *r_uv = (float *)malloc(sizeof(float) * output->vertex_count * 3); + *r_index = (int *)malloc(sizeof(int) * output->index_count); + + // printf("w: %i, h: %i\n", output->atlas_width, output->atlas_height); + for (int i = 0; i < output->vertex_count; i++) { + (*r_vertex)[i] = output->vertex_array[i].xref; + (*r_uv)[i * 2 + 0] = output->vertex_array[i].uv[0] / output->atlas_width; + (*r_uv)[i * 2 + 1] = output->vertex_array[i].uv[1] / output->atlas_height; + // printf("uv: %f,%f\n", (*r_uv)[i * 2 + 0], (*r_uv)[i * 2 + 1]); + } + *r_vertex_count = output->vertex_count; + + for (int i = 0; i < output->index_count; i++) { + (*r_index)[i] = output->index_array[i]; + } + + *r_index_count = output->index_count; + + *r_size_hint_x = output->atlas_height; + *r_size_hint_y = output->atlas_width; + } + + if (output) { + atlas_free(output); + } + + return err == Thekla::Atlas_Error_Success; +} + +void register_thekla_unwrap_types() { + + array_mesh_lightmap_unwrap_callback = thekla_mesh_lightmap_unwrap_callback; +} + +void unregister_thekla_unwrap_types() { +} diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 768d5185d7b8..82123d12acb9 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -623,6 +623,29 @@ void CanvasItem::draw_polyline_colors(const Vector &p_points, const Vect VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased); } + +void CanvasItem::draw_multiline(const Vector &p_points, const Color &p_color, float p_width, bool p_antialiased) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + Vector colors; + colors.push_back(p_color); + VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width, p_antialiased); +} + +void CanvasItem::draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width, bool p_antialiased) { + + if (!drawing) { + ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL(); + } + + VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width, p_antialiased); +} + void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) { if (!drawing) { @@ -979,6 +1002,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width", "antialiased"), &CanvasItem::draw_multiline, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true)); ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle); ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant())); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index ccbb528d6f99..2384c0f370d1 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -268,6 +268,8 @@ public: void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_polyline(const Vector &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_polyline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false); + void draw_multiline(const Vector &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); + void draw_multiline_colors(const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false); void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true); void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color); void draw_texture(const Ref &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref &p_normal_map = Ref()); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 0a886c25b1a8..842af20ac81e 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "mesh.h" +#include "pair.h" #include "scene/resources/concave_polygon_shape.h" #include "scene/resources/convex_polygon_shape.h" #include "surface_tool.h" - void Mesh::_clear_triangle_mesh() const { triangle_mesh.unref(); @@ -413,8 +413,21 @@ Ref Mesh::create_outline(float p_margin) const { return newmesh; } +void Mesh::set_lightmap_size_hint(const Vector2 &p_size) { + lightmap_size_hint = p_size; +} + +Size2 Mesh::get_lightmap_size_hint() const { + return lightmap_size_hint; +} + void Mesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &Mesh::set_lightmap_size_hint); + ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint); + + ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint"); + BIND_ENUM_CONSTANT(PRIMITIVE_POINTS); BIND_ENUM_CONSTANT(PRIMITIVE_LINES); BIND_ENUM_CONSTANT(PRIMITIVE_LINE_STRIP); @@ -1035,6 +1048,200 @@ void ArrayMesh::regen_normalmaps() { } } +//dirty hack +bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL; + +struct ArrayMeshLightmapSurface { + + Ref material; + Vector vertices; + Mesh::PrimitiveType primitive; + uint32_t format; +}; + +Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) { + + ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); + ERR_EXPLAIN("Can't unwrap mesh with blend shapes"); + ERR_FAIL_COND_V(blend_shapes.size() != 0, ERR_UNAVAILABLE); + + Vector vertices; + Vector normals; + Vector indices; + Vector face_materials; + Vector uv; + Vector > uv_index; + + Vector surfaces; + for (int i = 0; i < get_surface_count(); i++) { + ArrayMeshLightmapSurface s; + s.primitive = surface_get_primitive_type(i); + + if (s.primitive != Mesh::PRIMITIVE_TRIANGLES) { + ERR_EXPLAIN("Only triangles are supported for lightmap unwrap"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + s.format = surface_get_format(i); + if (!(s.format & ARRAY_FORMAT_NORMAL)) { + ERR_EXPLAIN("Normals are required for lightmap unwrap"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + + Array arrays = surface_get_arrays(i); + s.material = surface_get_material(i); + s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays); + + PoolVector rvertices = arrays[Mesh::ARRAY_VERTEX]; + int vc = rvertices.size(); + PoolVector::Read r = rvertices.read(); + + PoolVector rnormals = arrays[Mesh::ARRAY_NORMAL]; + PoolVector::Read rn = rnormals.read(); + + int vertex_ofs = vertices.size() / 3; + + vertices.resize((vertex_ofs + vc) * 3); + normals.resize((vertex_ofs + vc) * 3); + uv_index.resize(vertex_ofs + vc); + + for (int j = 0; j < vc; j++) { + + Vector3 v = p_base_transform.xform(r[j]); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = rn[j].x; + normals[(j + vertex_ofs) * 3 + 1] = rn[j].y; + normals[(j + vertex_ofs) * 3 + 2] = rn[j].z; + uv_index[j + vertex_ofs] = Pair(i, j); + } + + PoolVector rindices = arrays[Mesh::ARRAY_INDEX]; + int ic = rindices.size(); + int index_ofs = indices.size(); + + if (ic == 0) { + indices.resize(index_ofs + vc); + face_materials.resize((index_ofs + vc) / 3); + for (int j = 0; j < vc; j++) { + indices[index_ofs + j] = vertex_ofs + j; + } + for (int j = 0; j < vc / 3; j++) { + face_materials[(index_ofs / 3) + j] = i; + } + + } else { + PoolVector::Read ri = rindices.read(); + indices.resize(index_ofs + ic); + face_materials.resize((index_ofs + ic) / 3); + for (int j = 0; j < ic; j++) { + indices[index_ofs + j] = vertex_ofs + ri[j]; + } + for (int j = 0; j < ic / 3; j++) { + face_materials[(index_ofs / 3) + j] = i; + } + } + + surfaces.push_back(s); + } + + //unwrap + + float *gen_uvs; + int *gen_vertices; + int *gen_indices; + int gen_vertex_count; + int gen_index_count; + int size_x; + int size_y; + + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), face_materials.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); + + if (!ok) { + return ERR_CANT_CREATE; + } + + //remove surfaces + while (get_surface_count()) { + surface_remove(0); + } + + //create surfacetools for each surface.. + Vector > surfaces_tools; + + for (int i = 0; i < surfaces.size(); i++) { + Ref st; + st.instance(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + st->set_material(surfaces[i].material); + surfaces_tools.push_back(st); //stay there + } + + print_line("gen indices: " + itos(gen_index_count)); + //go through all indices + for (int i = 0; i < gen_index_count; i += 3) { + + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_index.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_index.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_index.size(), ERR_BUG); + + ERR_FAIL_COND_V(uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 1]]].first || uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); + + int surface = uv_index[gen_vertices[gen_indices[i + 0]]].first; + + for (int j = 0; j < 3; j++) { + + int vertex_idx = gen_vertices[gen_indices[i + j]]; + + SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second]; + + if (surfaces[surface].format & ARRAY_FORMAT_COLOR) { + surfaces_tools[surface]->add_color(v.color); + } + if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { + surfaces_tools[surface]->add_uv(v.uv); + } + if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) { + surfaces_tools[surface]->add_normal(v.normal); + } + if (surfaces[surface].format & ARRAY_FORMAT_TANGENT) { + Plane t; + t.normal = v.tangent; + t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; + surfaces_tools[surface]->add_tangent(t); + } + if (surfaces[surface].format & ARRAY_FORMAT_BONES) { + surfaces_tools[surface]->add_bones(v.bones); + } + if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { + surfaces_tools[surface]->add_weights(v.weights); + } + + Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); + surfaces_tools[surface]->add_uv2(uv2); + + surfaces_tools[surface]->add_vertex(v.vertex); + } + } + + //free stuff + ::free(gen_vertices); + ::free(gen_indices); + ::free(gen_uvs); + + //generate surfaces + + for (int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + surfaces_tools[i]->commit(Ref((ArrayMesh *)this), surfaces[i].format); + } + + set_lightmap_size_hint(Size2(size_x, size_y)); + + return OK; +} + void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ArrayMesh::add_blend_shape); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index b85a6a84af4a..ea38ebf2ff1f 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -43,6 +43,8 @@ class Mesh : public Resource { GDCLASS(Mesh, Resource); mutable Ref triangle_mesh; //cached + Size2 lightmap_size_hint; + protected: void _clear_triangle_mesh() const; @@ -138,6 +140,9 @@ public: virtual AABB get_aabb() const = 0; + void set_lightmap_size_hint(const Vector2 &p_size); + Size2 get_lightmap_size_hint() const; + Mesh(); }; @@ -216,6 +221,8 @@ public: void center_geometry(); void regen_normalmaps(); + Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); + virtual void reload_from_file(); ArrayMesh(); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index bf89e704bca9..352418e65c46 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -101,6 +101,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { vtx.color = last_color; vtx.normal = last_normal; vtx.uv = last_uv; + vtx.uv2 = last_uv2; vtx.weights = last_weights; vtx.bones = last_bones; vtx.tangent = last_tangent.normal; @@ -401,7 +402,7 @@ Array SurfaceTool::commit_to_arrays() { return a; } -Ref SurfaceTool::commit(const Ref &p_existing) { +Ref SurfaceTool::commit(const Ref &p_existing, uint32_t p_flags) { Ref mesh; if (p_existing.is_valid()) @@ -418,7 +419,7 @@ Ref SurfaceTool::commit(const Ref &p_existing) { Array a = commit_to_arrays(); - mesh->add_surface_from_arrays(primitive, a); + mesh->add_surface_from_arrays(primitive, a, Array(), p_flags); if (material.is_valid()) mesh->surface_set_material(surface, material); @@ -482,6 +483,113 @@ void SurfaceTool::_create_list(const Ref &p_existing, int p_surface, List< _create_list_from_arrays(arr, r_vertex, r_index, lformat); } +Vector SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays) { + + Vector ret; + + PoolVector varr = p_arrays[VS::ARRAY_VERTEX]; + PoolVector narr = p_arrays[VS::ARRAY_NORMAL]; + PoolVector tarr = p_arrays[VS::ARRAY_TANGENT]; + PoolVector carr = p_arrays[VS::ARRAY_COLOR]; + PoolVector uvarr = p_arrays[VS::ARRAY_TEX_UV]; + PoolVector uv2arr = p_arrays[VS::ARRAY_TEX_UV2]; + PoolVector barr = p_arrays[VS::ARRAY_BONES]; + PoolVector warr = p_arrays[VS::ARRAY_WEIGHTS]; + + int vc = varr.size(); + + if (vc == 0) + return ret; + int lformat = 0; + + PoolVector::Read rv; + if (varr.size()) { + lformat |= VS::ARRAY_FORMAT_VERTEX; + rv = varr.read(); + } + PoolVector::Read rn; + if (narr.size()) { + lformat |= VS::ARRAY_FORMAT_NORMAL; + rn = narr.read(); + } + PoolVector::Read rt; + if (tarr.size()) { + lformat |= VS::ARRAY_FORMAT_TANGENT; + rt = tarr.read(); + } + PoolVector::Read rc; + if (carr.size()) { + lformat |= VS::ARRAY_FORMAT_COLOR; + rc = carr.read(); + } + + PoolVector::Read ruv; + if (uvarr.size()) { + lformat |= VS::ARRAY_FORMAT_TEX_UV; + ruv = uvarr.read(); + } + + PoolVector::Read ruv2; + if (uv2arr.size()) { + lformat |= VS::ARRAY_FORMAT_TEX_UV2; + ruv2 = uv2arr.read(); + } + + PoolVector::Read rb; + if (barr.size()) { + lformat |= VS::ARRAY_FORMAT_BONES; + rb = barr.read(); + } + + PoolVector::Read rw; + if (warr.size()) { + lformat |= VS::ARRAY_FORMAT_WEIGHTS; + rw = warr.read(); + } + + for (int i = 0; i < vc; i++) { + + Vertex v; + if (lformat & VS::ARRAY_FORMAT_VERTEX) + v.vertex = varr[i]; + if (lformat & VS::ARRAY_FORMAT_NORMAL) + v.normal = narr[i]; + if (lformat & VS::ARRAY_FORMAT_TANGENT) { + Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]); + v.tangent = p.normal; + v.binormal = p.normal.cross(v.tangent).normalized() * p.d; + } + if (lformat & VS::ARRAY_FORMAT_COLOR) + v.color = carr[i]; + if (lformat & VS::ARRAY_FORMAT_TEX_UV) + v.uv = uvarr[i]; + if (lformat & VS::ARRAY_FORMAT_TEX_UV2) + v.uv2 = uv2arr[i]; + if (lformat & VS::ARRAY_FORMAT_BONES) { + Vector b; + b.resize(4); + b[0] = barr[i * 4 + 0]; + b[1] = barr[i * 4 + 1]; + b[2] = barr[i * 4 + 2]; + b[3] = barr[i * 4 + 3]; + v.bones = b; + } + if (lformat & VS::ARRAY_FORMAT_WEIGHTS) { + Vector w; + w.resize(4); + w[0] = warr[i * 4 + 0]; + w[1] = warr[i * 4 + 1]; + w[2] = warr[i * 4 + 2]; + w[3] = warr[i * 4 + 3]; + v.weights = w; + } + + ret.push_back(v); + } + + return ret; +} + void SurfaceTool::_create_list_from_arrays(Array arr, List *r_vertex, List *r_index, int &lformat) { PoolVector varr = arr[VS::ARRAY_VERTEX]; @@ -882,7 +990,7 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from); ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from); - ClassDB::bind_method(D_METHOD("commit", "existing"), &SurfaceTool::commit, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("commit", "existing"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT)); } SurfaceTool::SurfaceTool() { diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index cdaac643deeb..b180ffe26082 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -127,10 +127,11 @@ public: List &get_vertex_array() { return vertex_array; } void create_from_triangle_arrays(const Array &p_arrays); + static Vector create_vertex_array_from_triangle_arrays(const Array &p_arrays); Array commit_to_arrays(); void create_from(const Ref &p_existing, int p_surface); void append_from(const Ref &p_existing, int p_surface, const Transform &p_xform); - Ref commit(const Ref &p_existing = Ref()); + Ref commit(const Ref &p_existing = Ref(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT); SurfaceTool(); }; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 0ee70878dcc3..df41c3b5ced3 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -636,6 +636,7 @@ public: struct CommandPolyLine : public Command { bool antialiased; + bool multiline; Vector triangles; Vector triangle_colors; Vector lines; @@ -643,6 +644,7 @@ public: CommandPolyLine() { type = TYPE_POLYLINE; antialiased = false; + multiline = false; } }; diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 31c09dc23d54..0fed9cc961b3 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -416,6 +416,7 @@ void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vectorantialiased = p_antialiased; + pline->multiline = false; if (p_width <= 1) { pline->lines = p_points; @@ -486,6 +487,90 @@ void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vectorcommands.push_back(pline); } +void VisualServerCanvas::canvas_item_add_multiline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width, bool p_antialiased) { + + ERR_FAIL_COND(p_points.size() < 2); + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + Item::CommandPolyLine *pline = memnew(Item::CommandPolyLine); + ERR_FAIL_COND(!pline); + + pline->antialiased = false; //todo + pline->multiline = true; + + // if (p_width <= 1) { + pline->lines = p_points; + pline->line_colors = p_colors; + if (pline->line_colors.size() == 0) { + pline->line_colors.push_back(Color(1, 1, 1, 1)); + } else if (pline->line_colors.size() > 1 && pline->line_colors.size() != pline->lines.size()) { + pline->line_colors.resize(1); + } +#if 0 +//width not yet + } else { + //make a trianglestrip for drawing the line... + Vector2 prev_t; + pline->triangles.resize(p_points.size() * 2); + if (p_antialiased) { + pline->lines.resize(p_points.size() * 2); + } + + if (p_colors.size() == 0) { + pline->triangle_colors.push_back(Color(1, 1, 1, 1)); + if (p_antialiased) { + pline->line_colors.push_back(Color(1, 1, 1, 1)); + } + } + if (p_colors.size() == 1) { + pline->triangle_colors = p_colors; + pline->line_colors = p_colors; + } else { + pline->triangle_colors.resize(pline->triangles.size()); + pline->line_colors.resize(pline->lines.size()); + } + + for (int i = 0; i < p_points.size(); i++) { + + Vector2 t; + if (i == p_points.size() - 1) { + t = prev_t; + } else { + t = (p_points[i + 1] - p_points[i]).normalized().tangent(); + if (i == 0) { + prev_t = t; + } + } + + Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; + + if (p_antialiased) { + pline->lines[i] = p_points[i] + tangent; + pline->lines[p_points.size() * 2 - i - 1] = p_points[i] - tangent; + if (pline->line_colors.size() > 1) { + pline->line_colors[i] = p_colors[i]; + pline->line_colors[p_points.size() * 2 - i - 1] = p_colors[i]; + } + } + + pline->triangles[i * 2 + 0] = p_points[i] + tangent; + pline->triangles[i * 2 + 1] = p_points[i] - tangent; + + if (pline->triangle_colors.size() > 1) { + + pline->triangle_colors[i * 2 + 0] = p_colors[i]; + pline->triangle_colors[i * 2 + 1] = p_colors[i]; + } + + prev_t = t; + } + } +#endif + canvas_item->rect_dirty = true; + canvas_item->commands.push_back(pline); +} + void VisualServerCanvas::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) { Item *canvas_item = canvas_item_owner.getornull(p_item); diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index a92370f1f0b9..3143ac847fc0 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -172,6 +172,7 @@ public: void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void canvas_item_add_polyline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false); + void canvas_item_add_multiline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false); void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color); void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color); void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID()); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 9223d81e32ba..d843c443a229 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -551,6 +551,7 @@ public: BIND6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) BIND5(canvas_item_add_polyline, RID, const Vector &, const Vector &, float, bool) + BIND5(canvas_item_add_multiline, RID, const Vector &, const Vector &, float, bool) BIND3(canvas_item_add_rect, RID, const Rect2 &, const Color &) BIND4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) BIND7(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID) diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index b58b1d917e2f..94f450c0242e 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -467,6 +467,7 @@ public: FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) FUNC5(canvas_item_add_polyline, RID, const Vector &, const Vector &, float, bool) + FUNC5(canvas_item_add_multiline, RID, const Vector &, const Vector &, float, bool) FUNC3(canvas_item_add_rect, RID, const Rect2 &, const Color &) FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &) FUNC7(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID) diff --git a/servers/visual_server.h b/servers/visual_server.h index 350097c1b53c..de5ef7da0a75 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -815,6 +815,7 @@ public: virtual void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false) = 0; virtual void canvas_item_add_polyline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false) = 0; + virtual void canvas_item_add_multiline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width = 1.0, bool p_antialiased = false) = 0; virtual void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) = 0; virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0; virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID()) = 0; diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp b/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp index 4e41e7669588..98f92cef9682 100644 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp +++ b/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp @@ -38,6 +38,7 @@ using namespace nv; /// Ctor. Atlas::Atlas() { + failed=false; } // Dtor. @@ -100,6 +101,7 @@ void Atlas::extractCharts(const HalfEdge::Mesh * mesh) void Atlas::computeCharts(const HalfEdge::Mesh * mesh, const SegmentationSettings & settings, const Array & unchartedMaterialArray) { + failed=false; MeshCharts * meshCharts = new MeshCharts(mesh); meshCharts->computeCharts(settings, unchartedMaterialArray); addMeshCharts(meshCharts); @@ -235,6 +237,8 @@ float Atlas::packCharts(int quality, float texelsPerUnit, bool blockAlign, bool { AtlasPacker packer(this); packer.packCharts(quality, texelsPerUnit, blockAlign, conservative); + if (hasFailed()) + return 0; return packer.computeAtlasUtilization(); } diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h b/thirdparty/thekla_atlas/nvmesh/param/Atlas.h index 8b478207e350..0398bd905cf0 100644 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h +++ b/thirdparty/thekla_atlas/nvmesh/param/Atlas.h @@ -64,9 +64,12 @@ namespace nv // Pack charts in the smallest possible rectangle. float packCharts(int quality, float texelArea, bool blockAlign, bool conservative); + bool setFailed() { failed = true; } + bool hasFailed() const { return failed; } private: + bool failed; Array m_meshChartsArray; }; diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp index f2156899ae1e..5ce452cb9e72 100644 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp +++ b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp @@ -152,7 +152,7 @@ AtlasPacker::~AtlasPacker() } // This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method. -static void computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * minorAxis, Vector2 * minCorner, Vector2 * maxCorner) +static bool computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * minorAxis, Vector2 * minCorner, Vector2 * maxCorner) { // Compute list of boundary points. Array points(16); @@ -184,6 +184,9 @@ static void computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * min #if 1 Array hull; + if (points.size()==0) { + return false; + } convexHull(points, hull, 0.00001f); @@ -373,6 +376,8 @@ static void computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * min } }*/ #endif + + return true; } @@ -431,7 +436,10 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned // Compute bounding box of chart. Vector2 majorAxis, minorAxis, origin, end; - computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end); + if (!computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end)) { + m_atlas->setFailed(); + return; + } nvCheck(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(origin));