diff options
Diffstat (limited to 'tools')
38 files changed, 4212 insertions, 179 deletions
diff --git a/tools/SCsub b/tools/SCsub index 4f1432143b..528ffbf3c3 100644 --- a/tools/SCsub +++ b/tools/SCsub @@ -11,6 +11,7 @@ SConscript('collada/SCsub'); SConscript('docdump/SCsub'); SConscript('freetype/SCsub'); SConscript('doc/SCsub'); +SConscript('pck/SCsub'); lib = env.Library("tool",env.tool_sources, LIBSUFFIX=env['platform_libsuffix']) diff --git a/tools/collada/collada.cpp b/tools/collada/collada.cpp index 9962eed1b2..e29888b433 100644 --- a/tools/collada/collada.cpp +++ b/tools/collada/collada.cpp @@ -322,7 +322,7 @@ void Collada::_parse_image(XMLParser& parser) { String path = parser.get_attribute_value("source").strip_edges(); if (path.find("://")==-1 && path.is_rel_path()) { // path is relative to file being loaded, so convert to a resource path - image.path=Globals::get_singleton()->localize_path(state.local_path.get_base_dir()+"/"+path); + image.path=Globals::get_singleton()->localize_path(state.local_path.get_base_dir()+"/"+path.percent_decode()); } } else { @@ -338,7 +338,7 @@ void Collada::_parse_image(XMLParser& parser) { if (name=="init_from") { parser.read(); - String path = parser.get_node_data().strip_edges(); + String path = parser.get_node_data().strip_edges().percent_decode(); if (path.find("://")==-1 && path.is_rel_path()) { // path is relative to file being loaded, so convert to a resource path @@ -1388,8 +1388,11 @@ void Collada::_parse_morph_controller(XMLParser& parser, String p_id) { state.morph_controller_data_map[p_id]=MorphControllerData(); MorphControllerData &morphdata = state.morph_controller_data_map[p_id]; + print_line("morph source: "+parser.get_attribute_value("source")+" id: "+p_id); morphdata.mesh=_uri_to_id(parser.get_attribute_value("source")); + print_line("morph source2: "+morphdata.mesh); morphdata.mode=parser.get_attribute_value("method"); + printf("JJmorph: %p\n",&morphdata); String current_source; while(parser.read()==OK) { @@ -1680,6 +1683,10 @@ Collada::Node* Collada::_parse_visual_scene_node(XMLParser& parser) { } else if (state.idref_joints.has(name)) joint->sid=name; //kind of a cheat but.. + if (joint->sid!="") { + state.sid_to_node_map[joint->sid]=id; + } + node=joint; @@ -2240,6 +2247,8 @@ void Collada::_create_skeletons(Collada::Node **p_node) { Node *node = *p_node; + + if (node->type==Node::TYPE_JOINT) { // ohohohoohoo it's a joint node, time to work! @@ -2346,6 +2355,79 @@ void Collada::_merge_skeletons(VisualScene *p_vscene,Node *p_node) { } + +void Collada::_merge_skeletons2(VisualScene *p_vscene) { + + for (Map<String,SkinControllerData>::Element *E=state.skin_controller_data_map.front();E;E=E->next()) { + + SkinControllerData &cd=E->get(); + + NodeSkeleton *skeleton=NULL; + + for (Map<String,Transform>::Element *F=cd.bone_rest_map.front();F;F=F->next()) { + + String name; + + if (!state.sid_to_node_map.has(F->key())) { + continue; + } + + name = state.sid_to_node_map[F->key()]; + + if (!state.scene_map.has(name)) { + print_line("no foundie node for: "+name); + } + + ERR_CONTINUE( !state.scene_map.has(name) ); + + Node *node=state.scene_map[name]; + ERR_CONTINUE( node->type!=Node::TYPE_JOINT ); + if (node->type!=Node::TYPE_JOINT) + continue; + NodeSkeleton *sk=NULL; + + while(node && !sk) { + + if (node->type==Node::TYPE_SKELETON) { + sk=static_cast<NodeSkeleton*>(node); + } + node=node->parent; + } + ERR_CONTINUE( !sk ); + + if (!sk) + continue; //bleh + + if (!skeleton) { + skeleton=sk; + continue; + } + + if (skeleton!=sk) { + //whoa.. wtf, merge. + print_line("MERGED BONES!!"); + + //NodeSkeleton *merged = E->get(); + _remove_node(p_vscene,sk); + for(int i=0;i<sk->children.size();i++) { + + _joint_set_owner(sk->children[i],skeleton); + skeleton->children.push_back( sk->children[i] ); + sk->children[i]->parent=skeleton; + + + } + + sk->children.clear(); //take children from it + memdelete( sk ); + } + } + } + + + +} + bool Collada::_optimize_skeletons(VisualScene *p_vscene,Node *p_node) { Node *node=p_node; @@ -2537,6 +2619,9 @@ void Collada::_optimize() { _merge_skeletons(&vs,vs.root_nodes[i]); } + _merge_skeletons2(&vs); + + for(int i=0;i<vs.root_nodes.size();i++) { _optimize_skeletons(&vs,vs.root_nodes[i]); } diff --git a/tools/collada/collada.h b/tools/collada/collada.h index 69ed05beba..360f54ec05 100644 --- a/tools/collada/collada.h +++ b/tools/collada/collada.h @@ -544,6 +544,7 @@ public: Map<String,VisualScene> visual_scene_map; Map<String,Node*> scene_map; Set<String> idref_joints; + Map<String,String> sid_to_node_map; //Map<String,NodeJoint*> bone_map; Map<String,Transform> bone_rest_map; @@ -616,6 +617,7 @@ private: // private stuff 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); diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp index 649db5fc45..6e8cb987e9 100644 --- a/tools/editor/editor_import_export.cpp +++ b/tools/editor/editor_import_export.cpp @@ -439,8 +439,8 @@ bool EditorExportPlatformPC::_get(const StringName& p_name,Variant &r_ret) const void EditorExportPlatformPC::_get_property_list( List<PropertyInfo> *p_list) const { - p_list->push_back( PropertyInfo( Variant::STRING, "custom_binary/debug", PROPERTY_HINT_FILE,binary_extension)); - p_list->push_back( PropertyInfo( Variant::STRING, "custom_binary/release", PROPERTY_HINT_FILE,binary_extension)); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_binary/debug", PROPERTY_HINT_GLOBAL_FILE,binary_extension)); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_binary/release", PROPERTY_HINT_GLOBAL_FILE,binary_extension)); p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Single Exec.,Exec+Pack (.pck),Copy,Bundles (Optical)")); p_list->push_back( PropertyInfo( Variant::BOOL, "binary/64_bits")); } @@ -1009,15 +1009,15 @@ Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; if (use64) { if (p_debug) - exe_path+=custom_debug_binary!=""?custom_debug_binary:debug_binary64; + exe_path=custom_debug_binary!=""?custom_debug_binary:exe_path+debug_binary64; else - exe_path+=custom_release_binary!=""?custom_release_binary:release_binary64; + exe_path=custom_release_binary!=""?custom_release_binary:exe_path+release_binary64; } else { if (p_debug) - exe_path+=custom_debug_binary!=""?custom_debug_binary:debug_binary32; + exe_path=custom_debug_binary!=""?custom_debug_binary:exe_path+debug_binary32; else - exe_path+=custom_release_binary!=""?custom_release_binary:release_binary32; + exe_path=custom_release_binary!=""?custom_release_binary:exe_path+release_binary32; } @@ -1032,7 +1032,7 @@ Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, if (!dst) { EditorNode::add_io_error("Can't copy executable file to:\n "+p_path); - return ERR_FILE_CANT_READ; + return ERR_FILE_CANT_WRITE; } uint8_t buff[32768]; @@ -1061,7 +1061,7 @@ Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, if (!dst) { EditorNode::add_io_error("Can't write data pack to:\n "+p_path); - return ERR_FILE_CANT_READ; + return ERR_FILE_CANT_WRITE; } } diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index a6243df69d..4e2e5118dd 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -41,6 +41,7 @@ #include "scene/resources/packed_scene.h" #include "editor_settings.h" #include "io_plugins/editor_import_collada.h" +#include "io_plugins/editor_scene_importer_fbxconv.h" #include "globals.h" #include <stdio.h> #include "object_type_db.h" @@ -78,6 +79,7 @@ #include "plugins/path_editor_plugin.h" #include "plugins/rich_text_editor_plugin.h" #include "plugins/collision_polygon_editor_plugin.h" +#include "plugins/collision_polygon_2d_editor_plugin.h" #include "plugins/script_editor_plugin.h" #include "plugins/path_2d_editor_plugin.h" #include "plugins/particles_editor_plugin.h" @@ -4048,6 +4050,8 @@ EditorNode::EditorNode() { Ref<EditorSceneImportPlugin> _scene_import = memnew(EditorSceneImportPlugin(this) ); Ref<EditorSceneImporterCollada> _collada_import = memnew( EditorSceneImporterCollada); _scene_import->add_importer(_collada_import); + Ref<EditorSceneImporterFBXConv> _fbxconv_import = memnew( EditorSceneImporterFBXConv); + _scene_import->add_importer(_fbxconv_import); editor_import_export->add_import_plugin( _scene_import); editor_import_export->add_import_plugin( Ref<EditorSceneAnimationImportPlugin>( memnew(EditorSceneAnimationImportPlugin(this)))); editor_import_export->add_import_plugin( Ref<EditorMeshImportPlugin>( memnew(EditorMeshImportPlugin(this)))); @@ -4083,6 +4087,7 @@ EditorNode::EditorNode() { add_editor_plugin( memnew( ItemListEditorPlugin(this) ) ); add_editor_plugin( memnew( RichTextEditorPlugin(this) ) ); add_editor_plugin( memnew( CollisionPolygonEditorPlugin(this) ) ); + add_editor_plugin( memnew( CollisionPolygon2DEditorPlugin(this) ) ); add_editor_plugin( memnew( TileSetEditorPlugin(this) ) ); add_editor_plugin( memnew( TileMapEditorPlugin(this) ) ); add_editor_plugin( memnew( SpriteFramesEditorPlugin(this) ) ); diff --git a/tools/editor/editor_settings.cpp b/tools/editor/editor_settings.cpp index 0b896a725e..6f1a24d7e8 100644 --- a/tools/editor/editor_settings.cpp +++ b/tools/editor/editor_settings.cpp @@ -386,6 +386,10 @@ void EditorSettings::_load_defaults() { set("global/font",""); hints["global/font"]=PropertyInfo(Variant::STRING,"global/font",PROPERTY_HINT_GLOBAL_FILE,"*.fnt"); + set("global/default_project_path",""); + hints["global/default_project_path"]=PropertyInfo(Variant::STRING,"global/default_project_path",PROPERTY_HINT_GLOBAL_DIR); + set("global/default_project_export_path",""); + hints["global/default_project_export_path"]=PropertyInfo(Variant::STRING,"global/default_project_export_path",PROPERTY_HINT_GLOBAL_DIR); set("text_editor/background_color",Color::html("3b000000")); set("text_editor/text_color",Color::html("aaaaaa")); @@ -402,8 +406,8 @@ void EditorSettings::_load_defaults() { set("text_editor/create_signal_callbacks",true); set("text_editor/autosave_interval_seconds",60); set("text_editor/font",""); - set("text_editor/auto_brace_complete", false); hints["text_editor/font"]=PropertyInfo(Variant::STRING,"text_editor/font",PROPERTY_HINT_GLOBAL_FILE,"*.fnt"); + set("text_editor/auto_brace_complete", false); set("3d_editor/default_fov",45.0); diff --git a/tools/editor/icons/icon_instance_options.png b/tools/editor/icons/icon_instance_options.png Binary files differnew file mode 100644 index 0000000000..2d3e98b2ea --- /dev/null +++ b/tools/editor/icons/icon_instance_options.png diff --git a/tools/editor/icons/icon_light_map.png b/tools/editor/icons/icon_light_map.png Binary files differnew file mode 100644 index 0000000000..96d3f6e11c --- /dev/null +++ b/tools/editor/icons/icon_light_map.png diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp index a0f1626b03..9bee47ca66 100644 --- a/tools/editor/io_plugins/editor_import_collada.cpp +++ b/tools/editor/io_plugins/editor_import_collada.cpp @@ -82,8 +82,8 @@ struct ColladaImport { Error _create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data); Error load(const String& p_path, int p_flags, bool p_force_make_tangents=false); void _fix_param_animation_tracks(); - void create_animation(int p_clip=-1); - void create_animations(); + void create_animation(int p_clip,bool p_make_tracks_in_all_bones); + void create_animations(bool p_make_tracks_in_all_bones); Set<String> tracks_in_clips; Vector<String> missing_textures; @@ -564,6 +564,7 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co bool local_xform_mirror=p_local_xform.basis.determinant() < 0; if (p_morph_data) { + //add morphie target ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA ); String mt = p_morph_data->targets["MORPH_TARGET"]; @@ -1088,7 +1089,7 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co DVector<Vector3>::Write uv2arrayw = uv2array.write(); for(int k=0;k<vlen;k++) { - uv2arrayw[k]=vertex_array[k].uv; + uv2arrayw[k]=vertex_array[k].uv2; } uv2arrayw = DVector<Vector3>::Write(); @@ -1478,8 +1479,11 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { Transform apply_xform; Vector<int> bone_remap; + print_line("mesh: "+String(mi->get_name())); + if (ng->controller) { + print_line("has controller"); if (collada.state.skin_controller_data_map.has(ng->source)) { @@ -1524,13 +1528,20 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { for(int i=0;i<bone_remap.size();i++) { String str = joint_src->sarray[i]; + if (!bone_remap_map.has(str)) { + print_line("bone not found for remap: "+str); + print_line("in skeleton: "+skname); + } ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA ); bone_remap[i]=bone_remap_map[str]; } } else if (collada.state.morph_controller_data_map.has(ng->source)) { + print_line("is morph "+ng->source); //it's a morph!! - morph = &collada.state.morph_controller_data_map[meshid]; + morph = &collada.state.morph_controller_data_map[ng->source]; meshid=morph->mesh; + printf("KKmorph: %p\n",morph); + print_line("morph mshid: "+meshid); } else { ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!"); ERR_FAIL_V( ERR_INVALID_DATA ); @@ -1705,7 +1716,7 @@ void ColladaImport::_fix_param_animation_tracks() { } -void ColladaImport::create_animations() { +void ColladaImport::create_animations(bool p_make_tracks_in_all_bones) { print_line("-=-=-=-=-PRE CA"); _fix_param_animation_tracks(); @@ -1737,14 +1748,14 @@ void ColladaImport::create_animations() { } - create_animation(); + create_animation(-1,p_make_tracks_in_all_bones); print_line("clipcount: "+itos(collada.state.animation_clips.size())); for(int i=0;i<collada.state.animation_clips.size();i++) - create_animation(i); + create_animation(i,p_make_tracks_in_all_bones); } -void ColladaImport::create_animation(int p_clip) { +void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones) { Ref<Animation> animation = Ref<Animation>( memnew( Animation )); @@ -1980,46 +1991,48 @@ void ColladaImport::create_animation(int p_clip) { } + if (p_make_tracks_in_all_bones) { - //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! - for(Map<String,bool>::Element *E=bones_with_animation.front();E;E=E->next()) { + //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! + for(Map<String,bool>::Element *E=bones_with_animation.front();E;E=E->next()) { - if (E->get()) - continue; + if (E->get()) + continue; - //print_line("BONE LACKS ANIM: "+E->key()); + //print_line("BONE LACKS ANIM: "+E->key()); - NodeMap &nm = node_map[E->key()]; - String path = scene->get_path_to(nm.node); - ERR_CONTINUE( nm.bone <0 ); - Skeleton *sk = static_cast<Skeleton*>(nm.node); - String name = sk->get_bone_name(nm.bone); - path=path+":"+name; + NodeMap &nm = node_map[E->key()]; + String path = scene->get_path_to(nm.node); + ERR_CONTINUE( nm.bone <0 ); + Skeleton *sk = static_cast<Skeleton*>(nm.node); + String name = sk->get_bone_name(nm.bone); + path=path+":"+name; - Collada::Node *cn = collada.state.scene_map[E->key()]; - if (cn->ignore_anim) { - print_line("warning, ignoring animation on node: "+path); - continue; - } + Collada::Node *cn = collada.state.scene_map[E->key()]; + if (cn->ignore_anim) { + print_line("warning, ignoring animation on node: "+path); + continue; + } - animation->add_track(Animation::TYPE_TRANSFORM); - int track = animation->get_track_count() -1; - animation->track_set_path( track , path ); + animation->add_track(Animation::TYPE_TRANSFORM); + int track = animation->get_track_count() -1; + animation->track_set_path( track , path ); - Transform xform = cn->compute_transform(collada); - xform = collada.fix_transform(xform) * cn->post_transform; + Transform xform = cn->compute_transform(collada); + xform = collada.fix_transform(xform) * cn->post_transform; - xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; + xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; - Quat q = xform.basis; - q.normalize(); - Vector3 s = xform.basis.get_scale(); - Vector3 l = xform.origin; + Quat q = xform.basis; + q.normalize(); + Vector3 s = xform.basis.get_scale(); + Vector3 l = xform.origin; - animation->transform_track_insert_key(track,0,l,q,s); + animation->transform_track_insert_key(track,0,l,q,s); - tracks_found=true; + tracks_found=true; + } } @@ -2149,7 +2162,7 @@ Node* EditorSceneImporterCollada::import_scene(const String& p_path, uint32_t p_ if (p_flags&IMPORT_ANIMATION) { - state.create_animations(); + state.create_animations(p_flags&IMPORT_ANIMATION_FORCE_TRACKS_IN_ALL_BONES); AnimationPlayer *ap = memnew( AnimationPlayer ); for(int i=0;i<state.animations.size();i++) { String name; @@ -2188,7 +2201,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path ERR_FAIL_COND_V(err!=OK,RES()); - state.create_animations(); + state.create_animations(p_flags&EditorSceneImporter::IMPORT_ANIMATION_FORCE_TRACKS_IN_ALL_BONES); if (state.scene) memdelete(state.scene); diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp index 0b7ffd55f0..2482728c87 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp @@ -35,6 +35,7 @@ #include "scene/animation/animation_player.h" #include "io/resource_saver.h" #include "scene/3d/mesh_instance.h" +#include "scene/3d/navigation.h" #include "scene/3d/room_instance.h" #include "scene/3d/body_shape.h" #include "scene/3d/physics_body.h" @@ -176,6 +177,7 @@ static const char *anim_flag_names[]={ "Detect Loop (-loop,-cycle)", "Keep Value Tracks", "Optimize", + "Force Tracks in All Bones", NULL }; @@ -183,6 +185,7 @@ static const char *anim_flag_descript[]={ "Set loop flag for animation names that\ncontain 'cycle' or 'loop' in the name.", "When merging an existing aimation,\nkeep the user-created value-tracks.", "Remove redundant keyframes in\n transform tacks.", + "Some exporters will rely on default pose for some bones.\nThis forces those bones to have at least one animation key.", NULL }; @@ -404,6 +407,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) { rim->add_source(EditorImportPlugin::validate_source_path(import_path->get_text())); rim->set_option("flags",flags); + print_line("GET FLAGS: "+itos(texture_options->get_flags())); rim->set_option("texture_flags",texture_options->get_flags()); rim->set_option("texture_format",texture_options->get_format()); rim->set_option("texture_quality",texture_options->get_quality()); @@ -414,7 +418,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) { List<String> missing; Error err = plugin->import1(rim,&scene,&missing); - if (err) { + if (err || !scene) { error_dialog->set_text("Error importing scene."); error_dialog->popup_centered(Size2(200,100)); @@ -637,6 +641,7 @@ const EditorSceneImportDialog::FlagInfo EditorSceneImportDialog::scene_flag_name {EditorSceneImportPlugin::SCENE_FLAG_DETECT_ALPHA,"Materials","Set Alpha in Materials (-alpha)",true}, {EditorSceneImportPlugin::SCENE_FLAG_DETECT_VCOLOR,"Materials","Set Vert. Color in Materials (-vcol)",true}, {EditorSceneImportPlugin::SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES,"Actions","SRGB->Linear Of Diffuse Textures",false}, + {EditorSceneImportPlugin::SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS,"Actions","Set Material Lightmap to UV2 if Tex2Array Exists",true}, {EditorSceneImportPlugin::SCENE_FLAG_CREATE_COLLISIONS,"Create","Create Collisions (-col},-colonly)",true}, {EditorSceneImportPlugin::SCENE_FLAG_CREATE_PORTALS,"Create","Create Portals (-portal)",true}, {EditorSceneImportPlugin::SCENE_FLAG_CREATE_ROOMS,"Create","Create Rooms (-room)",true}, @@ -806,7 +811,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce animation_options = memnew( EditorImportAnimationOptions ); ovb->add_child(animation_options); animation_options->set_v_size_flags(SIZE_EXPAND_FILL); - animation_options->set_flags(EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP|EditorSceneAnimationImportPlugin::ANIMATION_KEEP_VALUE_TRACKS|EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE); + animation_options->set_flags(EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP|EditorSceneAnimationImportPlugin::ANIMATION_KEEP_VALUE_TRACKS|EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE|EditorSceneAnimationImportPlugin::ANIMATION_FORCE_TRACKS_IN_ALL_BONES); confirm_import = memnew( ConfirmationDialog ); @@ -873,6 +878,8 @@ static bool _teststr(const String& p_what,const String& p_str) { return true; if (p_what.to_lower().ends_with("-"+p_str)) //collada only supports "_" and "-" besides letters return true; + if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters + return true; return false; } @@ -882,6 +889,8 @@ static String _fixstr(const String& p_what,const String& p_str) { return p_what.replace("$"+p_str,""); if (p_what.to_lower().ends_with("-"+p_str)) //collada only supports "_" and "-" besides letters return p_what.substr(0,p_what.length()-(p_str.length()+1)); + if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters + return p_what.substr(0,p_what.length()-(p_str.length()+1)); return p_what; } @@ -1033,7 +1042,7 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> } - if (p_flags&(SCENE_FLAG_DETECT_ALPHA|SCENE_FLAG_DETECT_VCOLOR) && p_node->cast_to<MeshInstance>()) { + if (p_flags&(SCENE_FLAG_DETECT_ALPHA|SCENE_FLAG_DETECT_VCOLOR|SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS) && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); @@ -1058,6 +1067,10 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> mat->set_name(_fixstr(mat->get_name(),"vcol")); } + if (p_flags&SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS && m->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV2) { + mat->set_flag(Material::FLAG_LIGHTMAP_ON_UV2,true); + } + } } } @@ -1221,13 +1234,34 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> col->set_name("col"); p_node->add_child(col); - - StaticBody *sb = col->cast_to<StaticBody>(); + StaticBody *sb=col->cast_to<StaticBody>(); CollisionShape *colshape = memnew( CollisionShape); colshape->set_shape(sb->get_shape(0)); colshape->set_name("shape"); - sb->add_child(colshape); + col->add_child(colshape); colshape->set_owner(p_node->get_owner()); + sb->set_owner(p_node->get_owner()); + + } else if (p_flags&SCENE_FLAG_CREATE_NAVMESH &&_teststr(name,"navmesh") && p_node->cast_to<MeshInstance>()) { + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + Ref<Mesh> mesh=mi->get_mesh(); + ERR_FAIL_COND_V(mesh.is_null(),NULL); + NavigationMeshInstance *nmi = memnew( NavigationMeshInstance ); + + + nmi->set_name(_fixstr(name,"navmesh")); + Ref<NavigationMesh> nmesh = memnew( NavigationMesh); + nmesh->create_from_mesh(mesh); + nmi->set_navigation_mesh(nmesh); + nmi->cast_to<Spatial>()->set_transform(mi->get_transform()); + p_node->replace_by(nmi); + memdelete(p_node); + p_node=nmi; } else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) { @@ -1514,22 +1548,74 @@ void EditorSceneImportPlugin::_merge_existing_node(Node *p_node,Node *p_imported skeleton_node->add_bone(skeleton_imported->get_bone_name(i)); skeleton_node->set_bone_parent(i,skeleton_imported->get_bone_parent(i)); skeleton_node->set_bone_rest(i,skeleton_imported->get_bone_rest(i)); - skeleton_node->set_bone_pose(i,skeleton_imported->get_bone_pose(i)); + //skeleton_node->set_bone_pose(i,skeleton_imported->get_bone_pose(i)); // not in a scene, will throw errors } - } else if (p_node->get_type()=="AnimationPlayer") { + } + else if (p_node->get_type() == "AnimationPlayer") { //for paths, overwrite path - - AnimationPlayer *aplayer_imported =imported_node->cast_to<AnimationPlayer>(); - AnimationPlayer *aplayer_node =p_node->cast_to<AnimationPlayer>(); + AnimationPlayer *aplayer_imported = imported_node->cast_to<AnimationPlayer>(); + AnimationPlayer *aplayer_node = p_node->cast_to<AnimationPlayer>(); //use imported bones, obviously List<StringName> anims; + List<StringName> existing_anims; aplayer_imported->get_animation_list(&anims); - //use imported animations, could merge some stuff though - for (List<StringName>::Element *E=anims.front();E;E=E->next()) { + aplayer_node->get_animation_list(&existing_anims); + + //use imported animations + for (List<StringName>::Element *N = anims.front(); N; N = N->next()) { + Ref<Animation> candidate = aplayer_imported->get_animation(N->get()); - aplayer_node->add_animation(E->get(),aplayer_imported->get_animation(E->get())); + if (aplayer_node->has_animation(N->get())) { + + Ref<Animation> found = aplayer_node->get_animation(N->get()); + + candidate->set_loop(found->has_loop()); + candidate->set_step(found->get_step()); + + //For each track candidate + for (int i = 0; i < candidate->get_track_count(); i++) { + + NodePath track_path = candidate->track_get_path(i); + // For each track existing + for (int x = 0; x < found->get_track_count(); x++) { + + NodePath path_to_compare = found->track_get_path(x); + + if (track_path.hash() == path_to_compare.hash() && candidate->track_get_type(x) == found->track_get_type(i)) { + + //Tracks matches + if (candidate->track_get_interpolation_type(i) != found->track_get_interpolation_type(x)) + candidate->track_set_interpolation_type(i, found->track_get_interpolation_type(x)); + if (candidate->track_get_type(i) == Animation::TYPE_VALUE && candidate->value_track_is_continuous(i) != found->value_track_is_continuous(x)) + candidate->value_track_set_continuous(i, found->value_track_is_continuous(x)); + + //Key transitions might have changed, but the animation remained unchanged + if (candidate->track_get_key_count(i) == found->track_get_key_count(x)) { + for (int k = 0; k < candidate->track_get_key_count(i); k++) { + + if (candidate->track_get_key_transition(i, k) != found->track_get_key_transition(x, k)) + candidate->track_set_key_transition(i, k, found->track_get_key_transition(x, k)); + } + } + + } + + } + } + + // Append function callbacks and values + for (int x = 0; x < found->get_track_count(); x++) { + if (found->track_get_type(x) == Animation::TYPE_METHOD || found->track_get_type(x) == Animation::TYPE_VALUE) + candidate->add_track(found->track_get_type(x), candidate->get_track_count()); + + for (int k = 0; k < found->track_get_key_count(x); k++) + candidate->track_insert_key(x, found->track_get_key_time(x, k), found->track_get_key_value(x, k), found->track_get_key_transition(x, k)); + } + } + + aplayer_node->add_animation(N->get(), candidate); } } else if (p_node->get_type()=="CollisionShape") { @@ -1737,10 +1823,12 @@ Error EditorSceneImportPlugin::import1(const Ref<ResourceImportMetadata>& p_from import_flags|=EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP; if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE) import_flags|=EditorSceneImporter::IMPORT_ANIMATION_OPTIMIZE; + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_TRACKS_IN_ALL_BONES) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION_FORCE_TRACKS_IN_ALL_BONES; if (scene_flags&SCENE_FLAG_IMPORT_ANIMATIONS) import_flags|=EditorSceneImporter::IMPORT_ANIMATION; -// if (scene_flags&SCENE_FLAG_FAIL_ON_MISSING_IMAGES) -// import_flags|=EditorSceneImporter::IMPORT_FAIL_ON_MISSING_DEPENDENCIES; + //if (scene_flags&SCENE_FLAG_FAIL_ON_MISSING_IMAGES) + // import_flags|=EditorSceneImporter::IMPORT_FAIL_ON_MISSING_DEPENDENCIES; if (scene_flags&SCENE_FLAG_GENERATE_TANGENT_ARRAYS) import_flags|=EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS; @@ -1866,8 +1954,7 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); print_line("flags: "+itos(image_flags)); uint32_t flags = image_flags; - if (E->get()) - flags|=EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_TO_LINEAR; + imd->set_option("flags",flags); imd->set_option("format",image_format); imd->set_option("quality",image_quality); diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h index 928fff2afb..3b39f0c962 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.h +++ b/tools/editor/io_plugins/editor_scene_import_plugin.h @@ -58,8 +58,9 @@ public: IMPORT_ANIMATION=2, IMPORT_ANIMATION_DETECT_LOOP=4, IMPORT_ANIMATION_OPTIMIZE=8, - IMPORT_GENERATE_TANGENT_ARRAYS=16, - IMPORT_FAIL_ON_MISSING_DEPENDENCIES=128 + IMPORT_ANIMATION_FORCE_TRACKS_IN_ALL_BONES=16, + IMPORT_GENERATE_TANGENT_ARRAYS=256, + IMPORT_FAIL_ON_MISSING_DEPENDENCIES=512 }; @@ -130,6 +131,7 @@ public: SCENE_FLAG_COMPRESS_GEOMETRY=1<<26, SCENE_FLAG_GENERATE_TANGENT_ARRAYS=1<<27, SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES=1<<28, + SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS=1<<29, }; @@ -160,7 +162,8 @@ public: ANIMATION_DETECT_LOOP=1, ANIMATION_KEEP_VALUE_TRACKS=2, - ANIMATION_OPTIMIZE=4 + ANIMATION_OPTIMIZE=4, + ANIMATION_FORCE_TRACKS_IN_ALL_BONES=8 }; virtual String get_name() const; diff --git a/tools/editor/io_plugins/editor_scene_importer_fbxconv.cpp b/tools/editor/io_plugins/editor_scene_importer_fbxconv.cpp new file mode 100644 index 0000000000..0c388b91ca --- /dev/null +++ b/tools/editor/io_plugins/editor_scene_importer_fbxconv.cpp @@ -0,0 +1,1106 @@ +#include "editor_scene_importer_fbxconv.h" +#include "os/file_access.h" +#include "os/os.h" +#include "tools/editor/editor_settings.h" +#include "scene/3d/mesh_instance.h" +#include "scene/animation/animation_player.h" + + +String EditorSceneImporterFBXConv::_id(const String& p_id) const { + + return p_id.replace(":","_").replace("/","_"); +} + +uint32_t EditorSceneImporterFBXConv::get_import_flags() const { + + return IMPORT_SCENE|IMPORT_ANIMATION; +} +void EditorSceneImporterFBXConv::get_extensions(List<String> *r_extensions) const{ + + r_extensions->push_back("fbx"); + r_extensions->push_back("g3dj"); +} + + +Color EditorSceneImporterFBXConv::_get_color(const Array& a) { + + if (a.size()<3) + return Color(); + Color c; + c.r=a[0]; + c.g=a[1]; + c.b=a[2]; + if (a.size()>=4) + c.a=a[3]; + return c; + +} + +Transform EditorSceneImporterFBXConv::_get_transform_mixed(const Dictionary& d,const Dictionary& dbase) { + + + + + Array translation; + + if (d.has("translation")) + translation=d["translation"]; + else if (dbase.has("translation")) + translation=dbase["translation"]; + + Array rotation; + + if (d.has("rotation")) + rotation=d["rotation"]; + else if (dbase.has("rotation")) + rotation=dbase["rotation"]; + + Array scale; + + if (d.has("scale")) + scale=d["scale"]; + else if (dbase.has("scale")) + scale=dbase["scale"]; + + Transform t; + + + if (translation.size()) { + Array tr = translation; + if (tr.size()>=3) { + t.origin.x=tr[0]; + t.origin.y=tr[1]; + t.origin.z=tr[2]; + } + } + + if (rotation.size()) { + + Array r = rotation; + if (r.size()>=4) { + + Quat q; + q.x = r[0]; + q.y = r[1]; + q.z = r[2]; + q.w = r[3]; + t.basis=Matrix3(q); + } + } + + + if (scale.size()) { + + Array sc = scale; + if (sc.size()>=3) { + Vector3 s; + s.x=sc[0]; + s.y=sc[1]; + s.z=sc[2]; + t.basis.scale(s); + } + } + + return t; + + +} + +Transform EditorSceneImporterFBXConv::_get_transform(const Dictionary& d) { + + + Transform t; + + if (d.has("translation")) { + Array tr = d["translation"]; + if (tr.size()>=3) { + t.origin.x=tr[0]; + t.origin.y=tr[1]; + t.origin.z=tr[2]; + } + } + + if (d.has("rotation")) { + + Array r = d["rotation"]; + if (r.size()>=4) { + + Quat q; + q.x = r[0]; + q.y = r[1]; + q.z = r[2]; + q.w = r[3]; + t.basis=Matrix3(q); + } + } + + + if (d.has("scale")) { + + Array sc = d["scale"]; + if (sc.size()>=3) { + Vector3 s; + s.x=sc[0]; + s.y=sc[1]; + s.z=sc[2]; + t.basis.scale(s); + } + } + + return t; +} + + +void EditorSceneImporterFBXConv::_detect_bones_in_nodes(State& state,const Array& p_nodes) { + + + for(int i=0;i<p_nodes.size();i++) { + + Dictionary d = p_nodes[i]; + if (d.has("isBone") && bool(d["isBone"])) { + + String bone_name=_id(d["id"]); + print_line("IS BONE: "+bone_name); + if (!state.bones.has(bone_name)) { + state.bones.insert(bone_name,BoneInfo()); + } + + if (!state.bones[bone_name].has_rest) { + state.bones[bone_name].rest=_get_transform(d).affine_inverse(); + } + + state.bones[bone_name].node=d; + + //state.bones[name].rest=_get_transform(b); + } + + if (d.has("parts")) { + + Array parts=d["parts"]; + for(int j=0;j<parts.size();j++) { + + Dictionary p=parts[j]; + if (p.has("bones")) { + Array bones=p["bones"]; + //omfg + for(int k=0;k<bones.size();k++) { + + Dictionary b = bones[k]; + if (b.has("node")) { + + String name = _id(b["node"]); + if (!state.bones.has(name)) { + state.bones.insert(name,BoneInfo()); + } + + state.bones[name].rest=_get_transform(b); + state.bones[name].has_rest=true; + } + } + } + + } + } + + if (d.has("children")) { + + _detect_bones_in_nodes(state,d["children"]); + } + } + +} + +void EditorSceneImporterFBXConv::_parse_skeletons(const String& p_name,State& state, const Array &p_nodes, Skeleton *p_skeleton,int p_parent) { + + + + for(int i=0;i<p_nodes.size();i++) { + + + Dictionary d = p_nodes[i]; + int bone_idx=-1; + String id; + Skeleton* skeleton=p_skeleton; + if (d.has("id")) { + + id=_id(d["id"]); + if (state.bones.has(id)) { + //BONER + if (!skeleton) { + skeleton=memnew( Skeleton ); + state.skeletons[id]=skeleton; + } + bone_idx = skeleton->get_bone_count(); + skeleton->add_bone(id); + skeleton->set_bone_parent(bone_idx,p_parent); + skeleton->set_bone_rest(bone_idx,state.bones[id].rest); + state.bones[id].skeleton=skeleton; + } + } + + if (d.has("children")) { + + _parse_skeletons(id,state,d["children"],skeleton,bone_idx); + } + } + +} + +void EditorSceneImporterFBXConv::_detect_bones(State& state) { + //This format should mark when a node is a bone, + //which is the only thing that Collada does right. + //think about others implementing a parser. + //Just _one_ string and you avoid loads of lines of code to other people. + + for(int i=0;i<state.animations.size();i++) { + + Dictionary an = state.animations[i]; + if (an.has("bones")) { + + Array bo=an["bones"]; + for(int j=0;j<bo.size();j++) { + + Dictionary b=bo[j]; + if (b.has("boneId")) { + + String id = b["boneId"]; + if (!state.bones.has(id)) { + state.bones.insert(id,BoneInfo()); + } + state.bones[id].has_anim_chan=true; //used in anim + + + } + } + } + } + + _detect_bones_in_nodes(state,state.nodes); + _parse_skeletons("",state,state.nodes,NULL,-1); + + print_line("found bones: "+itos(state.bones.size())); + print_line("found skeletons: "+itos(state.skeletons.size())); +} + +Error EditorSceneImporterFBXConv::_parse_bones(State& state,const Array &p_bones,Skeleton* p_skeleton) { + + + + return OK; +} + + +void EditorSceneImporterFBXConv::_add_surface(State& state,Ref<Mesh>& m,const Dictionary &part) { + + if (part.has("meshpartid")) { + + String id = part["meshpartid"]; + ERR_FAIL_COND(!state.surface_cache.has(id)); + + + Ref<Material> mat; + if (part.has("materialid")) { + String matid=part["materialid"]; + if (state.material_cache.has(matid)) { + mat=state.material_cache[matid]; + } + } + int idx = m->get_surface_count(); + + Array array = state.surface_cache[id].array; + DVector<float> indices = array[Mesh::ARRAY_BONES]; + if (indices.size() && part.has("bones")) { + + + Map<int,int> index_map; + + Array bones=part["bones"]; + + for(int i=0;i<bones.size();i++) { + + Dictionary bone=bones[i]; + String name=_id(bone["node"]); + + if (state.bones.has(name)) { + int idx=state.bones[name].skeleton->find_bone(name); + if (idx==-1) + idx=0; + index_map[i]=idx; + } + } + + + + int ilen=indices.size(); + { + DVector<float>::Write iw=indices.write(); + for(int j=0;j<ilen;j++) { + int b = iw[j]; + ERR_CONTINUE(!index_map.has(b)); + b=index_map[b]; + iw[j]=b; + } + } + + array[Mesh::ARRAY_BONES]=indices; + + + } + + m->add_surface(state.surface_cache[id].primitive,array); + m->surface_set_material(idx,mat); + m->surface_set_name(idx,id); + } + +} + +Error EditorSceneImporterFBXConv::_parse_nodes(State& state,const Array &p_nodes,Node* p_base) { + + for(int i=0;i<p_nodes.size();i++) { + + Dictionary n = p_nodes[i]; + Spatial *node=NULL; + bool skip=false; + + String id; + if (n.has("id")) { + id=_id(n["id"]); + } + + print_line("ID: "+id); + + if (state.skeletons.has(id)) { + + Skeleton *skeleton = state.skeletons[id]; + node=skeleton; + skeleton->localize_rests(); + print_line("IS SKELETON! "); + } else if (state.bones.has(id)) { + if (p_base) + node=p_base->cast_to<Spatial>(); + if (!state.bones[id].has_anim_chan) { + print_line("no has anim "+id); + } + skip=true; + } else if (n.has("parts")) { + //is a mesh + MeshInstance *mesh = memnew( MeshInstance ); + node=mesh; + + Array parts=n["parts"]; + String long_identifier; + for(int j=0;j<parts.size();j++) { + + Dictionary part=parts[j]; + if (part.has("meshpartid")) { + String partid=part["meshpartid"]; + long_identifier+=partid; + } + } + + Ref<Mesh> m; + + if (state.mesh_cache.has(long_identifier)) { + m=state.mesh_cache[long_identifier]; + } else { + m = Ref<Mesh>( memnew( Mesh ) ); + + //and parts are surfaces + for(int j=0;j<parts.size();j++) { + + Dictionary part=parts[j]; + if (part.has("meshpartid")) { + _add_surface(state,m,part); + } + } + + + state.mesh_cache[long_identifier]=m; + } + + mesh->set_mesh(m); + } + + if (!skip) { + + if (!node) { + node = memnew( Spatial ); + } + + node->set_name(id); + node->set_transform(_get_transform(n)); + p_base->add_child(node); + node->set_owner(state.scene); + } + + + if (n.has("children")) { + Error err = _parse_nodes(state,n["children"],node); + if (err) + return err; + } + } + + return OK; +} + + +void EditorSceneImporterFBXConv::_parse_materials(State& state) { + + for(int i=0;i<state.materials.size();i++) { + + Dictionary material = state.materials[i]; + + ERR_CONTINUE(!material.has("id")); + String id = _id(material["id"]); + + Ref<FixedMaterial> mat = memnew( FixedMaterial ); + + if (material.has("diffuse")) { + mat->set_parameter(FixedMaterial::PARAM_DIFFUSE,_get_color(material["diffuse"])); + } + + if (material.has("specular")) { + mat->set_parameter(FixedMaterial::PARAM_SPECULAR,_get_color(material["specular"])); + } + + if (material.has("emissive")) { + mat->set_parameter(FixedMaterial::PARAM_EMISSION,_get_color(material["emissive"])); + } + + if (material.has("shininess")) { + float exp = material["shininess"]; + mat->set_parameter(FixedMaterial::PARAM_SPECULAR_EXP,exp); + } + + if (material.has("opacity")) { + Color c = mat->get_parameter(FixedMaterial::PARAM_DIFFUSE); + c.a=material["opacity"]; + mat->set_parameter(FixedMaterial::PARAM_DIFFUSE,c); + } + + + if (material.has("textures")) { + + Array textures = material["textures"]; + for(int j=0;j<textures.size();j++) { + + Dictionary texture=textures[j]; + Ref<Texture> tex; + if (texture.has("filename")) { + + + String filename=texture["filename"]; + String path=state.base_path+"/"+filename.replace("\\","/"); + if (state.texture_cache.has(path)) { + tex=state.texture_cache[path]; + } else { + tex = ResourceLoader::load(path,"ImageTexture"); + if (tex.is_null()) { + if (state.missing_deps) + state.missing_deps->push_back(path); + } + state.texture_cache[path]=tex; //add anyway + } + } + + if (tex.is_valid() && texture.has("type")) { + + String type=texture["type"]; + if (type=="DIFFUSE") + mat->set_texture(FixedMaterial::PARAM_DIFFUSE,tex); + else if (type=="SPECULAR") + mat->set_texture(FixedMaterial::PARAM_SPECULAR,tex); + else if (type=="SHININESS") + mat->set_texture(FixedMaterial::PARAM_SPECULAR_EXP,tex); + else if (type=="NORMAL") + mat->set_texture(FixedMaterial::PARAM_NORMAL,tex); + else if (type=="EMISSIVE") + mat->set_texture(FixedMaterial::PARAM_EMISSION,tex); + } + + } + } + + state.material_cache[id]=mat; + + } + +} + +void EditorSceneImporterFBXConv::_parse_surfaces(State& state) { + + for(int i=0;i<state.meshes.size();i++) { + + Dictionary mesh = state.meshes[i]; + + ERR_CONTINUE(!mesh.has("attributes")); + ERR_CONTINUE(!mesh.has("vertices")); + ERR_CONTINUE(!mesh.has("parts")); + + print_line("MESH #"+itos(i)); + + Array attrlist=mesh["attributes"]; + Array vertices=mesh["vertices"]; + bool exists[Mesh::ARRAY_MAX]; + int ofs[Mesh::ARRAY_MAX]; + int weight_max=0; + int binormal_ofs=-1; + int weight_ofs[4]; + + for(int j=0;j<Mesh::ARRAY_MAX;j++) { + exists[j]=false; + ofs[j]=0; + } + exists[Mesh::ARRAY_INDEX]=true; + float stride=0; + + for(int j=0;j<attrlist.size();j++) { + + String attr=attrlist[j]; + if (attr=="POSITION") { + exists[Mesh::ARRAY_VERTEX]=true; + ofs[Mesh::ARRAY_VERTEX]=stride; + stride+=3; + } else if (attr=="NORMAL") { + exists[Mesh::ARRAY_NORMAL]=true; + ofs[Mesh::ARRAY_NORMAL]=stride; + stride+=3; + } else if (attr=="COLOR") { + exists[Mesh::ARRAY_COLOR]=true; + ofs[Mesh::ARRAY_COLOR]=stride; + stride+=4; + } else if (attr=="COLORPACKED") { + stride+=1; //ignore + } else if (attr=="TANGENT") { + exists[Mesh::ARRAY_TANGENT]=true; + ofs[Mesh::ARRAY_TANGENT]=stride; + stride+=3; + } else if (attr=="BINORMAL") { + binormal_ofs=stride; + stride+=3; + } else if (attr=="TEXCOORD0") { + exists[Mesh::ARRAY_TEX_UV]=true; + ofs[Mesh::ARRAY_TEX_UV]=stride; + stride+=2; + } else if (attr=="TEXCOORD1") { + exists[Mesh::ARRAY_TEX_UV2]=true; + ofs[Mesh::ARRAY_TEX_UV2]=stride; + stride+=2; + } else if (attr.begins_with("TEXCOORD")) { + stride+=2; + } else if (attr.begins_with("BLENDWEIGHT")) { + int idx=attr.replace("BLENDWEIGHT","").to_int(); + if (idx==0) { + exists[Mesh::ARRAY_BONES]=true; + ofs[Mesh::ARRAY_BONES]=stride; + exists[Mesh::ARRAY_WEIGHTS]=true; + ofs[Mesh::ARRAY_WEIGHTS]=stride+1; + } if (idx<4) { + weight_ofs[idx]=stride; + weight_max=MAX(weight_max,idx+1); + } + + stride+=2; + } + + print_line("ATTR "+attr+" OFS: "+itos(stride)); + + } + + Array parts=mesh["parts"]; + + for(int j=0;j<parts.size();j++) { + + + + Dictionary part=parts[j]; + ERR_CONTINUE(!part.has("indices")); + ERR_CONTINUE(!part.has("id")); + + print_line("PART: "+String(part["id"])); + Array indices=part["indices"]; + Map<int,int> iarray; + Map<int,int> array; + + for(int k=0;k<indices.size();k++) { + + int idx = indices[k]; + if (!iarray.has(idx)) { + int map_to=array.size(); + iarray[idx]=map_to; + array[map_to]=idx; + } + } + + print_line("indices total "+itos(indices.size())+" vertices used: "+itos(array.size())); + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + + + + for(int k=0;k<Mesh::ARRAY_MAX;k++) { + + + if (!exists[k]) + continue; + print_line("exists: "+itos(k)); + int lofs = ofs[k]; + switch(k) { + + case Mesh::ARRAY_VERTEX: + case Mesh::ARRAY_NORMAL: { + + DVector<Vector3> vtx; + vtx.resize(array.size()); + { + int len=array.size(); + DVector<Vector3>::Write w = vtx.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].x=vertices[pos*stride+lofs+0]; + w[l].y=vertices[pos*stride+lofs+1]; + w[l].z=vertices[pos*stride+lofs+2]; + } + } + arrays[k]=vtx; + + } break; + case Mesh::ARRAY_TANGENT: { + + if (binormal_ofs<0) + break; + + DVector<float> tangents; + tangents.resize(array.size()*4); + { + int len=array.size(); + + DVector<float>::Write w = tangents.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + Vector3 n; + n.x=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+0]; + n.y=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+1]; + n.z=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+2]; + Vector3 t; + t.x=vertices[pos*stride+lofs+0]; + t.y=vertices[pos*stride+lofs+1]; + t.z=vertices[pos*stride+lofs+2]; + Vector3 bi; + bi.x=vertices[pos*stride+binormal_ofs+0]; + bi.y=vertices[pos*stride+binormal_ofs+1]; + bi.z=vertices[pos*stride+binormal_ofs+2]; + float d = bi.dot(n.cross(t)); + + w[l*4+0]=t.x; + w[l*4+1]=t.y; + w[l*4+2]=t.z; + w[l*4+3]=d; + + } + } + arrays[k]=tangents; + + } break; + case Mesh::ARRAY_COLOR: { + + DVector<Color> cols; + cols.resize(array.size()); + { + int len=array.size(); + DVector<Color>::Write w = cols.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].r=vertices[pos*stride+lofs+0]; + w[l].g=vertices[pos*stride+lofs+1]; + w[l].b=vertices[pos*stride+lofs+2]; + w[l].a=vertices[pos*stride+lofs+3]; + } + } + arrays[k]=cols; + + } break; + case Mesh::ARRAY_TEX_UV: + case Mesh::ARRAY_TEX_UV2: { + + DVector<Vector2> uvs; + uvs.resize(array.size()); + { + int len=array.size(); + DVector<Vector2>::Write w = uvs.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].x=vertices[pos*stride+lofs+0]; + w[l].y=vertices[pos*stride+lofs+1]; + w[l].y=1.0-w[l].y; + } + } + arrays[k]=uvs; + + } break; + case Mesh::ARRAY_BONES: + case Mesh::ARRAY_WEIGHTS: { + + DVector<float> arr; + arr.resize(array.size()*4); + int po=k==Mesh::ARRAY_WEIGHTS?1:0; + lofs=ofs[Mesh::ARRAY_BONES]; + { + int len=array.size(); + + DVector<float>::Write w = arr.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + + for(int m=0;m<4;m++) { + + float val=0; + if (m<=weight_max) + val=vertices[pos*stride+lofs+m*2+po]; + w[l*4+m]=val; + } + } + } + + arrays[k]=arr; + } break; + case Mesh::ARRAY_INDEX: { + + DVector<int> arr; + arr.resize(indices.size()); + { + int len=indices.size(); + + DVector<int>::Write w = arr.write(); + for(int l=0;l<len;l++) { + + w[l]=iarray[ indices[l] ]; + } + } + + arrays[k]=arr; + + } break; + + + } + + + } + + Mesh::PrimitiveType pt=Mesh::PRIMITIVE_TRIANGLES; + + if (part.has("type")) { + String type=part["type"]; + if (type=="LINES") + pt=Mesh::PRIMITIVE_LINES; + else if (type=="POINTS") + pt=Mesh::PRIMITIVE_POINTS; + else if (type=="TRIANGLE_STRIP") + pt=Mesh::PRIMITIVE_TRIANGLE_STRIP; + else if (type=="LINE_STRIP") + pt=Mesh::PRIMITIVE_LINE_STRIP; + } + + if (pt==Mesh::PRIMITIVE_TRIANGLES) { + DVector<int> ia=arrays[Mesh::ARRAY_INDEX]; + int len=ia.size(); + { + DVector<int>::Write w=ia.write(); + for(int l=0;l<len;l+=3) { + SWAP(w[l+1],w[l+2]); + } + } + arrays[Mesh::ARRAY_INDEX]=ia; + + + } + SurfaceInfo si; + si.array=arrays; + si.primitive=pt; + state.surface_cache[_id(part["id"])]=si; + + } + } +} + + +Error EditorSceneImporterFBXConv::_parse_animations(State& state) { + + AnimationPlayer *ap = memnew( AnimationPlayer ); + + state.scene->add_child(ap); + ap->set_owner(state.scene); + + for(int i=0;i<state.animations.size();i++) { + + Dictionary anim = state.animations[i]; + ERR_CONTINUE(!anim.has("id")); + Ref<Animation> an = memnew( Animation ); + an->set_name(_id(anim["id"])); + + + if (anim.has("bones")) { + + Array bone_tracks = anim["bones"]; + for(int j=0;j<bone_tracks.size();j++) { + Dictionary bone_track=bone_tracks[j]; + String bone = bone_track["boneId"]; + if (!bone_track.has("keyframes")) + continue; + if (!state.bones.has(bone)) + continue; + + Skeleton *sk = state.bones[bone].skeleton; + + if (!sk) + continue; + int bone_idx=sk->find_bone(bone); + if (bone_idx==-1) + continue; + + + + String path = state.scene->get_path_to(sk); + path+=":"+bone; + an->add_track(Animation::TYPE_TRANSFORM); + int tidx = an->get_track_count()-1; + an->track_set_path(tidx,path); + + + Dictionary parent_xform_dict; + Dictionary xform_dict; + + if (state.bones.has(bone)) { + xform_dict=state.bones[bone].node; + } + + + Array parent_keyframes; + if (sk->get_bone_parent(bone_idx)!=-1) { + String parent_name = sk->get_bone_name(sk->get_bone_parent(bone_idx)); + if (state.bones.has(parent_name)) { + parent_xform_dict=state.bones[parent_name].node; + } + + print_line("parent for "+bone+"? "+parent_name+" XFD: "+String(Variant(parent_xform_dict))); + for(int k=0;k<bone_tracks.size();k++) { + Dictionary d = bone_tracks[k]; + if (d["boneId"]==parent_name) { + parent_keyframes=d["keyframes"]; + print_line("found keyframes"); + break; + } + } + + + } + + print_line("BONE XFD "+String(Variant(xform_dict))); + + Array keyframes=bone_track["keyframes"]; + + for(int k=0;k<keyframes.size();k++) { + + Dictionary key=keyframes[k]; + Transform xform=_get_transform_mixed(key,xform_dict); + float time = key["keytime"]; + time=time/1000.0; +#if 0 + if (parent_keyframes.size()) { + //localize + print_line(itos(k)+" localizate for: "+bone); + + float prev_kt=-1; + float kt; + int idx=0; + + for(int l=0;l<parent_keyframes.size();l++) { + + Dictionary d=parent_keyframes[l]; + kt=d["keytime"]; + kt=kt/1000.0; + if (kt>time) + break; + prev_kt=kt; + idx++; + + } + + Transform t; + if (idx==0) { + t=_get_transform_mixed(parent_keyframes[0],parent_xform_dict); + } else if (idx==parent_keyframes.size()){ + t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); + } else { + t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); + float d = (time-prev_kt)/(kt-prev_kt); + if (d>0) { + Transform t2=_get_transform_mixed(parent_keyframes[idx],parent_xform_dict); + t=t.interpolate_with(t2,d); + } else { + print_line("exact: "+rtos(kt)); + } + } + + xform = t.affine_inverse() * xform; //localize + } else if (!parent_xform_dict.empty()) { + Transform t = _get_transform(parent_xform_dict); + xform = t.affine_inverse() * xform; //localize + } +#endif + + xform = sk->get_bone_rest(bone_idx).affine_inverse() * xform; + + + Quat q = xform.basis; + q.normalize(); + Vector3 s = xform.basis.get_scale(); + Vector3 l = xform.origin; + + + + an->transform_track_insert_key(tidx,time,l,q,s); + + } + + } + + + } + + + ap->add_animation(_id(anim["id"]),an); + + } + + return OK; +} + +Error EditorSceneImporterFBXConv::_parse_json(State& state, const String &p_path) { + + //not the happiest.... + Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); + ERR_FAIL_COND_V(!data.size(),ERR_FILE_CANT_OPEN); + String str; + bool utferr = str.parse_utf8((const char*)data.ptr(),data.size()); + ERR_FAIL_COND_V(utferr,ERR_PARSE_ERROR); + + Dictionary dict; + Error err = dict.parse_json(str); + str=String(); //free mem immediately + ERR_FAIL_COND_V(err,err); + + if (dict.has("meshes")) + state.meshes=dict["meshes"]; + if (dict.has("materials")) + state.materials=dict["materials"]; + if (dict.has("nodes")) + state.nodes=dict["nodes"]; + if (dict.has("animations")) + state.animations=dict["animations"]; + + + state.scene = memnew( Spatial ); + _detect_bones(state); + _parse_surfaces(state); + _parse_materials(state); + err = _parse_nodes(state,state.nodes,state.scene); + if (err) + return err; + + if (state.import_animations) { + err = _parse_animations(state); + if (err) + return err; + } + + print_line("JSON PARSED O-K!"); + + return OK; +} + +Error EditorSceneImporterFBXConv::_parse_fbx(State& state,const String& p_path) { + + state.base_path=p_path.get_base_dir(); + + if (p_path.to_lower().ends_with("g3dj")) { + return _parse_json(state,p_path.basename()+".g3dj"); + } + + String tool = EDITOR_DEF("fbxconv/path",""); + ERR_FAIL_COND_V( !FileAccess::exists(tool),ERR_UNCONFIGURED); + String wine = EDITOR_DEF("fbxconv/use_wine",""); + + List<String> args; + String path=p_path; + if (wine!="") { + List<String> wpargs; + wpargs.push_back("-w"); + wpargs.push_back(p_path); + String pipe; //winepath to convert to windows path + int wpres; + Error wperr = OS::get_singleton()->execute(wine+"path",wpargs,true,NULL,&pipe,&wpres); + ERR_FAIL_COND_V(wperr!=OK,ERR_CANT_CREATE); + ERR_FAIL_COND_V(wpres!=0,ERR_CANT_CREATE); + path=pipe.strip_edges(); + args.push_back(tool); + tool=wine; + } + + args.push_back("-o"); + args.push_back("G3DJ"); + args.push_back(path); + + int res; + Error err = OS::get_singleton()->execute(tool,args,true,NULL,NULL,&res); + ERR_FAIL_COND_V(err!=OK,ERR_CANT_CREATE); + ERR_FAIL_COND_V(res!=0,ERR_CANT_CREATE); + + return _parse_json(state,p_path.basename()+".g3dj"); + + +} + +Node* EditorSceneImporterFBXConv::import_scene(const String& p_path,uint32_t p_flags,List<String> *r_missing_deps,Error* r_err){ + + State state; + state.scene=NULL; + state.missing_deps=r_missing_deps; + state.import_animations=p_flags&IMPORT_ANIMATION; + Error err = _parse_fbx(state,p_path); + if (err!=OK) { + if (r_err) + *r_err=err; + return NULL; + } + + + return state.scene; +} +Ref<Animation> EditorSceneImporterFBXConv::import_animation(const String& p_path,uint32_t p_flags){ + + + return Ref<Animation>(); +} + + +EditorSceneImporterFBXConv::EditorSceneImporterFBXConv() { + + EDITOR_DEF("fbxconv/path",""); +#ifndef WINDOWS_ENABLED + EDITOR_DEF("fbxconv/use_wine",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/use_wine",PROPERTY_HINT_GLOBAL_FILE)); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/path",PROPERTY_HINT_GLOBAL_FILE)); +#else + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/path",PROPERTY_HINT_GLOBAL_FILE,"exe")); +#endif + +} diff --git a/tools/editor/io_plugins/editor_scene_importer_fbxconv.h b/tools/editor/io_plugins/editor_scene_importer_fbxconv.h new file mode 100644 index 0000000000..261b072b04 --- /dev/null +++ b/tools/editor/io_plugins/editor_scene_importer_fbxconv.h @@ -0,0 +1,81 @@ +#ifndef EDITOR_SCENE_IMPORTER_FBXCONV_H +#define EDITOR_SCENE_IMPORTER_FBXCONV_H + +#include "tools/editor/io_plugins/editor_scene_import_plugin.h" +#include "scene/3d/skeleton.h" + + +class EditorSceneImporterFBXConv : public EditorSceneImporter { + + OBJ_TYPE(EditorSceneImporterFBXConv,EditorSceneImporter ); + + + struct BoneInfo { + + Skeleton *skeleton; + Transform rest; + int index; + bool has_anim_chan; + bool has_rest; + Dictionary node; + BoneInfo() { + has_rest=false; + skeleton=NULL; + index=-1; + has_anim_chan=false; + } + }; + + struct SurfaceInfo { + Array array; + Mesh::PrimitiveType primitive; + }; + + struct State { + + Node *scene; + Array meshes; + Array materials; + Array nodes; + Array animations; + Map<String,BoneInfo > bones; + Map<String,Skeleton*> skeletons; + Map<String,Ref<Mesh> > mesh_cache; + Map<String,SurfaceInfo> surface_cache; + Map<String,Ref<Material> > material_cache; + Map<String,Ref<Texture> > texture_cache; + List<String> *missing_deps; + String base_path; + bool import_animations; + }; + + String _id(const String& p_id) const; + + Transform _get_transform_mixed(const Dictionary& d, const Dictionary& dbase); + Transform _get_transform(const Dictionary& d); + Color _get_color(const Array& a); + void _detect_bones_in_nodes(State& state,const Array& p_nodes); + void _detect_bones(State& state); + + Error _parse_bones(State& state,const Array &p_bones,Skeleton* p_skeleton); + void _parse_skeletons(const String& p_name,State& state, const Array &p_nodes, Skeleton*p_skeleton=NULL, int p_parent=-1); + + void _add_surface(State& state,Ref<Mesh>& m,const Dictionary &part); + Error _parse_nodes(State& state,const Array &p_nodes,Node* p_base); + Error _parse_animations(State& state); + void _parse_materials(State& state); + void _parse_surfaces(State& state); + Error _parse_json(State& state,const String& p_path); + Error _parse_fbx(State &state, const String &p_path); + +public: + + 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,List<String> *r_missing_deps=NULL,Error* r_err=NULL); + virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags); + + EditorSceneImporterFBXConv(); +}; + +#endif // EDITOR_SCENE_IMPORTER_FBXCONV_H diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.cpp b/tools/editor/io_plugins/editor_texture_import_plugin.cpp index c7593625ff..760651bbfd 100644 --- a/tools/editor/io_plugins/editor_texture_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_texture_import_plugin.cpp @@ -725,6 +725,7 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc bool atlas = from->get_option("atlas"); int flags=from->get_option("flags"); + print_line("GET FLAGS: "+itos(flags)); uint32_t tex_flags=0; if (flags&EditorTextureImportPlugin::IMAGE_FLAG_REPEAT) @@ -993,6 +994,7 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc if ((image.get_format()==Image::FORMAT_RGB || image.get_format()==Image::FORMAT_RGBA) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + print_line("CONVERT BECAUSE: "+itos(flags)); image.srgb_to_linear(); } diff --git a/tools/editor/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp index 1fa4d8d06c..dea83e0ff8 100644 --- a/tools/editor/plugins/baked_light_baker.cpp +++ b/tools/editor/plugins/baked_light_baker.cpp @@ -45,7 +45,7 @@ BakedLightBaker::MeshTexture* BakedLightBaker::_get_mat_tex(const Ref<Texture>& } -void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform) { +void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform,int p_baked_texture) { for(int i=0;i<p_mesh->get_surface_count();i++) { @@ -55,6 +55,7 @@ void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_m Ref<Material> mat = p_mat_override.is_valid()?p_mat_override:p_mesh->surface_get_material(i); MeshMaterial *matptr=NULL; + int baked_tex=p_baked_texture; if (mat.is_valid()) { @@ -112,6 +113,8 @@ void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_m DVector<Vector3>::Read vr=vertices.read(); DVector<Vector2> uv; DVector<Vector2>::Read uvr; + DVector<Vector2> uv2; + DVector<Vector2>::Read uv2r; DVector<Vector3> normal; DVector<Vector3>::Read normalr; bool read_uv=false; @@ -122,6 +125,18 @@ void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_m uv=a[Mesh::ARRAY_TEX_UV]; uvr=uv.read(); read_uv=true; + + if (mat.is_valid() && mat->get_flag(Material::FLAG_LIGHTMAP_ON_UV2) && p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV2) { + + uv2=a[Mesh::ARRAY_TEX_UV2]; + uv2r=uv2.read(); + + } else { + uv2r=uv.read(); + if (baked_light->get_transfer_lightmaps_only_to_uv2()) { + baked_tex=-1; + } + } } if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_NORMAL) { @@ -145,11 +160,16 @@ void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_m t.vertices[1]=p_xform.xform(vr[ ir[i*3+1] ]); t.vertices[2]=p_xform.xform(vr[ ir[i*3+2] ]); t.material=matptr; + t.baked_texture=baked_tex; if (read_uv) { t.uvs[0]=uvr[ ir[i*3+0] ]; t.uvs[1]=uvr[ ir[i*3+1] ]; t.uvs[2]=uvr[ ir[i*3+2] ]; + + t.bake_uvs[0]=uv2r[ ir[i*3+0] ]; + t.bake_uvs[1]=uv2r[ ir[i*3+1] ]; + t.bake_uvs[2]=uv2r[ ir[i*3+2] ]; } if (read_normal) { @@ -167,11 +187,17 @@ void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_m t.vertices[1]=p_xform.xform(vr[ i*3+1 ]); t.vertices[2]=p_xform.xform(vr[ i*3+2 ]); t.material=matptr; + t.baked_texture=baked_tex; if (read_uv) { t.uvs[0]=uvr[ i*3+0 ]; t.uvs[1]=uvr[ i*3+1 ]; t.uvs[2]=uvr[ i*3+2 ]; + + t.bake_uvs[0]=uv2r[ i*3+0 ]; + t.bake_uvs[1]=uv2r[ i*3+1 ]; + t.bake_uvs[2]=uv2r[ i*3+2 ]; + } if (read_normal) { @@ -193,7 +219,7 @@ void BakedLightBaker::_parse_geometry(Node* p_node) { MeshInstance *meshi=p_node->cast_to<MeshInstance>(); Ref<Mesh> mesh=meshi->get_mesh(); if (mesh.is_valid()) { - _add_mesh(mesh,meshi->get_material_override(),base_inv * meshi->get_global_transform()); + _add_mesh(mesh,meshi->get_material_override(),base_inv * meshi->get_global_transform(),meshi->get_baked_light_texture_id()); } } else if (p_node->cast_to<Light>()) { @@ -214,9 +240,11 @@ void BakedLightBaker::_parse_geometry(Node* p_node) { dirl.spot_angle=dl->get_parameter(DirectionalLight::PARAM_SPOT_ANGLE); dirl.spot_attenuation=dl->get_parameter(DirectionalLight::PARAM_SPOT_ATTENUATION); dirl.attenuation=dl->get_parameter(DirectionalLight::PARAM_ATTENUATION); + dirl.darkening=dl->get_parameter(DirectionalLight::PARAM_SHADOW_DARKENING); dirl.radius=dl->get_parameter(DirectionalLight::PARAM_RADIUS); dirl.bake_direct=dl->get_bake_mode()==Light::BAKE_MODE_FULL; dirl.rays_thrown=0; + dirl.bake_shadow=dl->get_bake_mode()==Light::BAKE_MODE_INDIRECT_AND_SHADOWS; lights.push_back(dirl); } @@ -310,7 +338,7 @@ void BakedLightBaker::_fix_lights() { } if (dl.type==VS::LIGHT_OMNI) { - dl.area=4.0*Math_PI*pow(dl.radius,2.0); + dl.area=4.0*Math_PI*pow(dl.radius,2.0f); dl.constant=1.0/3.5; } else { @@ -1023,7 +1051,7 @@ float BakedLightBaker::_throw_ray(int p_light_index,const Vector3& p_begin, cons if (!p_first_bounce) { - float r = plot_size * cell_size; + float r = plot_size * cell_size*4; if (ret<r) { //avoid accumulaiton of light on corners //plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size); @@ -1310,7 +1338,7 @@ double BakedLightBaker::get_normalization(int p_light_idx) const { nrg*=(Math_PI*plot_size*plot_size)*0.5; // damping of radial linear gradient kernel nrg*=dl.constant; //nrg*=5; - print_line("CS: "+rtos(cell_size)); + return nrg; } @@ -1460,6 +1488,13 @@ void BakedLightBaker::bake(const Ref<BakedLight> &p_light, Node* p_node) { normal_damp=baked_light->get_normal_damp(); octree_extra_margin=baked_light->get_cell_extra_margin(); + baked_textures.clear(); + for(int i=0;i<baked_light->get_lightmaps_count();i++) { + BakeTexture bt; + bt.width=baked_light->get_lightmap_gen_size(i).x; + bt.height=baked_light->get_lightmap_gen_size(i).y; + baked_textures.push_back(bt); + } ep.step("Parsing Geometry",0); @@ -1690,6 +1725,484 @@ void BakedLightBaker::_stop_thread() { thread=NULL; } +void BakedLightBaker::_plot_pixel_to_lightmap(int x, int y, int width, int height, uint8_t *image, const Vector3& p_pos,const Vector3& p_normal,double *p_norm_ptr,float mult,float gamma) { + + + uint8_t *ptr = &image[(y*width+x)*4]; + int lc = lights.size(); + + + Color color; + + Octant *octants=octant_pool.ptr(); + + + int octant_idx=0; + + + while(true) { + + Octant &octant=octants[octant_idx]; + + if (octant.leaf) { + + Vector3 lpos = p_pos-octant.aabb.pos; + lpos/=octant.aabb.size; + + Vector3 cols[8]; + + for(int i=0;i<8;i++) { + + for(int j=0;j<lc;j++) { + cols[i].x+=octant.light[j].accum[i][0]*p_norm_ptr[j]; + cols[i].y+=octant.light[j].accum[i][1]*p_norm_ptr[j]; + cols[i].z+=octant.light[j].accum[i][2]*p_norm_ptr[j]; + } + } + + + /*Vector3 final = (cols[0] + (cols[1] - cols[0]) * lpos.y); + final = final + ((cols[2] + (cols[3] - cols[2]) * lpos.y) - final)*lpos.x; + + Vector3 final2 = (cols[4+0] + (cols[4+1] - cols[4+0]) * lpos.y); + final2 = final2 + ((cols[4+2] + (cols[4+3] - cols[4+2]) * lpos.y) - final2)*lpos.x;*/ + + Vector3 finala = cols[0].linear_interpolate(cols[1],lpos.x); + Vector3 finalb = cols[2].linear_interpolate(cols[3],lpos.x); + Vector3 final = finala.linear_interpolate(finalb,lpos.y); + + Vector3 final2a = cols[4+0].linear_interpolate(cols[4+1],lpos.x); + Vector3 final2b = cols[4+2].linear_interpolate(cols[4+3],lpos.x); + Vector3 final2 = final2a.linear_interpolate(final2b,lpos.y); + + final = final.linear_interpolate(final2,lpos.z); + if (baked_light->get_format()==BakedLight::FORMAT_HDR8) + final*=8.0; + + + color.r=pow(final.x*mult,gamma); + color.g=pow(final.y*mult,gamma); + color.b=pow(final.z*mult,gamma); + color.a=1.0; + + int lc = lights.size(); + LightData *lv = lights.ptr(); + for(int i=0;i<lc;i++) { + //shadow baking + if (!lv[i].bake_shadow) + continue; + Vector3 from = p_pos+p_normal*0.01; + Vector3 to; + float att=0; + switch(lv[i].type) { + case VS::LIGHT_DIRECTIONAL: { + to=from-lv[i].dir*lv[i].length; + } break; + case VS::LIGHT_OMNI: { + to=lv[i].pos; + float d = MIN(lv[i].radius,to.distance_to(from))/lv[i].radius; + att=d;//1.0-d; + } break; + default: continue; + } + + uint32_t* stack = ray_stack; + BVH **bstack = bvh_stack; + + enum { + TEST_RAY_BIT=0, + VISIT_LEFT_BIT=1, + VISIT_RIGHT_BIT=2, + VISIT_DONE_BIT=3, + + + }; + + bool intersected=false; + + int level=0; + + Vector3 n = (to-from); + float len=n.length(); + if (len==0) + continue; + n/=len; + + const BVH *bvhptr = bvh; + + bstack[0]=bvh; + stack[0]=TEST_RAY_BIT; + + + while(!intersected) { + + uint32_t mode = stack[level]; + const BVH &b = *bstack[level]; + bool done=false; + + switch(mode) { + case TEST_RAY_BIT: { + + if (b.leaf) { + + + Face3 f3(b.leaf->vertices[0],b.leaf->vertices[1],b.leaf->vertices[2]); + + + Vector3 res; + + if (f3.intersects_segment(from,to)) { + intersected=true; + done=true; + } + + stack[level]=VISIT_DONE_BIT; + } else { + + + bool valid = b.aabb.smits_intersect_ray(from,n,0,len); + //bool valid = b.aabb.intersects_segment(p_begin,p_end); + // bool valid = b.aabb.intersects(ray_aabb); + + if (!valid) { + + stack[level]=VISIT_DONE_BIT; + + } else { + + stack[level]=VISIT_LEFT_BIT; + } + } + + } continue; + case VISIT_LEFT_BIT: { + + stack[level]=VISIT_RIGHT_BIT; + bstack[level+1]=b.children[0]; + stack[level+1]=TEST_RAY_BIT; + level++; + + } continue; + case VISIT_RIGHT_BIT: { + + stack[level]=VISIT_DONE_BIT; + bstack[level+1]=b.children[1]; + stack[level+1]=TEST_RAY_BIT; + level++; + } continue; + case VISIT_DONE_BIT: { + + if (level==0) { + done=true; + break; + } else + level--; + + } continue; + } + + + if (done) + break; + } + + + + if (intersected) { + + color.a=Math::lerp(MAX(0.01,lv[i].darkening),1.0,att); + } + + } + + break; + } else { + + Vector3 lpos = p_pos - octant.aabb.pos; + Vector3 half = octant.aabb.size * 0.5; + + int ofs=0; + + if (lpos.x >= half.x) + ofs|=1; + if (lpos.y >= half.y) + ofs|=2; + if (lpos.z >= half.z) + ofs|=4; + + octant_idx = octant.children[ofs]; + + if (octant_idx==0) + return; + + } + } + + ptr[0]=CLAMP(color.r*255.0,0,255); + ptr[1]=CLAMP(color.g*255.0,0,255); + ptr[2]=CLAMP(color.b*255.0,0,255); + ptr[3]=CLAMP(color.a*255.0,0,255); + +} + + +Error BakedLightBaker::transfer_to_lightmaps() { + + if (!triangles.size() || baked_textures.size()==0) + return ERR_UNCONFIGURED; + + EditorProgress ep("transfer_to_lightmaps","Transfer to Lightmaps:",baked_textures.size()*2+triangles.size()); + + for(int i=0;i<baked_textures.size();i++) { + + ERR_FAIL_COND_V( baked_textures[i].width<=0 || baked_textures[i].height<=0,ERR_UNCONFIGURED ); + + baked_textures[i].data.resize( baked_textures[i].width*baked_textures[i].height*4 ); + zeromem(baked_textures[i].data.ptr(),baked_textures[i].data.size()); + ep.step("Allocating Texture #"+itos(i+1),i); + } + + Vector<double> norm_arr; + norm_arr.resize(lights.size()); + + for(int i=0;i<lights.size();i++) { + norm_arr[i] = 1.0/get_normalization(i); + } + float gamma = baked_light->get_gamma_adjust(); + float mult = baked_light->get_energy_multiplier(); + + + const double *normptr=norm_arr.ptr(); + for(int i=0;i<triangles.size();i++) { + + if (i%200==0) { + ep.step("Baking Triangle #"+itos(i),i+baked_textures.size()); + } + Triangle &t=triangles[i]; + if (t.baked_texture<0 || t.baked_texture>=baked_textures.size()) + continue; + + BakeTexture &bt=baked_textures[t.baked_texture]; + Vector3 normal = Plane(t.vertices[0],t.vertices[1],t.vertices[2]).normal; + + + int x[3]; + int y[3]; + + Vector3 vertices[3]={ + t.vertices[0], + t.vertices[1], + t.vertices[2] + }; + + for(int j=0;j<3;j++) { + + x[j]=t.bake_uvs[j].x*bt.width; + y[j]=t.bake_uvs[j].y*bt.height; + x[j]=CLAMP(x[j],0,bt.width-1); + y[j]=CLAMP(y[j],0,bt.height-1); + } + + + { + + // sort the points vertically + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + SWAP(vertices[1],vertices[2]); + } + if (y[0] > y[1]) { + SWAP(x[0], x[1]); + SWAP(y[0], y[1]); + SWAP(vertices[0],vertices[1]); + } + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + SWAP(vertices[1],vertices[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 + for (int yi = y[0]; yi <= (y[2] > bt.height-1 ? bt.height-1 : y[2]); yi++) + { + if (yi >= 0) { + for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < bt.width ? xt : bt.width-1) ; xi++) { + //pixels[int(x + y * width)] = color; + + Vector2 v0 = Vector2(x[1]-x[0],y[1]-y[0]); + Vector2 v1 = Vector2(x[2]-x[0],y[2]-y[0]); + //vertices[2] - vertices[0]; + Vector2 v2 = Vector2(xi-x[0],yi-y[0]); + float d00 = v0.dot( v0); + float d01 = v0.dot( v1); + float d11 = v1.dot( v1); + float d20 = v2.dot( v0); + float d21 = v2.dot( v1); + float denom = (d00 * d11 - d01 * d01); + Vector3 pos; + if (denom==0) { + pos=t.vertices[0]; + } else { + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + pos = vertices[0]*u + vertices[1]*v + vertices[2]*w; + } + _plot_pixel_to_lightmap(xi,yi,bt.width,bt.height,bt.data.ptr(),pos,normal,norm_arr.ptr(),mult,gamma); + + } + + for (int xi = (xf < bt.width ? int(xf) : bt.width-1); xi >= (xt > 0 ? xt : 0); xi--) { + //pixels[int(x + y * width)] = color; + Vector2 v0 = Vector2(x[1]-x[0],y[1]-y[0]); + Vector2 v1 = Vector2(x[2]-x[0],y[2]-y[0]); + //vertices[2] - vertices[0]; + Vector2 v2 = Vector2(xi-x[0],yi-y[0]); + float d00 = v0.dot( v0); + float d01 = v0.dot( v1); + float d11 = v1.dot( v1); + float d20 = v2.dot( v0); + float d21 = v2.dot( v1); + float denom = (d00 * d11 - d01 * d01); + Vector3 pos; + if (denom==0) { + pos=t.vertices[0]; + } else { + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + pos = vertices[0]*u + vertices[1]*v + vertices[2]*w; + } + + _plot_pixel_to_lightmap(xi,yi,bt.width,bt.height,bt.data.ptr(),pos,normal,norm_arr.ptr(),mult,gamma); + + } + } + xf += dx_far; + if (yi < y[1]) + xt += dx_upper; + else + xt += dx_low; + } + } + + } + + + for(int i=0;i<baked_textures.size();i++) { + + + { + + ep.step("Post-Processing Texture #"+itos(i),i+baked_textures.size()+triangles.size()); + + BakeTexture &bt=baked_textures[i]; + + Vector<uint8_t> copy_data=bt.data; + uint8_t *data=bt.data.ptr(); + uint8_t *src_data=copy_data.ptr(); + const int max_radius=8; + const int shadow_radius=2; + const int max_dist=0x7FFFFFFF; + + for(int x=0;x<bt.width;x++) { + + for(int y=0;y<bt.height;y++) { + + + uint8_t a = copy_data[(y*bt.width+x)*4+3]; + + if (a>0) { + //blur shadow + + int from_x = MAX(0,x-shadow_radius); + int to_x = MIN(bt.width-1,x+shadow_radius); + int from_y = MAX(0,y-shadow_radius); + int to_y = MIN(bt.height-1,y+shadow_radius); + + int sum=0; + int sumc=0; + + for(int k=from_y;k<=to_y;k++) { + for(int l=from_x;l<=to_x;l++) { + + const uint8_t * rp = ©_data[(k*bt.width+l)<<2]; + + sum+=rp[3]; + sumc++; + } + } + + sum/=sumc; + data[(y*bt.width+x)*4+3]=sum; + + } else { + + int closest_dist=max_dist; + uint8_t closest_color[4]; + + int from_x = MAX(0,x-max_radius); + int to_x = MIN(bt.width-1,x+max_radius); + int from_y = MAX(0,y-max_radius); + int to_y = MIN(bt.height-1,y+max_radius); + + for(int k=from_y;k<=to_y;k++) { + for(int l=from_x;l<=to_x;l++) { + + int dy = y-k; + int dx = x-l; + int dist = dy*dy+dx*dx; + if (dist>=closest_dist) + continue; + + const uint8_t * rp = ©_data[(k*bt.width+l)<<2]; + + if (rp[3]==0) + continue; + + closest_dist=dist; + closest_color[0]=rp[0]; + closest_color[1]=rp[1]; + closest_color[2]=rp[2]; + closest_color[3]=rp[3]; + } + } + + + if (closest_dist!=max_dist) { + + data[(y*bt.width+x)*4+0]=closest_color[0]; + data[(y*bt.width+x)*4+1]=closest_color[1]; + data[(y*bt.width+x)*4+2]=closest_color[2]; + data[(y*bt.width+x)*4+3]=closest_color[3]; + } + } + } + } + } + + DVector<uint8_t> dv; + dv.resize(baked_textures[i].data.size()); + { + DVector<uint8_t>::Write w = dv.write(); + copymem(w.ptr(),baked_textures[i].data.ptr(),baked_textures[i].data.size()); + } + + Image img(baked_textures[i].width,baked_textures[i].height,0,Image::FORMAT_RGBA,dv); + Ref<ImageTexture> tex = memnew( ImageTexture ); + tex->create_from_image(img); + baked_light->set_lightmap_texture(i,tex); + } + + + return OK; +} + void BakedLightBaker::clear() { @@ -1711,7 +2224,14 @@ void BakedLightBaker::clear() { for(int i=0;i<octant_pool.size();i++) { if (octant_pool[i].leaf) { memdelete_arr( octant_pool[i].light ); + } Vector<double> norm_arr; + norm_arr.resize(lights.size()); + + for(int i=0;i<lights.size();i++) { + norm_arr[i] = 1.0/get_normalization(i); } + + const double *normptr=norm_arr.ptr(); } octant_pool.clear(); octant_pool_size=0; diff --git a/tools/editor/plugins/baked_light_baker.h b/tools/editor/plugins/baked_light_baker.h index 99c8211eed..722255a565 100644 --- a/tools/editor/plugins/baked_light_baker.h +++ b/tools/editor/plugins/baked_light_baker.h @@ -94,11 +94,13 @@ public: struct Triangle { - AABB aabb; + AABB aabb; Vector3 vertices[3]; Vector2 uvs[3]; + Vector2 bake_uvs[3]; Vector3 normals[3]; MeshMaterial *material; + int baked_texture; _FORCE_INLINE_ Vector2 get_uv(const Vector3& p_pos) { @@ -180,6 +182,12 @@ public: } }; + struct BakeTexture { + + Vector<uint8_t> data; + int width,height; + }; + struct LightData { @@ -194,10 +202,12 @@ public: float energy; float length; int rays_thrown; + bool bake_shadow; float radius; float attenuation; float spot_angle; + float darkening; float spot_attenuation; float area; @@ -220,6 +230,7 @@ public: int octant_pool_size; BVH*bvh; Vector<Triangle> triangles; + Vector<BakeTexture> baked_textures; Transform base_inv; int leaf_list; int octree_depth; @@ -255,13 +266,14 @@ public: MeshTexture* _get_mat_tex(const Ref<Texture>& p_tex); - void _add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform); + void _add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform,int p_baked_texture=-1); void _parse_geometry(Node* p_node); BVH* _parse_bvh(BVH** p_children,int p_size,int p_depth,int& max_depth); void _make_bvh(); void _make_octree(); void _make_octree_texture(); void _octree_insert(int p_octant, Triangle* p_triangle, int p_depth); + _FORCE_INLINE_ void _plot_pixel_to_lightmap(int x, int y, int width, int height, uint8_t *image, const Vector3& p_pos,const Vector3& p_normal,double *p_norm_ptr,float mult,float gamma); void _free_bvh(BVH* p_bvh); @@ -302,6 +314,8 @@ public: bool is_paused(); int get_rays_sec() { return rays_sec; } + Error transfer_to_lightmaps(); + void update_octree_image(DVector<uint8_t> &p_image); Ref<BakedLight> get_baked_light() { return baked_light; } diff --git a/tools/editor/plugins/baked_light_editor_plugin.cpp b/tools/editor/plugins/baked_light_editor_plugin.cpp index a1383f22fe..3d48f2e732 100644 --- a/tools/editor/plugins/baked_light_editor_plugin.cpp +++ b/tools/editor/plugins/baked_light_editor_plugin.cpp @@ -38,6 +38,7 @@ void BakedLightEditor::_notification(int p_option) { button_bake->set_icon(get_icon("Bake","EditorIcons")); button_reset->set_icon(get_icon("Reload","EditorIcons")); + button_make_lightmaps->set_icon(get_icon("LightMap","EditorIcons")); } if (p_option==NOTIFICATION_PROCESS) { @@ -148,7 +149,7 @@ void BakedLightEditor::_menu_option(int p_option) { ERR_FAIL_COND(!node); ERR_FAIL_COND(node->get_baked_light().is_null()); baker->bake(node->get_baked_light(),node); - + node->get_baked_light()->set_mode(BakedLight::MODE_OCTREE); update_timeout=0; set_process(true); @@ -180,14 +181,19 @@ void BakedLightEditor::_bake_pressed() { set_process(false); bake_info->set_text(""); + button_reset->show(); + button_make_lightmaps->show(); + } else { update_timeout=0; set_process(true); + button_make_lightmaps->hide(); + button_reset->hide(); } - } else { baker->bake(node->get_baked_light(),node); + node->get_baked_light()->set_mode(BakedLight::MODE_OCTREE); update_timeout=0; set_process(true); } @@ -216,13 +222,27 @@ void BakedLightEditor::edit(BakedLightInstance *p_baked_light) { } +void BakedLightEditor::_bake_lightmaps() { + + Error err = baker->transfer_to_lightmaps(); + if (err) { + + err_dialog->set_text("Error baking to lightmaps!\nMake sure that a bake has just\n happened and that lightmaps are\n configured. "); + err_dialog->popup_centered(Size2(350,70)); + return; + } + node->get_baked_light()->set_mode(BakedLight::MODE_LIGHTMAPS); + + +} void BakedLightEditor::_bind_methods() { ObjectTypeDB::bind_method("_menu_option",&BakedLightEditor::_menu_option); ObjectTypeDB::bind_method("_bake_pressed",&BakedLightEditor::_bake_pressed); ObjectTypeDB::bind_method("_clear_pressed",&BakedLightEditor::_clear_pressed); + ObjectTypeDB::bind_method("_bake_lightmaps",&BakedLightEditor::_bake_lightmaps); } BakedLightEditor::BakedLightEditor() { @@ -233,6 +253,11 @@ BakedLightEditor::BakedLightEditor() { button_bake->set_text("Bake!"); button_bake->set_toggle_mode(true); button_reset = memnew( Button ); + button_make_lightmaps = memnew( Button ); + button_bake->set_tooltip("Start/Unpause the baking process.\nThis bakes lighting into the lightmap octree."); + button_make_lightmaps ->set_tooltip("Convert the lightmap octree to lightmap textures\n(must have set up UV/Lightmaps properly before!)."); + + bake_info = memnew( Label ); bake_hbox->add_child( button_bake ); bake_hbox->add_child( button_reset ); @@ -243,8 +268,15 @@ BakedLightEditor::BakedLightEditor() { node=NULL; baker = memnew( BakedLightBaker ); + bake_hbox->add_child(button_make_lightmaps); + button_make_lightmaps->hide(); + button_bake->connect("pressed",this,"_bake_pressed"); button_reset->connect("pressed",this,"_clear_pressed"); + button_make_lightmaps->connect("pressed",this,"_bake_lightmaps"); + button_reset->hide(); + button_reset->set_tooltip("Reset the lightmap octree baking process (start over)."); + update_timeout=0; diff --git a/tools/editor/plugins/baked_light_editor_plugin.h b/tools/editor/plugins/baked_light_editor_plugin.h index 4ecc0b458f..7912bd92e5 100644 --- a/tools/editor/plugins/baked_light_editor_plugin.h +++ b/tools/editor/plugins/baked_light_editor_plugin.h @@ -30,6 +30,7 @@ class BakedLightEditor : public Control { HBoxContainer *bake_hbox; Button *button_bake; Button *button_reset; + Button *button_make_lightmaps; Label *bake_info; @@ -41,6 +42,8 @@ class BakedLightEditor : public Control { MENU_OPTION_CLEAR }; + void _bake_lightmaps(); + void _bake_pressed(); void _clear_pressed(); diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp new file mode 100644 index 0000000000..080ed7d11c --- /dev/null +++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp @@ -0,0 +1,457 @@ +#include "collision_polygon_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void CollisionPolygon2DEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon( get_icon("Edit","EditorIcons")); + button_edit->set_icon( get_icon("MovePoint","EditorIcons")); + button_edit->set_pressed(true); + + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void CollisionPolygon2DEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +Vector2 CollisionPolygon2DEditor::snap_point(const Vector2& p_point) const { + + if (canvas_item_editor->is_snap_active()) { + + return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); + + } else { + return p_point; + } +} + +void CollisionPolygon2DEditor::_menu_option(int p_option) { + + switch(p_option) { + + case MODE_CREATE: { + + mode=MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode=MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + + } +} + +void CollisionPolygon2DEditor::_wip_close() { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node,"set_polygon",node->get_polygon()); + undo_redo->add_do_method(node,"set_polygon",wip); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + wip.clear(); + wip_active=false; + mode=MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + edited_point=-1; +} + +bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) { + + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + + Vector2 gpoint = Point2(mb.x,mb.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + Vector<Vector2> poly = node->get_polygon(); + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( cpoint ); + wip_active=true; + edited_point_pos=cpoint; + canvas_item_editor->get_viewport_control()->update(); + edited_point=1; + return true; + } else { + + + if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(); + + return true; + } else { + + wip.push_back( cpoint ); + edited_point=wip.size(); + canvas_item_editor->get_viewport_control()->update(); + return true; + + //add wip point + } + } + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) { + _wip_close(); + } + + + + } break; + + case MODE_EDIT: { + + if (mb.button_index==BUTTON_LEFT) { + if (mb.pressed) { + + if (mb.mod.control) { + + + if (poly.size() < 3) { + + undo_redo->create_action("Edit Poly"); + undo_redo->add_undo_method(node,"set_polygon",poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + //search edges + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 points[2] ={ xform.xform(poly[i]), + xform.xform(poly[(i+1)%poly.size()]) }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); + if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + edited_point=closest_idx+1; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + node->set_polygon(poly); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } else { + + //look for points to move + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + edited_point=closest_idx; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } + } else { + + if (edited_point!=-1) { + + //apply + + ERR_FAIL_INDEX_V(edited_point,poly.size(),false); + poly[edited_point]=edited_point_pos; + undo_redo->create_action("Edit Poly"); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_undo_method(node,"set_polygon",pre_move_edit); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + edited_point=-1; + return true; + } + } + } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) { + + + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + + undo_redo->create_action("Edit Poly (Remove Point)"); + undo_redo->add_undo_method(node,"set_polygon",poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + } + + + + } break; + } + + + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + Vector2 gpoint = Point2(mm.x,mm.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); + + canvas_item_editor->get_viewport_control()->update(); + + } + + } break; + } + + return false; +} +void CollisionPolygon2DEditor::_canvas_draw() { + + if (!node) + return; + + Control *vpc = canvas_item_editor->get_viewport_control(); + + Vector<Vector2> poly; + + if (wip_active) + poly=wip; + else + poly=node->get_polygon(); + + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + int len = poly.size(); + + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = i==edited_point ? edited_point_pos : poly[i]; + if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)) + p2=edited_point_pos; + else + p2 = poly[(i+1)%poly.size()]; + + Vector2 point = xform.xform(p); + Vector2 next_point = xform.xform(p2); + + Color col=Color(1,0.3,0.1,0.8); + vpc->draw_line(point,next_point,col,2); + vpc->draw_texture(handle,point-handle->get_size()*0.5); + } +} + + + +void CollisionPolygon2DEditor::edit(Node *p_collision_polygon) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_collision_polygon) { + + node=p_collision_polygon->cast_to<CollisionPolygon2D>(); + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + + } else { + node=NULL; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void CollisionPolygon2DEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&CollisionPolygon2DEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&CollisionPolygon2DEditor::_canvas_draw); + +} + +CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) { + + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + add_child( memnew( VSeparator )); + button_create = memnew( ToolButton ); + add_child(button_create); + button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + + button_edit = memnew( ToolButton ); + add_child(button_edit); + button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT)); + button_edit->set_toggle_mode(true); + + //add_constant_override("separation",0); + +#if 0 + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + options->set_text("Polygon"); + //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + + mode = MODE_EDIT; + wip_active=false; + +} + + +void CollisionPolygon2DEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool CollisionPolygon2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("CollisionPolygon2D"); +} + +void CollisionPolygon2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( CollisionPolygon2DEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +CollisionPolygon2DEditorPlugin::~CollisionPolygon2DEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.h b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h new file mode 100644 index 0000000000..052019b6c5 --- /dev/null +++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h @@ -0,0 +1,84 @@ +#ifndef COLLISION_POLYGON_2D_EDITOR_PLUGIN_H +#define COLLISION_POLYGON_2D_EDITOR_PLUGIN_H + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class CollisionPolygon2DEditor : public HBoxContainer { + + OBJ_TYPE(CollisionPolygon2DEditor, HBoxContainer ); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + CollisionPolygon2D *node; + MenuButton *options; + + int edited_point; + Vector2 edited_point_pos; + Vector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + + void _wip_close(); + void _canvas_draw(); + void _menu_option(int p_option); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + Vector2 snap_point(const Vector2& p_point) const; + bool forward_input_event(const InputEvent& p_event); + void edit(Node *p_collision_polygon); + CollisionPolygon2DEditor(EditorNode *p_editor); +}; + +class CollisionPolygon2DEditorPlugin : public EditorPlugin { + + OBJ_TYPE( CollisionPolygon2DEditorPlugin, EditorPlugin ); + + CollisionPolygon2DEditor *collision_polygon_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "CollisionPolygon2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + CollisionPolygon2DEditorPlugin(EditorNode *p_node); + ~CollisionPolygon2DEditorPlugin(); + +}; + +#endif // COLLISION_POLYGON_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp index 93ad918b0e..16b9622312 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -27,10 +27,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "collision_polygon_editor_plugin.h" -#include "canvas_item_editor_plugin.h" +#include "spatial_editor_plugin.h" #include "os/file_access.h" #include "tools/editor/editor_settings.h" - +#include "scene/3d/camera.h" void CollisionPolygonEditor::_notification(int p_what) { switch(p_what) { @@ -40,11 +40,16 @@ void CollisionPolygonEditor::_notification(int p_what) { button_create->set_icon( get_icon("Edit","EditorIcons")); button_edit->set_icon( get_icon("MovePoint","EditorIcons")); button_edit->set_pressed(true); + get_scene()->connect("node_removed",this,"_node_removed"); } break; - case NOTIFICATION_FIXED_PROCESS: { + case NOTIFICATION_PROCESS: { + if (node->get_depth() != prev_depth) { + _polygon_draw(); + prev_depth=node->get_depth(); + } } break; } @@ -54,7 +59,10 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { if(p_node==node) { node=NULL; + if (imgeom->get_parent()==p_node) + p_node->remove_child(imgeom); hide(); + set_process(false); } } @@ -62,13 +70,15 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const { + return p_point; + /* if (canvas_item_editor->is_snap_active()) { return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); } else { return p_point; - } + } ??? */ } void CollisionPolygonEditor::_menu_option(int p_option) { @@ -93,21 +103,28 @@ void CollisionPolygonEditor::_menu_option(int p_option) { void CollisionPolygonEditor::_wip_close() { - undo_redo->create_action("Create Poly"); + undo_redo->create_action("Create Poly3D"); undo_redo->add_undo_method(node,"set_polygon",node->get_polygon()); undo_redo->add_do_method(node,"set_polygon",wip); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); - undo_redo->commit_action(); + undo_redo->add_do_method(this,"_polygon_draw"); + undo_redo->add_undo_method(this,"_polygon_draw"); wip.clear(); wip_active=false; mode=MODE_EDIT; button_edit->set_pressed(true); button_create->set_pressed(false); edited_point=-1; + undo_redo->commit_action(); + } -bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { +bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { + + + Transform gt = node->get_global_transform(); + float depth = node->get_depth()*0.5; + Vector3 n = gt.basis.get_axis(2).normalized(); + Plane p(gt.origin+n*depth,n); switch(p_event.type) { @@ -116,13 +133,20 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { const InputEventMouseButton &mb=p_event.mouse_button; - Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - Vector2 gpoint = Point2(mb.x,mb.y); - Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); - cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + Vector2 gpoint=Point2(mb.x,mb.y); + Vector3 ray_from = p_camera->project_ray_origin(gpoint); + Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + + Vector3 spoint; + + if (!p.intersects_ray(ray_from,ray_dir,&spoint)) + break; + + Vector2 cpoint(spoint.x,spoint.y); + + //cpoint=snap_point(cpoint); snap? Vector<Vector2> poly = node->get_polygon(); @@ -143,13 +167,13 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { wip.push_back( cpoint ); wip_active=true; edited_point_pos=cpoint; - canvas_item_editor->get_viewport_control()->update(); + _polygon_draw(); edited_point=1; return true; } else { - if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + if (wip.size()>1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x,wip[0].y,depth))).distance_to(gpoint)<grab_treshold) { //wip closed _wip_close(); @@ -158,7 +182,7 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { wip.push_back( cpoint ); edited_point=wip.size(); - canvas_item_editor->get_viewport_control()->update(); + _polygon_draw(); return true; //add wip point @@ -186,8 +210,8 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { undo_redo->add_undo_method(node,"set_polygon",poly); poly.push_back(cpoint); undo_redo->add_do_method(node,"set_polygon",poly); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_do_method(this,"_polygon_draw"); + undo_redo->add_undo_method(this,"_polygon_draw"); undo_redo->commit_action(); return true; } @@ -198,8 +222,10 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { real_t closest_dist=1e10; for(int i=0;i<poly.size();i++) { - Vector2 points[2] ={ xform.xform(poly[i]), - xform.xform(poly[(i+1)%poly.size()]) }; + Vector2 points[2] ={ + p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth))), + p_camera->unproject_position(gt.xform(Vector3(poly[(i+1)%poly.size()].x,poly[(i+1)%poly.size()].y,depth))) + }; Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) @@ -218,11 +244,11 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { if (closest_idx>=0) { pre_move_edit=poly; - poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + poly.insert(closest_idx+1,cpoint); edited_point=closest_idx+1; - edited_point_pos=xform.affine_inverse().xform(closest_pos); + edited_point_pos=cpoint; node->set_polygon(poly); - canvas_item_editor->get_viewport_control()->update(); + _polygon_draw(); return true; } } else { @@ -234,7 +260,7 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { real_t closest_dist=1e10; for(int i=0;i<poly.size();i++) { - Vector2 cp =xform.xform(poly[i]); + Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth))); real_t d = cp.distance_to(gpoint); if (d<closest_dist && d<grab_treshold) { @@ -249,8 +275,8 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { pre_move_edit=poly; edited_point=closest_idx; - edited_point_pos=xform.affine_inverse().xform(closest_pos); - canvas_item_editor->get_viewport_control()->update(); + edited_point_pos=poly[closest_idx]; + _polygon_draw(); return true; } } @@ -265,8 +291,8 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { undo_redo->create_action("Edit Poly"); undo_redo->add_do_method(node,"set_polygon",poly); undo_redo->add_undo_method(node,"set_polygon",pre_move_edit); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_do_method(this,"_polygon_draw"); + undo_redo->add_undo_method(this,"_polygon_draw"); undo_redo->commit_action(); edited_point=-1; @@ -282,7 +308,7 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { real_t closest_dist=1e10; for(int i=0;i<poly.size();i++) { - Vector2 cp =xform.xform(poly[i]); + Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth))); real_t d = cp.distance_to(gpoint); if (d<closest_dist && d<grab_treshold) { @@ -300,8 +326,8 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { undo_redo->add_undo_method(node,"set_polygon",poly); poly.remove(closest_idx); undo_redo->add_do_method(node,"set_polygon",poly); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_do_method(this,"_polygon_draw"); + undo_redo->add_undo_method(this,"_polygon_draw"); undo_redo->commit_action(); return true; } @@ -323,11 +349,21 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { Vector2 gpoint = Point2(mm.x,mm.y); - Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); - edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); - canvas_item_editor->get_viewport_control()->update(); + Vector3 ray_from = p_camera->project_ray_origin(gpoint); + Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + + Vector3 spoint; + + if (!p.intersects_ray(ray_from,ray_dir,&spoint)) + break; + + Vector2 cpoint(spoint.x,spoint.y); + + //cpoint=snap_point(cpoint); + edited_point_pos = cpoint; + + _polygon_draw(); } @@ -336,13 +372,11 @@ bool CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { return false; } -void CollisionPolygonEditor::_canvas_draw() { +void CollisionPolygonEditor::_polygon_draw() { if (!node) return; - Control *vpc = canvas_item_editor->get_viewport_control(); - Vector<Vector2> poly; if (wip_active) @@ -351,10 +385,16 @@ void CollisionPolygonEditor::_canvas_draw() { poly=node->get_polygon(); - Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); int len = poly.size(); + float depth = node->get_depth()*0.5; + + imgeom->clear(); + imgeom->set_material_override(line_material); + imgeom->begin(Mesh::PRIMITIVE_LINES,Ref<Texture>()); + + + Rect2 rect; for(int i=0;i<poly.size();i++) { @@ -366,38 +406,127 @@ void CollisionPolygonEditor::_canvas_draw() { else p2 = poly[(i+1)%poly.size()]; - Vector2 point = xform.xform(p); - Vector2 next_point = xform.xform(p2); + if (i==0) + rect.pos=p; + else + rect.expand_to(p); + + Vector3 point = Vector3(p.x,p.y,depth); + Vector3 next_point = Vector3(p2.x,p2.y,depth); + + imgeom->set_color(Color(1,0.3,0.1,0.8)); + imgeom->add_vertex(point); + imgeom->set_color(Color(1,0.3,0.1,0.8)); + imgeom->add_vertex(next_point); - Color col=Color(1,0.3,0.1,0.8); - vpc->draw_line(point,next_point,col,2); - vpc->draw_texture(handle,point-handle->get_size()*0.5); + //Color col=Color(1,0.3,0.1,0.8); + //vpc->draw_line(point,next_point,col,2); + //vpc->draw_texture(handle,point-handle->get_size()*0.5); } + + rect=rect.grow(1); + + AABB r; + r.pos.x=rect.pos.x; + r.pos.y=rect.pos.y; + r.pos.z=depth; + r.size.x=rect.size.x; + r.size.y=rect.size.y; + r.size.z=0; + + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0.3,0,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0.0,0.3,0)); + + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)-Vector3(0.3,0,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)+Vector3(0,0.3,0)); + + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)-Vector3(0,0.3,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)+Vector3(0.3,0,0)); + + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+r.size); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+r.size-Vector3(0.3,0,0)); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+r.size); + imgeom->set_color(Color(0.8,0.8,0.8,0.2)); + imgeom->add_vertex(r.pos+r.size-Vector3(0.0,0.3,0)); + + imgeom->end(); + + + while(m->get_surface_count()) { + m->surface_remove(0); + } + + if (poly.size()==0) + return; + + Array a; + a.resize(Mesh::ARRAY_MAX); + DVector<Vector3> va; + { + + va.resize(poly.size()); + DVector<Vector3>::Write w=va.write(); + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = i==edited_point ? edited_point_pos : poly[i]; + + Vector3 point = Vector3(p.x,p.y,depth); + w[i]=point; + } + } + a[Mesh::ARRAY_VERTEX]=va; + m->add_surface(Mesh::PRIMITIVE_POINTS,a); + m->surface_set_material(0,handle_material); + } void CollisionPolygonEditor::edit(Node *p_collision_polygon) { - if (!canvas_item_editor) { - canvas_item_editor=CanvasItemEditor::get_singleton(); - } + if (p_collision_polygon) { - node=p_collision_polygon->cast_to<CollisionPolygon2D>(); - if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) - canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + node=p_collision_polygon->cast_to<CollisionPolygon>(); wip.clear(); wip_active=false; edited_point=-1; + p_collision_polygon->add_child(imgeom); + _polygon_draw(); + set_process(true); + prev_depth=-1; } else { node=NULL; - if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) - canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + if (imgeom->get_parent()) + imgeom->get_parent()->remove_child(imgeom); + set_process(false); } } @@ -405,13 +534,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) { void CollisionPolygonEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_menu_option"),&CollisionPolygonEditor::_menu_option); - ObjectTypeDB::bind_method(_MD("_canvas_draw"),&CollisionPolygonEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_polygon_draw"),&CollisionPolygonEditor::_polygon_draw); + ObjectTypeDB::bind_method(_MD("_node_removed"),&CollisionPolygonEditor::_node_removed); } CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { - canvas_item_editor=NULL; + editor=p_editor; undo_redo = editor->get_undo_redo(); @@ -439,7 +569,42 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { mode = MODE_EDIT; wip_active=false; + imgeom = memnew( ImmediateGeometry ); + imgeom->set_transform(Transform(Matrix3(),Vector3(0,0,0.00001))); + + + line_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + line_material->set_flag(Material::FLAG_UNSHADED, true); + line_material->set_line_width(3.0); + line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true); + line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); + + + + + handle_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + handle_material->set_flag(Material::FLAG_UNSHADED, true); + handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true); + handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); + handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false); + Ref<Texture> handle= SpatialEditor::get_singleton()->get_icon("Editor3DHandle","EditorIcons"); + handle_material->set_point_size(handle->get_width()); + handle_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle); + + pointsm = memnew( MeshInstance ); + imgeom->add_child(pointsm); + m = Ref<Mesh>( memnew( Mesh ) ); + pointsm->set_mesh(m); + pointsm->set_transform(Transform(Matrix3(),Vector3(0,0,0.00001))); + + +} + +CollisionPolygonEditor::~CollisionPolygonEditor() { + memdelete( imgeom ); } @@ -450,7 +615,7 @@ void CollisionPolygonEditorPlugin::edit(Object *p_object) { bool CollisionPolygonEditorPlugin::handles(Object *p_object) const { - return p_object->is_type("CollisionPolygon2D"); + return p_object->is_type("CollisionPolygon"); } void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { @@ -469,7 +634,7 @@ CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) { editor=p_node; collision_polygon_editor = memnew( CollisionPolygonEditor(p_node) ); - CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); collision_polygon_editor->hide(); diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.h b/tools/editor/plugins/collision_polygon_editor_plugin.h index a05ac0f8c8..54b0706149 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.h +++ b/tools/editor/plugins/collision_polygon_editor_plugin.h @@ -31,7 +31,9 @@ #include "tools/editor/editor_plugin.h" #include "tools/editor/editor_node.h" -#include "scene/2d/collision_polygon_2d.h" +#include "scene/3d/collision_polygon.h" +#include "scene/3d/immediate_geometry.h" +#include "scene/3d/mesh_instance.h" #include "scene/gui/tool_button.h" #include "scene/gui/button_group.h" @@ -57,10 +59,17 @@ class CollisionPolygonEditor : public HBoxContainer { ToolButton *button_create; ToolButton *button_edit; - CanvasItemEditor *canvas_item_editor; + + Ref<FixedMaterial> line_material; + Ref<FixedMaterial> handle_material; + EditorNode *editor; Panel *panel; - CollisionPolygon2D *node; + CollisionPolygon *node; + ImmediateGeometry *imgeom; + MeshInstance *pointsm; + Ref<Mesh> m; + MenuButton *options; int edited_point; @@ -69,9 +78,10 @@ class CollisionPolygonEditor : public HBoxContainer { Vector<Vector2> wip; bool wip_active; + float prev_depth; void _wip_close(); - void _canvas_draw(); + void _polygon_draw(); void _menu_option(int p_option); protected: @@ -81,9 +91,10 @@ protected: public: Vector2 snap_point(const Vector2& p_point) const; - bool forward_input_event(const InputEvent& p_event); + virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event); void edit(Node *p_collision_polygon); CollisionPolygonEditor(EditorNode *p_editor); + ~CollisionPolygonEditor(); }; class CollisionPolygonEditorPlugin : public EditorPlugin { @@ -95,7 +106,7 @@ class CollisionPolygonEditorPlugin : public EditorPlugin { public: - virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { return collision_polygon_editor->forward_spatial_input_event(p_camera,p_event); } virtual String get_name() const { return "CollisionPolygon"; } bool has_main_screen() const { return false; } diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index 31ccc79d2a..209434440d 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -1102,7 +1102,6 @@ void ScriptEditor::ensure_select_current() { if (!ste) return; Ref<Script> script = ste->get_edited_script(); - editor->call("_resource_selected",script); } } diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp index 73b6265819..17c4291378 100644 --- a/tools/editor/plugins/shader_editor_plugin.cpp +++ b/tools/editor/plugins/shader_editor_plugin.cpp @@ -436,8 +436,10 @@ void ShaderEditor::save_external_data() { void ShaderEditor::apply_shaders() { - if (shader.is_valid()) + if (shader.is_valid()) { shader->set_code(vertex_editor->get_text_edit()->get_text(),fragment_editor->get_text_edit()->get_text(),light_editor->get_text_edit()->get_text(),0,0); + shader->set_edited(true); + } } void ShaderEditor::_close_callback() { diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp index 1bf425f3f3..78df6ffa0c 100644 --- a/tools/editor/plugins/spatial_editor_plugin.cpp +++ b/tools/editor/plugins/spatial_editor_plugin.cpp @@ -38,7 +38,7 @@ #include "tools/editor/editor_settings.h" #include "scene/resources/surface_tool.h" #include "tools/editor/spatial_editor_gizmos.h" - +#include "globals.h" #define DISTANCE_DEFAULT 4 @@ -690,10 +690,16 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { case BUTTON_WHEEL_UP: { + cursor.distance/=1.08; + if (cursor.distance<0.001) + cursor.distance=0.001; + } break; case BUTTON_WHEEL_DOWN: { + if (cursor.distance<0.001) + cursor.distance=0.001; cursor.distance*=1.08; } break; @@ -1753,6 +1759,41 @@ void SpatialEditorViewport::_draw() { } + if (previewing) { + + + Size2 ss = Size2( Globals::get_singleton()->get("display/width"), Globals::get_singleton()->get("display/height") ); + float aspect = ss.get_aspect(); + Size2 s = get_size(); + + Rect2 draw_rect; + + + switch(previewing->get_keep_aspect_mode()) { + case Camera::KEEP_WIDTH: { + + draw_rect.size = Size2(s.width,s.width/aspect); + draw_rect.pos.x=0; + draw_rect.pos.y=(s.height-draw_rect.size.y)*0.5; + + } break; + case Camera::KEEP_HEIGHT: { + + draw_rect.size = Size2(s.height*aspect,s.height); + draw_rect.pos.y=0; + draw_rect.pos.x=(s.width-draw_rect.size.x)*0.5; + + } break; + } + + draw_rect = Rect2(Vector2(),s).clip(draw_rect); + + surface->draw_line(draw_rect.pos,draw_rect.pos+Vector2(draw_rect.size.x,0),Color(0.6,0.6,0.1,0.5),2.0); + surface->draw_line(draw_rect.pos+Vector2(draw_rect.size.x,0),draw_rect.pos+draw_rect.size,Color(0.6,0.6,0.1,0.5),2.0); + surface->draw_line(draw_rect.pos+draw_rect.size,draw_rect.pos+Vector2(0,draw_rect.size.y),Color(0.6,0.6,0.1,0.5),2.0); + surface->draw_line(draw_rect.pos,draw_rect.pos+Vector2(0,draw_rect.size.y),Color(0.6,0.6,0.1,0.5),2.0); + } + } @@ -1936,6 +1977,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { if (!preview) preview_camera->hide(); view_menu->show(); + surface->update(); } else { @@ -1943,6 +1985,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { previewing->connect("exit_scene",this,"_preview_exited_scene"); VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), preview->get_camera() ); //replace view_menu->hide(); + surface->update(); } } @@ -2345,8 +2388,7 @@ void SpatialEditor::set_state(const Dictionary& p_state) { bool use = d["show_grid"]; if (use!=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID))) { - view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), use ); - grid_enabled=use; + _menu_item_pressed(MENU_VIEW_GRID); } } @@ -2653,6 +2695,13 @@ void SpatialEditor::_menu_item_pressed(int p_option) { grid_enabled=!is_checked; + for(int i=0;i<3;++i) { + if (grid_enable[i]) { + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,grid_enabled); + grid_visible[i]=grid_enabled; + } + } + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(p_option), grid_enabled ); @@ -2800,11 +2849,13 @@ void SpatialEditor::_init_indicators() { //move gizmo + float gizmo_alph = EditorSettings::get_singleton()->get("3d_editor/manipulator_gizmo_opacity"); + gizmo_hl = Ref<FixedMaterial>( memnew( FixedMaterial ) ); gizmo_hl->set_flag(Material::FLAG_UNSHADED, true); gizmo_hl->set_flag(Material::FLAG_ONTOP, true); gizmo_hl->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); - gizmo_hl->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.4)); + gizmo_hl->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,gizmo_alph+0.2f)); for(int i=0;i<3;i++) { @@ -2818,7 +2869,7 @@ void SpatialEditor::_init_indicators() { mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); Color col; col[i]=1.0; - col.a=0.2; + col.a= gizmo_alph; mat->set_parameter(FixedMaterial::PARAM_DIFFUSE,col); gizmo_color[i]=mat; @@ -3507,6 +3558,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { EDITOR_DEF("3d_editor/manipulator_gizmo_size",80); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT,"3d_editor/manipulator_gizmo_size",PROPERTY_HINT_RANGE,"16,1024,1")); + EDITOR_DEF("3d_editor/manipulator_gizmo_opacity",0.2); over_gizmo_handle=-1; } diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp index d757a4c07d..22331c86bf 100644 --- a/tools/editor/project_export.cpp +++ b/tools/editor/project_export.cpp @@ -1336,6 +1336,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { file_export = memnew( FileDialog ); add_child(file_export); file_export->set_access(FileDialog::ACCESS_FILESYSTEM); + file_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") ); file_export->set_title("Export Project"); file_export->connect("file_selected", this,"_export_action"); @@ -1353,6 +1354,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { pck_export = memnew( FileDialog ); pck_export->set_access(FileDialog::ACCESS_FILESYSTEM); + pck_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") ); pck_export->set_title("Export Project PCK"); pck_export->connect("file_selected", this,"_export_action_pck"); pck_export->add_filter("*.pck ; Data Pack"); diff --git a/tools/editor/project_manager.cpp b/tools/editor/project_manager.cpp index cd28c6da97..26d7abb745 100644 --- a/tools/editor/project_manager.cpp +++ b/tools/editor/project_manager.cpp @@ -900,6 +900,7 @@ ProjectManager::ProjectManager() { scan_dir = memnew( FileDialog ); scan_dir->set_access(FileDialog::ACCESS_FILESYSTEM); scan_dir->set_mode(FileDialog::MODE_OPEN_DIR); + scan_dir->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_path") ); add_child(scan_dir); scan_dir->connect("dir_selected",this,"_scan_begin"); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index 4ac2ff0594..645d967a4b 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -38,6 +38,7 @@ #include "scene/scene_string_names.h" #include "editor_settings.h" #include "editor_import_export.h" +#include "editor_node.h" void CustomPropertyEditor::_notification(int p_what) { @@ -1619,6 +1620,7 @@ CustomPropertyEditor::CustomPropertyEditor() { scene_tree = memnew( SceneTreeDialog ); add_child(scene_tree); scene_tree->connect("selected", this,"_node_path_selected"); + scene_tree->get_tree()->set_show_enabled_subscene(true); texture_preview = memnew( TextureFrame ); add_child( texture_preview); @@ -2037,6 +2039,17 @@ void PropertyEditor::update_tree() { List<PropertyInfo> plist; obj->get_property_list(&plist,true); + bool draw_red=false; + + { + Node *nod = obj->cast_to<Node>(); + Node *es = EditorNode::get_singleton()->get_edited_scene(); + if (nod && es!=nod && nod->get_owner()!=es) { + draw_red=true; + } + } + + Color sscolor=get_color("prop_subsection","Editor"); TreeItem * current_category=NULL; @@ -2141,11 +2154,16 @@ void PropertyEditor::update_tree() { item->set_metadata( 0, d ); item->set_metadata( 1, p.name ); + + if (draw_red) + item->set_custom_color(0,Color(0.8,0.4,0.20)); + if (p.name==selected_property) { item->select(1); } + //printf("property %s type %i\n",p.name.ascii().get_data(),p.type); switch( p.type ) { diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index c4c3a10f74..e7f4beb46e 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -108,6 +108,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_NEW: { + + if (!_validate_no_foreign()) + break; create_dialog->popup_centered_ratio(); } break; case TOOL_INSTANCE: { @@ -124,6 +127,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } + if (!_validate_no_foreign()) + break; + file->set_mode(FileDialog::MODE_OPEN_FILE); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); @@ -147,6 +153,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!current) break; + if (!_validate_no_foreign()) + break; connect_dialog->popup_centered_ratio(); connect_dialog->set_node(current); @@ -156,6 +164,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *current = scene_tree->get_selected(); if (!current) break; + if (!_validate_no_foreign()) + break; groups_editor->set_current(current); groups_editor->popup_centered_ratio(); } break; @@ -165,6 +175,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!selected) break; + if (!_validate_no_foreign()) + break; + Ref<Script> existing = selected->get_script(); if (existing.is_valid()) editor->push_item(existing.ptr()); @@ -183,6 +196,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!scene_tree->get_selected()) break; + if (scene_tree->get_selected()==edited_scene) { @@ -195,6 +209,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } + if (!_validate_no_foreign()) + break; + Node * node=scene_tree->get_selected(); ERR_FAIL_COND(!node->get_parent()); int current_pos = node->get_index(); @@ -214,6 +231,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!edited_scene) break; + if (editor_selection->is_selected(edited_scene)) { @@ -225,6 +243,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } + if (!_validate_no_foreign()) + break; + List<Node*> selection = editor_selection->get_selected_node_list(); List<Node*> reselect; @@ -313,6 +334,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!scene_tree->get_selected()) break; + if (editor_selection->is_selected(edited_scene)) { @@ -324,6 +346,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } + if (!_validate_no_foreign()) + break; + List<Node*> nodes = editor_selection->get_selected_node_list(); Set<Node*> nodeset; for(List<Node*>::Element *E=nodes.front();E;E=E->next()) { @@ -341,6 +366,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (remove_list.empty()) return; + if (!_validate_no_foreign()) + break; + if (p_confirm_override) { _delete_confirm(); @@ -707,6 +735,25 @@ void SceneTreeDock::_node_prerenamed(Node* p_node, const String& p_new_name) { } +bool SceneTreeDock::_validate_no_foreign() { + + List<Node*> selection = editor_selection->get_selected_node_list(); + + for (List<Node*>::Element *E=selection.front();E;E=E->next()) { + + if (E->get()!=edited_scene && E->get()->get_owner()!=edited_scene) { + + accept->get_ok()->set_text("Makes Sense!"); + accept->set_text("Can't operate on nodes from a foreign scene!"); + accept->popup_centered(Size2(300,70));; + return false; + + } + } + + return true; +} + void SceneTreeDock::_node_reparent(NodePath p_path,bool p_node_only) { @@ -894,7 +941,7 @@ void SceneTreeDock::_delete_confirm() { void SceneTreeDock::_update_tool_buttons() { Node *sel = scene_tree->get_selected(); - bool disable = !sel; + bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene); bool disable_root = disable || sel->get_parent()==scene_root; tool_buttons[TOOL_INSTANCE]->set_disabled(disable); diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h index 99ef16e6f6..e55a54377a 100644 --- a/tools/editor/scene_tree_dock.h +++ b/tools/editor/scene_tree_dock.h @@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer { void _import_subscene(); + bool _validate_no_foreign(); void _fill_path_renames(Vector<StringName> base_path,Vector<StringName> new_base_path,Node * p_node, List<Pair<NodePath,NodePath> > *p_renames); diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp index 4d0ed3e1dd..59bc24487a 100644 --- a/tools/editor/scene_tree_editor.cpp +++ b/tools/editor/scene_tree_editor.cpp @@ -45,6 +45,40 @@ Node *SceneTreeEditor::get_scene_node() { return NULL; } + +void SceneTreeEditor::_subscene_option(int p_idx) { + + Object *obj = ObjectDB::get_instance(instance_node); + if (!obj) + return; + Node *node = obj->cast_to<Node>(); + if (!node) + return; + + switch(p_idx) { + + case SCENE_MENU_SHOW_CHILDREN: { + + if (node->has_meta("__editor_show_subtree")) { + instance_menu->set_item_checked(0,true); + node->set_meta("__editor_show_subtree",Variant()); + _update_tree(); + } else { + node->set_meta("__editor_show_subtree",true); + _update_tree(); + } + + } break; + case SCENE_MENU_OPEN: { + + emit_signal("open",node->get_filename()); + } break; + + } + +} + + void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) { TreeItem *item=p_item->cast_to<TreeItem>(); @@ -57,7 +91,19 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) if (p_id==BUTTON_SUBSCENE) { //open scene request - emit_signal("open",n->get_filename()); + Rect2 item_rect = tree->get_item_rect(item,0); + item_rect.pos.y-=tree->get_scroll().y; + item_rect.pos+=tree->get_global_pos(); + instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y)); + instance_menu->set_size(Vector2(item_rect.size.x,0)); + if (n->has_meta("__editor_show_subtree")) + instance_menu->set_item_checked(0,true); + else + instance_menu->set_item_checked(0,false); + + instance_menu->popup(); + instance_node=n->get_instance_ID(); + //emit_signal("open",n->get_filename()); } else if (p_id==BUTTON_SCRIPT) { RefPtr script=n->get_script(); if (!script.is_null()) @@ -66,11 +112,19 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) } else if (p_id==BUTTON_VISIBILITY) { - if (n->is_type("GeometryInstance")) { - bool v = n->call("get_flag",VS::INSTANCE_FLAG_VISIBLE); - undo_redo->create_action("Toggle Geometry Visible"); - undo_redo->add_do_method(n,"set_flag",VS::INSTANCE_FLAG_VISIBLE,!v); - undo_redo->add_undo_method(n,"set_flag",VS::INSTANCE_FLAG_VISIBLE,v); + if (n->is_type("Spatial")) { + + Spatial *ci = n->cast_to<Spatial>(); + if (!ci->is_visible() && ci->get_parent_spatial() && !ci->get_parent_spatial()->is_visible()) { + error->set_text("This item cannot be made visible because the parent is hidden. Unhide the parent first."); + error->popup_centered_minsize(Size2(400,80)); + return; + } + + bool v = !bool(n->call("is_hidden")); + undo_redo->create_action("Toggle Spatial Visible"); + undo_redo->add_do_method(n,"_set_visible_",!v); + undo_redo->add_undo_method(n,"_set_visible_",v); undo_redo->commit_action(); } else if (n->is_type("CanvasItem")) { @@ -111,9 +165,19 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { // only owned nodes are editable, since nodes can create their own (manually owned) child nodes, // which the editor needs not to know about. - - if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) - return; + + bool part_of_subscene=false; + + if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) { + + if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) { + + part_of_subscene=true; + //allow + } else { + return; + } + } TreeItem *item = tree->create_item(p_parent); item->set_text(0, p_node->get_name() ); @@ -135,8 +199,12 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons"); item->set_icon(0, icon ); item->set_metadata( 0,p_node->get_path() ); - - if (marked.has(p_node)) { + if (part_of_subscene) { + + //item->set_selectable(0,marked_selectable); + item->set_custom_color(0,Color(0.8,0.4,0.20)); + + } else if (marked.has(p_node)) { item->set_selectable(0,marked_selectable); item->set_custom_color(0,Color(0.8,0.1,0.10)); @@ -155,7 +223,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) { - item->add_button(0,get_icon("Load","EditorIcons"),BUTTON_SUBSCENE); + item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE); item->set_tooltip(0,"Instance: "+p_node->get_filename()); } @@ -189,9 +257,9 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { if (!p_node->is_connected("visibility_changed",this,"_node_visibility_changed")) p_node->connect("visibility_changed",this,"_node_visibility_changed",varray(p_node)); - } else if (p_node->is_type("GeometryInstance")) { + } else if (p_node->is_type("Spatial")) { - bool h = !p_node->call("get_flag",VS::INSTANCE_FLAG_VISIBLE); + bool h = p_node->call("is_hidden"); if (h) item->add_button(0,get_icon("Hidden","EditorIcons"),BUTTON_VISIBILITY); else @@ -226,7 +294,16 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { void SceneTreeEditor::_node_visibility_changed(Node *p_node) { + + if (p_node!=get_scene_node() && !p_node->get_owner()) { + + return; + } TreeItem* item=p_node?_find(tree->get_root(),p_node->get_path()):NULL; + if (!item) { + + return; + } int idx=item->get_button_by_id(0,BUTTON_VISIBILITY); ERR_FAIL_COND(idx==-1); @@ -234,11 +311,10 @@ void SceneTreeEditor::_node_visibility_changed(Node *p_node) { if (p_node->is_type("CanvasItem")) { visible = !p_node->call("is_hidden"); - } else if (p_node->is_type("GeometryInstance")) { - visible = p_node->call("get_flag",VS::INSTANCE_FLAG_VISIBLE); + } else if (p_node->is_type("Spatial")) { + visible = !p_node->call("is_hidden"); } - if (!visible) item->set_button(0,idx,get_icon("Hidden","EditorIcons")); else @@ -274,7 +350,7 @@ void SceneTreeEditor::_node_removed(Node *p_node) { if (p_node->is_connected("script_changed",this,"_node_script_changed")) p_node->disconnect("script_changed",this,"_node_script_changed"); - if (p_node->is_type("GeometryInstance") || p_node->is_type("CanvasItem")) { + if (p_node->is_type("Spatial") || p_node->is_type("CanvasItem")) { if (p_node->is_connected("visibility_changed",this,"_node_visibility_changed")) p_node->disconnect("visibility_changed",this,"_node_visibility_changed"); } @@ -409,6 +485,7 @@ void SceneTreeEditor::_notification(int p_what) { get_scene()->connect("tree_changed",this,"_tree_changed"); get_scene()->connect("node_removed",this,"_node_removed"); + instance_menu->set_item_icon(2,get_icon("Load","EditorIcons")); tree->connect("item_collapsed",this,"_cell_collapsed"); // get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED); @@ -630,6 +707,7 @@ void SceneTreeEditor::_cell_collapsed(Object *p_obj) { } + void SceneTreeEditor::_bind_methods() { ObjectTypeDB::bind_method("_tree_changed",&SceneTreeEditor::_tree_changed); @@ -643,6 +721,7 @@ void SceneTreeEditor::_bind_methods() { ObjectTypeDB::bind_method("_selection_changed",&SceneTreeEditor::_selection_changed); ObjectTypeDB::bind_method("_cell_button_pressed",&SceneTreeEditor::_cell_button_pressed); ObjectTypeDB::bind_method("_cell_collapsed",&SceneTreeEditor::_cell_collapsed); + ObjectTypeDB::bind_method("_subscene_option",&SceneTreeEditor::_subscene_option); ObjectTypeDB::bind_method("_node_script_changed",&SceneTreeEditor::_node_script_changed); ObjectTypeDB::bind_method("_node_visibility_changed",&SceneTreeEditor::_node_visibility_changed); @@ -698,10 +777,20 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open error = memnew( AcceptDialog ); add_child(error); + show_enabled_subscene=false; + last_hash=0; pending_test_update=false; updating_tree=false; blocked=0; + + instance_menu = memnew( PopupMenu ); + instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN); + instance_menu->add_separator(); + instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN); + instance_menu->connect("item_pressed",this,"_subscene_option"); + add_child(instance_menu); + } diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h index 19375ba638..5e88c5a41d 100644 --- a/tools/editor/scene_tree_editor.h +++ b/tools/editor/scene_tree_editor.h @@ -51,8 +51,15 @@ class SceneTreeEditor : public Control { BUTTON_GROUP=4, }; + enum { + SCENE_MENU_SHOW_CHILDREN, + SCENE_MENU_OPEN, + }; + Tree *tree; Node *selected; + PopupMenu *instance_menu; + ObjectID instance_node; AcceptDialog *error; @@ -78,6 +85,7 @@ class SceneTreeEditor : public Control { bool can_rename; bool can_open_instance; bool updating_tree; + bool show_enabled_subscene; void _renamed(); UndoRedo *undo_redo; @@ -95,6 +103,7 @@ class SceneTreeEditor : public Control { void _update_selection(TreeItem *item); void _node_script_changed(Node *p_node); void _node_visibility_changed(Node *p_node); + void _subscene_option(int p_idx); void _selection_changed(); Node *get_scene_node(); @@ -112,6 +121,8 @@ public: void set_can_rename(bool p_can_rename) { can_rename=p_can_rename; } void set_editor_selection(EditorSelection *p_selection); + void set_show_enabled_subscene(bool p_show) { show_enabled_subscene=p_show; } + void update_tree() { _update_tree(); } SceneTreeEditor(bool p_label=true,bool p_can_rename=false, bool p_can_open_instance=false); diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp index 83efb48bd3..082878655c 100644 --- a/tools/editor/spatial_editor_gizmos.cpp +++ b/tools/editor/spatial_editor_gizmos.cpp @@ -1538,6 +1538,70 @@ CarWheelSpatialGizmo::CarWheelSpatialGizmo(CarWheel* p_car_wheel){ }
+/////
+
+
+void VehicleWheelSpatialGizmo::redraw() {
+
+ clear();
+
+
+ Vector<Vector3> points;
+
+ float r = car_wheel->get_radius();
+ const int skip=10;
+ for(int i=0;i<=360;i+=skip) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+skip);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));
+
+ const int springsec=4;
+
+ for(int j=0;j<springsec;j++) {
+ float t = car_wheel->get_suspension_rest_length()*5;
+ points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2);
+ points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2);
+ }
+
+
+ }
+
+ //travel
+ points.push_back(Vector3(0,0,0));
+ points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0));
+
+ //axis
+ points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0));
+ points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0));
+ //axis
+ points.push_back(Vector3(r*0.2,0,0));
+ points.push_back(Vector3(-r*0.2,0,0));
+
+ //forward line
+ points.push_back(Vector3(0,-r,0));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(r*2*0.2,-r,r*2*0.8));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8));
+
+ add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material);
+ add_collision_segments(points);
+
+}
+
+VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){
+
+ set_spatial_node(p_car_wheel);
+ car_wheel=p_car_wheel;
+}
+
+
///
@@ -2002,7 +2066,39 @@ CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape* p_cs) { +/////
+
+
+void CollisionPolygonSpatialGizmo::redraw() {
+
+ clear();
+
+ Vector<Vector2> points = polygon->get_polygon();
+ float depth = polygon->get_depth()*0.5;
+
+ Vector<Vector3> lines;
+ for(int i=0;i<points.size();i++) {
+
+ int n = (i+1)%points.size();
+ lines.push_back(Vector3(points[i].x,points[i].y,depth));
+ lines.push_back(Vector3(points[n].x,points[n].y,depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,-depth));
+ lines.push_back(Vector3(points[n].x,points[n].y,-depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,-depth));
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(lines);
+}
+
+CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon){
+
+ set_spatial_node(p_polygon);
+ polygon=p_polygon;
+}
+///
String VisibilityNotifierGizmo::get_handle_name(int p_idx) const {
@@ -2100,9 +2196,614 @@ VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier* p_notifier) set_spatial_node(p_notifier);
}
+////////
+
+
+
+void NavigationMeshSpatialGizmo::redraw() {
+
+ clear();
+ Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh();
+ if (navmeshie.is_null())
+ return;
+
+ DVector<Vector3> vertices = navmeshie->get_vertices();
+ DVector<Vector3>::Read vr=vertices.read();
+ List<Face3> faces;
+ for(int i=0;i<navmeshie->get_polygon_count();i++) {
+ Vector<int> p = navmeshie->get_polygon(i);
+
+ for(int j=2;j<p.size();j++) {
+ Face3 f;
+ f.vertex[0]=vr[p[0]];
+ f.vertex[1]=vr[p[j-1]];
+ f.vertex[2]=vr[p[j]];
+
+ faces.push_back(f);
+ }
+ }
+
+
+ Map<_EdgeKey,bool> edge_map;
+ DVector<Vector3> tmeshfaces;
+ tmeshfaces.resize(faces.size()*3);
+
+ {
+ DVector<Vector3>::Write tw=tmeshfaces.write();
+ int tidx=0;
+
+
+ for(List<Face3>::Element *E=faces.front();E;E=E->next()) {
+
+ const Face3 &f = E->get();
+
+ for(int j=0;j<3;j++) {
+
+ tw[tidx++]=f.vertex[j];
+ _EdgeKey ek;
+ ek.from=f.vertex[j].snapped(CMP_EPSILON);
+ ek.to=f.vertex[(j+1)%3].snapped(CMP_EPSILON);
+ if (ek.from<ek.to)
+ SWAP(ek.from,ek.to);
+
+ Map<_EdgeKey,bool>::Element *E=edge_map.find(ek);
+
+ if (E) {
+
+ E->get()=false;
+
+ } else {
+
+ edge_map[ek]=true;
+ }
+
+ }
+ }
+ }
+ Vector<Vector3> lines;
+
+ for(Map<_EdgeKey,bool>::Element *E=edge_map.front();E;E=E->next()) {
+
+ if (E->get()) {
+ lines.push_back(E->key().from);
+ lines.push_back(E->key().to);
+ }
+ }
+
+ Ref<TriangleMesh> tmesh = memnew( TriangleMesh);
+ tmesh->create(tmeshfaces);
+
+ add_lines(lines,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_edge_material:SpatialEditorGizmos::singleton->navmesh_edge_material_disabled);
+ add_collision_triangles(tmesh);
+ Ref<Mesh> m = memnew( Mesh );
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[0]=tmeshfaces;
+ m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
+ m->surface_set_material(0,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_solid_material:SpatialEditorGizmos::singleton->navmesh_solid_material_disabled);
+ add_mesh(m);
+ add_collision_segments(lines);
+
+}
+
+NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh){
+
+ set_spatial_node(p_navmesh);
+ navmesh=p_navmesh;
+}
+
+//////
+///
+///
+
+
+
+void PinJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs));
+ cursor_points.push_back(Vector3(0,0,-cs));
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+////
+
+void HingeJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));*/
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2));
+
+ float ll = p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER);
+ float ul = p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER);
+
+ if (p3d->get_flag(HingeJoint::FLAG_USE_LIMIT) && ll<ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
+ Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ cursor_points.push_back(Vector3(0,cs*1.5,0));
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+ Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
+ Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+void SliderJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));*/
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2));
+
+ float ll = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER);
+ float ul = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER);
+ float lll = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER);
+ float lul = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER);
+
+ if (lll>lul) {
+
+ cursor_points.push_back(Vector3(lul,0,0));
+ cursor_points.push_back(Vector3(lll,0,0));
+
+ cursor_points.push_back(Vector3(lul,-cs,-cs));
+ cursor_points.push_back(Vector3(lul,-cs,cs));
+ cursor_points.push_back(Vector3(lul,-cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,-cs));
+ cursor_points.push_back(Vector3(lul,cs,-cs));
+ cursor_points.push_back(Vector3(lul,-cs,-cs));
+
+
+ cursor_points.push_back(Vector3(lll,-cs,-cs));
+ cursor_points.push_back(Vector3(lll,-cs,cs));
+ cursor_points.push_back(Vector3(lll,-cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,-cs));
+ cursor_points.push_back(Vector3(lll,cs,-cs));
+ cursor_points.push_back(Vector3(lll,-cs,-cs));
+
+
+ } else {
+
+ cursor_points.push_back(Vector3(+cs*2,0,0));
+ cursor_points.push_back(Vector3(-cs*2,0,0));
+
+ }
+
+ if (ll<ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from=Vector3(0, Math::cos(s), -Math::sin(s) )*cs;
+ Vector3 to=Vector3(0,Math::cos(n), -Math::sin(n) )*cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ cursor_points.push_back(Vector3(0,cs*1.5,0));
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+ Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
+ Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+void ConeTwistJointSpatialGizmo::redraw() {
+
+ clear();
+ float cs = 0.25;
+ Vector<Vector3> points;
+
+ float r = 1.0;
+ float w = r*Math::sin(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
+ float d = r*Math::cos(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
+
+
+ //swing
+ for(int i=0;i<360;i+=10) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+10);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+ points.push_back(Vector3(d,a.x,a.y));
+ points.push_back(Vector3(d,b.x,b.y));
+
+ if (i%90==0) {
+
+ points.push_back(Vector3(d,a.x,a.y));
+ points.push_back(Vector3());
+
+ }
+ }
+
+ points.push_back(Vector3());
+ points.push_back(Vector3(1,0,0));
+
+ //twist
+ /*
+ */
+ float ts=Math::rad2deg(p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN));
+ ts=MIN(ts,720);
+
+
+ for(int i=0;i<int(ts);i+=5) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+5);
+ float c = i/720.0;
+ float cn = (i+5)/720.0;
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w*c;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w*cn;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+
+ points.push_back(Vector3(c,a.x,a.y));
+ points.push_back(Vector3(cn,b.x,b.y));
+
+ }
+
+
+ add_collision_segments(points);
+ add_lines(points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
////////
+/// \brief SpatialEditorGizmos::singleton
+///
+///////
+///
+////
+
+void Generic6DOFJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+
+ for(int ax=0;ax<3;ax++) {
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2)); */
+
+ float ll;
+ float ul;
+ float lll;
+ float lul;
+
+ int a1,a2,a3;
+ bool enable_ang;
+ bool enable_lin;
+
+ switch(ax) {
+ case 0:
+ ll = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+ a1=0;
+ a2=1;
+ a3=2;
+ break;
+ case 1:
+ ll = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+ a1=2;
+ a2=0;
+ a3=1;
+ break;
+ case 2:
+ ll = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+
+ a1=1;
+ a2=2;
+ a3=0;
+ break;
+ }
+
+#define ADD_VTX(x,y,z)\
+ {\
+ Vector3 v;\
+ v[a1]=(x);\
+ v[a2]=(y);\
+ v[a3]=(z);\
+ cursor_points.push_back(v);\
+ }
+
+#define SET_VTX(what,x,y,z)\
+ {\
+ Vector3 v;\
+ v[a1]=(x);\
+ v[a2]=(y);\
+ v[a3]=(z);\
+ what=v;\
+ }
+
+
+
+
+ if (enable_lin && lll>=lul) {
+
+ ADD_VTX(lul,0,0);
+ ADD_VTX(lll,0,0);
+
+ ADD_VTX(lul,-cs,-cs);
+ ADD_VTX(lul,-cs,cs);
+ ADD_VTX(lul,-cs,cs);
+ ADD_VTX(lul,cs,cs);
+ ADD_VTX(lul,cs,cs);
+ ADD_VTX(lul,cs,-cs);
+ ADD_VTX(lul,cs,-cs);
+ ADD_VTX(lul,-cs,-cs);
+
+
+ ADD_VTX(lll,-cs,-cs);
+ ADD_VTX(lll,-cs,cs);
+ ADD_VTX(lll,-cs,cs);
+ ADD_VTX(lll,cs,cs);
+ ADD_VTX(lll,cs,cs);
+ ADD_VTX(lll,cs,-cs);
+ ADD_VTX(lll,cs,-cs);
+ ADD_VTX(lll,-cs,-cs);
+
+
+ } else {
+
+ ADD_VTX(+cs*2,0,0);
+ ADD_VTX(-cs*2,0,0);
+
+ }
+
+ if (enable_ang && ll<=ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from;
+ SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
+ from*=cs;
+ Vector3 to;
+ SET_VTX(to,0,Math::cos(n), -Math::sin(n));
+ to*=cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ ADD_VTX(0,cs*1.5,0);
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+// Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
+// Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
+
+ Vector3 from;
+ SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
+ from*=cs;
+ Vector3 to;
+ SET_VTX(to,0,Math::cos(n), -Math::sin(n));
+ to*=cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ }
+
+#undef ADD_VTX
+#undef SET_VTX
+
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+
SpatialEditorGizmos *SpatialEditorGizmos::singleton=NULL;
Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
@@ -2144,6 +2845,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) { return misg;
}
+ if (p_spatial->cast_to<NavigationMeshInstance>()) {
+
+ Ref<NavigationMeshSpatialGizmo> misg = memnew( NavigationMeshSpatialGizmo(p_spatial->cast_to<NavigationMeshInstance>()) );
+ return misg;
+ }
+
if (p_spatial->cast_to<RayCast>()) {
Ref<RayCastSpatialGizmo> misg = memnew( RayCastSpatialGizmo(p_spatial->cast_to<RayCast>()) );
@@ -2191,6 +2898,47 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) { Ref<CarWheelSpatialGizmo> misg = memnew( CarWheelSpatialGizmo(p_spatial->cast_to<CarWheel>()) );
return misg;
}
+ if (p_spatial->cast_to<VehicleWheel>()) {
+
+ Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) );
+ return misg;
+ }
+ if (p_spatial->cast_to<PinJoint>()) {
+
+ Ref<PinJointSpatialGizmo> misg = memnew( PinJointSpatialGizmo(p_spatial->cast_to<PinJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<HingeJoint>()) {
+
+ Ref<HingeJointSpatialGizmo> misg = memnew( HingeJointSpatialGizmo(p_spatial->cast_to<HingeJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<SliderJoint>()) {
+
+ Ref<SliderJointSpatialGizmo> misg = memnew( SliderJointSpatialGizmo(p_spatial->cast_to<SliderJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<ConeTwistJoint>()) {
+
+ Ref<ConeTwistJointSpatialGizmo> misg = memnew( ConeTwistJointSpatialGizmo(p_spatial->cast_to<ConeTwistJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<Generic6DOFJoint>()) {
+
+ Ref<Generic6DOFJointSpatialGizmo> misg = memnew( Generic6DOFJointSpatialGizmo(p_spatial->cast_to<Generic6DOFJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<CollisionPolygon>()) {
+
+ Ref<CollisionPolygonSpatialGizmo> misg = memnew( CollisionPolygonSpatialGizmo(p_spatial->cast_to<CollisionPolygon>()) );
+ return misg;
+ }
+
return Ref<SpatialEditorGizmo>();
}
@@ -2209,6 +2957,17 @@ Ref<FixedMaterial> SpatialEditorGizmos::create_line_material(const Color& p_base }
+Ref<FixedMaterial> SpatialEditorGizmos::create_solid_material(const Color& p_base_color) {
+
+ Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ line_material->set_flag(Material::FLAG_UNSHADED, true);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
+
+ return line_material;
+
+}
+
SpatialEditorGizmos::SpatialEditorGizmos() {
singleton=this;
@@ -2249,6 +3008,16 @@ SpatialEditorGizmos::SpatialEditorGizmos() { camera_material = create_line_material(Color(1.0,0.5,1.0));
+ navmesh_edge_material = create_line_material(Color(0.1,0.8,1.0));
+ navmesh_solid_material = create_solid_material(Color(0.1,0.8,1.0,0.4));
+ navmesh_edge_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
+ navmesh_solid_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+
+ navmesh_edge_material_disabled = create_line_material(Color(1.0,0.8,0.1));
+ navmesh_solid_material_disabled = create_solid_material(Color(1.0,0.8,0.1,0.4));
+ navmesh_edge_material_disabled->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
+ navmesh_solid_material_disabled->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+
skeleton_material = create_line_material(Color(0.6,1.0,0.3));
skeleton_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
skeleton_material->set_flag(Material::FLAG_UNSHADED,true);
@@ -2303,6 +3072,7 @@ SpatialEditorGizmos::SpatialEditorGizmos() { raycast_material = create_line_material(Color(1.0,0.8,0.6));
car_wheel_material = create_line_material(Color(0.6,0.8,1.0));
visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0));
+ joint_material = create_line_material(Color(0.6,0.8,1.0));
stream_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
stream_player_icon->set_flag(Material::FLAG_UNSHADED, true);
diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h index 8176157bc9..ac31eb19e9 100644 --- a/tools/editor/spatial_editor_gizmos.h +++ b/tools/editor/spatial_editor_gizmos.h @@ -43,7 +43,11 @@ #include "scene/3d/visibility_notifier.h"
#include "scene/3d/portal.h"
#include "scene/3d/ray_cast.h"
+#include "scene/3d/navigation_mesh.h"
#include "scene/3d/car_body.h"
+#include "scene/3d/vehicle_body.h"
+#include "scene/3d/collision_polygon.h"
+#include "scene/3d/physics_joint.h"
class Camera;
@@ -299,6 +303,21 @@ public: };
+
+class CollisionPolygonSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(CollisionPolygonSpatialGizmo,SpatialGizmoTool);
+
+ CollisionPolygon* polygon;
+
+public:
+
+ void redraw();
+ CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon=NULL);
+
+};
+
+
class RayCastSpatialGizmo : public SpatialGizmoTool {
OBJ_TYPE(RayCastSpatialGizmo,SpatialGizmoTool);
@@ -327,11 +346,118 @@ public: };
+class VehicleWheelSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool);
+
+ VehicleWheel* car_wheel;
+
+public:
+
+ void redraw();
+ VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL);
+
+};
+
+
+class NavigationMeshSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool);
+
+
+ struct _EdgeKey {
+
+ Vector3 from;
+ Vector3 to;
+
+ bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
+ };
+
+
+
+ NavigationMeshInstance* navmesh;
+
+public:
+
+ void redraw();
+ NavigationMeshSpatialGizmo(NavigationMeshInstance* p_navmesh=NULL);
+
+};
+
+
+class PinJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(PinJointSpatialGizmo,SpatialGizmoTool);
+
+ PinJoint* p3d;
+
+public:
+
+ void redraw();
+ PinJointSpatialGizmo(PinJoint* p_p3d=NULL);
+
+};
+
+
+class HingeJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(HingeJointSpatialGizmo,SpatialGizmoTool);
+
+ HingeJoint* p3d;
+
+public:
+
+ void redraw();
+ HingeJointSpatialGizmo(HingeJoint* p_p3d=NULL);
+
+};
+
+class SliderJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(SliderJointSpatialGizmo,SpatialGizmoTool);
+
+ SliderJoint* p3d;
+
+public:
+
+ void redraw();
+ SliderJointSpatialGizmo(SliderJoint* p_p3d=NULL);
+
+};
+
+class ConeTwistJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(ConeTwistJointSpatialGizmo,SpatialGizmoTool);
+
+ ConeTwistJoint* p3d;
+
+public:
+
+ void redraw();
+ ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d=NULL);
+
+};
+
+
+class Generic6DOFJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(Generic6DOFJointSpatialGizmo,SpatialGizmoTool);
+
+ Generic6DOFJoint* p3d;
+
+public:
+
+ void redraw();
+ Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d=NULL);
+
+};
+
class SpatialEditorGizmos {
public:
Ref<FixedMaterial> create_line_material(const Color& p_base_color);
+ Ref<FixedMaterial> create_solid_material(const Color& p_base_color);
Ref<FixedMaterial> handle2_material;
Ref<FixedMaterial> handle_material;
Ref<FixedMaterial> light_material;
@@ -344,6 +470,13 @@ public: Ref<FixedMaterial> raycast_material;
Ref<FixedMaterial> visibility_notifier_material;
Ref<FixedMaterial> car_wheel_material;
+ Ref<FixedMaterial> joint_material;
+
+ Ref<FixedMaterial> navmesh_edge_material;
+ Ref<FixedMaterial> navmesh_solid_material;
+ Ref<FixedMaterial> navmesh_edge_material_disabled;
+ Ref<FixedMaterial> navmesh_solid_material_disabled;
+
Ref<FixedMaterial> sample_player_icon;
Ref<FixedMaterial> stream_player_icon;
diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py index 1a0cb37a17..cd785fca40 100644 --- a/tools/export/blender25/io_scene_dae/export_dae.py +++ b/tools/export/blender25/io_scene_dae/export_dae.py @@ -293,6 +293,9 @@ class DaeExporter: self.writel(S_FX,6,'</bump>') self.writel(S_FX,5,'</technique>') + self.writel(S_FX,5,'<technique profile="GOOGLEEARTH">') + self.writel(S_FX,6,'<double_sided>'+["0","1"][double_sided_hint]+"</double_sided>") + self.writel(S_FX,5,'</technique>') self.writel(S_FX,4,'</extra>') self.writel(S_FX,3,'</technique>') @@ -314,14 +317,14 @@ class DaeExporter: def export_mesh(self,node,armature=None): + if (node.data in self.mesh_cache): + return self.mesh_cache[mesh] + if (len(node.modifiers) and self.config["use_mesh_modifiers"]): mesh=node.to_mesh(self.scene,True,"RENDER") #is this allright? else: mesh=node.data - if (mesh in self.mesh_cache): - return self.mesh_cache[mesh] - mesh.update(calc_tessface=True) vertices=[] vertex_map={} @@ -359,7 +362,7 @@ class DaeExporter: mat= None if (mat!=None): - materials[f.material_index]=self.export_material( mat ) + materials[f.material_index]=self.export_material( mat,mesh.show_double_sided ) else: materials[f.material_index]=None #weird, has no material? @@ -516,7 +519,7 @@ class DaeExporter: meshdata={} meshdata["id"]=meshid meshdata["material_assign"]=mat_assign - self.mesh_cache[mesh]=meshdata + self.mesh_cache[node.data]=meshdata # Export armature data (if armature exists) @@ -730,7 +733,7 @@ class DaeExporter: self.writel(S_LAMPS,2,'<optics>') self.writel(S_LAMPS,3,'<technique_common>') - if (light.type=="POINT" or light.type=="HEMI"): + if (light.type=="POINT"): self.writel(S_LAMPS,4,'<point>') self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>') att_by_distance = 2.0 / light.distance # convert to linear attenuation @@ -1030,7 +1033,7 @@ class DaeExporter: return [anim_id] - def export_animation(self,start,end): + def export_animation(self,start,end,allowed=None): #Blender -> Collada frames needs a little work #Collada starts from 0, blender usually from 1 @@ -1047,7 +1050,7 @@ class DaeExporter: # Change frames first, export objects last # This improves performance enormously - print("anim from: "+str(start)+" to "+str(end)) + print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed)) for t in range(start,end+1): self.scene.frame_set(t) key = t * frame_len - frame_sub @@ -1057,6 +1060,8 @@ class DaeExporter: if (not node in self.valid_nodes): continue + if (allowed!=None and not (node in allowed)): + continue if (node.type=="MESH" and node.parent and node.parent.type=="ARMATURE"): continue #In Collada, nodes that have skin modifier must not export animation, animate the skin instead. @@ -1080,6 +1085,7 @@ class DaeExporter: bone_name=self.skeleton_info[node]["bone_ids"][bone] if (not (bone_name in xform_cache)): + print("has bone: "+bone_name) xform_cache[bone_name]=[] posebone = node.pose.bones[bone.name] @@ -1088,7 +1094,14 @@ class DaeExporter: mtx = posebone.matrix.copy() if (bone.parent): parent_posebone=node.pose.bones[bone.parent.name] - mtx = parent_posebone.matrix.inverted() * mtx + parent_invisible=False + + for i in range(3): + if (parent_posebone.scale[i]==0.0): + parent_invisible=True + + if (not parent_invisible): + mtx = parent_posebone.matrix.inverted() * mtx xform_cache[bone_name].append( (key,mtx) ) @@ -1113,12 +1126,33 @@ class DaeExporter: for x in bpy.data.actions[:]: if x in self.action_constraints: continue + + bones=[] + #find bones used + for p in x.fcurves: + dp = str(p.data_path) + base = "pose.bones[\"" + if (dp.find(base)==0): + dp=dp[len(base):] + if (dp.find('"')!=-1): + dp=dp[:dp.find('"')] + if (not dp in bones): + bones.append(dp) + + allowed_skeletons=[] for y in self.skeletons: if (y.animation_data): + for z in y.pose.bones: + if (z.bone.name in bones): + if (not y in allowed_skeletons): + allowed_skeletons.append(y) y.animation_data.action=x; - tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1])) + + print(str(x)) + + tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]),allowed_skeletons) framelen=(1.0/self.scene.render.fps) start = x.frame_range[0]*framelen end = x.frame_range[1]*framelen diff --git a/tools/pck/SCsub b/tools/pck/SCsub new file mode 100644 index 0000000000..b1fed9a472 --- /dev/null +++ b/tools/pck/SCsub @@ -0,0 +1,5 @@ +Import('env') + +if env["tools"] == "yes": + env.add_source_files(env.tool_sources, "*.cpp") + diff --git a/tools/pck/pck_packer.cpp b/tools/pck/pck_packer.cpp new file mode 100644 index 0000000000..09611b3a93 --- /dev/null +++ b/tools/pck/pck_packer.cpp @@ -0,0 +1,163 @@ +#include "pck_packer.h" + +#include "core/os/file_access.h" + +static uint64_t _align(uint64_t p_n, int p_alignment) { + + if (p_alignment == 0) + return p_n; + + uint64_t rest = p_n % p_alignment; + if (rest == 0) + return p_n; + else + return p_n + (p_alignment - rest); +}; + +static void _pad(FileAccess* p_file, int p_bytes) { + + for (int i=0; i<p_bytes; i++) { + + p_file->store_8(0); + }; +}; + +void PCKPacker::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("pck_start","pck_name","alignment"),&PCKPacker::pck_start); + ObjectTypeDB::bind_method(_MD("add_file","pck_path","source_path"),&PCKPacker::add_file); + ObjectTypeDB::bind_method(_MD("flush"),&PCKPacker::flush); +}; + + +Error PCKPacker::pck_start(const String& p_file, int p_alignment) { + + file = FileAccess::open(p_file, FileAccess::WRITE); + if (file == NULL) { + + return ERR_CANT_CREATE; + }; + + alignment = p_alignment; + + file->store_32(0x43504447); // MAGIC + file->store_32(0); // # version + file->store_32(0); // # major + file->store_32(0); // # minor + file->store_32(0); // # revision + + for (int i=0; i<16; i++) { + + file->store_32(0); // reserved + }; + + files.clear(); + + return OK; +}; + +Error PCKPacker::add_file(const String& p_file, const String& p_src) { + + FileAccess* f = FileAccess::open(p_src, FileAccess::READ); + if (!f) { + return ERR_FILE_CANT_OPEN; + }; + + File pf; + pf.path = p_file; + pf.src_path = p_src; + pf.size = f->get_len(); + pf.offset_offset = 0; + + files.push_back(pf); + + f->close(); + memdelete(f); + + return OK; +}; + +Error PCKPacker::flush(bool p_verbose) { + + if (!file) { + ERR_FAIL_COND_V(!file, ERR_INVALID_PARAMETER); + return ERR_INVALID_PARAMETER; + }; + + // write the index + + file->store_32(files.size()); + + for (int i=0; i<files.size(); i++) { + + file->store_pascal_string(files[i].path); + files[i].offset_offset = file->get_pos(); + file->store_64(0); // offset + file->store_64(files[i].size); // size + + // # empty md5 + file->store_32(0); + file->store_32(0); + file->store_32(0); + file->store_32(0); + }; + + + uint64_t ofs = file->get_pos(); + ofs = _align(ofs, alignment); + + _pad(file, ofs - file->get_pos()); + + const uint32_t buf_max = 65536; + uint8_t *buf = memnew_arr(uint8_t, buf_max); + + int count = 0; + for (int i=0; i<files.size(); i++) { + + FileAccess* src = FileAccess::open(files[i].src_path, FileAccess::READ); + uint64_t to_write = files[i].size; + while (to_write > 0) { + + int read = src->get_buffer(buf, MIN(to_write, buf_max)); + file->store_buffer(buf, read); + to_write -= read; + }; + + uint64_t pos = file->get_pos(); + file->seek(files[i].offset_offset); // go back to store the file's offset + file->store_64(ofs); + file->seek(pos); + + ofs = _align(ofs + files[i].size, alignment); + _pad(file, ofs - pos); + + src->close(); + memdelete(src); + count += 1; + if (p_verbose) { + if (count % 100 == 0) { + printf("%i/%i (%.2f\%)\r", count, files.size(), float(count) / files.size() * 100); + fflush(stdout); + }; + }; + }; + + if (p_verbose) + printf("\n"); + + file->close(); + + return OK; +}; + +PCKPacker::PCKPacker() { + + file = NULL; +}; + +PCKPacker::~PCKPacker() { + if (file != NULL) { + memdelete(file); + }; + file = NULL; +}; diff --git a/tools/pck/pck_packer.h b/tools/pck/pck_packer.h new file mode 100644 index 0000000000..76752a6170 --- /dev/null +++ b/tools/pck/pck_packer.h @@ -0,0 +1,31 @@ +#include "core/object.h" + +class FileAccess; + +class PCKPacker : public Object { + + OBJ_TYPE(PCKPacker, Object); + + FileAccess* file; + int alignment; + + static void _bind_methods(); + + struct File { + + String path; + String src_path; + int size; + uint64_t offset_offset; + }; + Vector<File> files; + +public: + Error pck_start(const String& p_file, int p_alignment); + Error add_file(const String& p_file, const String& p_src); + Error flush(bool p_verbose = false); + + + PCKPacker(); + ~PCKPacker(); +}; |