summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/SCsub1
-rw-r--r--tools/editor/editor_import_export.cpp4
-rw-r--r--tools/editor/editor_node.cpp3
-rw-r--r--tools/editor/io_plugins/editor_import_collada.cpp76
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.cpp32
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.h8
-rw-r--r--tools/editor/io_plugins/editor_scene_importer_fbxconv.cpp1106
-rw-r--r--tools/editor/io_plugins/editor_scene_importer_fbxconv.h81
-rw-r--r--tools/editor/plugins/baked_light_baker.cpp4
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.cpp10
-rw-r--r--tools/editor/spatial_editor_gizmos.cpp122
-rw-r--r--tools/editor/spatial_editor_gizmos.h32
-rw-r--r--tools/export/blender25/io_scene_dae/export_dae.py30
-rw-r--r--tools/pck/SCsub5
-rw-r--r--tools/pck/pck_packer.cpp162
-rw-r--r--tools/pck/pck_packer.h31
16 files changed, 1655 insertions, 52 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/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp
index 649db5fc45..8f9195110b 100644
--- a/tools/editor/editor_import_export.cpp
+++ b/tools/editor/editor_import_export.cpp
@@ -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..6ac72fc401 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"
@@ -4048,6 +4049,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))));
diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp
index a0f1626b03..15a671d623 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;
@@ -1088,7 +1088,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();
@@ -1705,7 +1705,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 +1737,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 +1980,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 +2151,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 +2190,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..3cc8ffd151 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
};
@@ -806,7 +809,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 );
@@ -1229,6 +1232,27 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>
sb->add_child(colshape);
colshape->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>()) {
@@ -1737,10 +1761,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;
diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h
index 928fff2afb..114233df80 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
};
@@ -160,7 +161,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/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp
index 1fa4d8d06c..2be6d3da24 100644
--- a/tools/editor/plugins/baked_light_baker.cpp
+++ b/tools/editor/plugins/baked_light_baker.cpp
@@ -720,7 +720,7 @@ void BakedLightBaker::_plot_light(int p_light_index, const Vector3& p_plot_pos,
float intensity = 1.0 - (d/r)*(d/r); //not gauss but..
float damp = Math::abs(p_plane.normal.dot(Vector3(octant.normal_accum[i][0],octant.normal_accum[i][1],octant.normal_accum[i][2])));
intensity*=pow(damp,edge_damp);
- //intensity*=1.0-Math::abs(p_plane.distance_to(pos))/(plot_size*cell_size);
+ intensity*=1.0-Math::abs(p_plane.distance_to(pos))/(plot_size*cell_size);
octant.light[p_light_index].accum[i][0]+=p_light.r*intensity;
octant.light[p_light_index].accum[i][1]+=p_light.g*intensity;
octant.light[p_light_index].accum[i][2]+=p_light.b*intensity;
@@ -1023,7 +1023,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);
diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp
index 1bf425f3f3..e9020c91f5 100644
--- a/tools/editor/plugins/spatial_editor_plugin.cpp
+++ b/tools/editor/plugins/spatial_editor_plugin.cpp
@@ -2345,8 +2345,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 +2652,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 );
diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp
index 83efb48bd3..c5bf43884e 100644
--- a/tools/editor/spatial_editor_gizmos.cpp
+++ b/tools/editor/spatial_editor_gizmos.cpp
@@ -2100,6 +2100,101 @@ 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;
+}
////////
@@ -2144,6 +2239,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>()) );
@@ -2209,6 +2310,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 +2361,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);
diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h
index 8176157bc9..e5c3417166 100644
--- a/tools/editor/spatial_editor_gizmos.h
+++ b/tools/editor/spatial_editor_gizmos.h
@@ -43,6 +43,7 @@
#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"
@@ -327,11 +328,36 @@ public:
};
+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 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;
@@ -345,6 +371,12 @@ public:
Ref<FixedMaterial> visibility_notifier_material;
Ref<FixedMaterial> car_wheel_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;
Ref<FixedMaterial> visibility_notifier_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..15ec77cf8f 100644
--- a/tools/export/blender25/io_scene_dae/export_dae.py
+++ b/tools/export/blender25/io_scene_dae/export_dae.py
@@ -1030,7 +1030,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 +1047,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 +1057,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 +1082,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]
@@ -1113,12 +1116,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..aade9fafe1
--- /dev/null
+++ b/tools/pck/pck_packer.cpp
@@ -0,0 +1,162 @@
+#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);
+ };
+ };
+ };
+
+ 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();
+};