|
@@ -1154,12 +1154,17 @@ class SceneEditor {
|
|
|
{ label : "Select all", click : selectAll, keys : view.config.get("key.selectAll") },
|
|
|
{ label : "Select children", enabled : current != null, click : function() selectElements(current.flatten()) },
|
|
|
]);
|
|
|
- if( !isRef )
|
|
|
+ if( !isRef ) {
|
|
|
+ var exportMenu = new Array<hide.comp.ContextMenu.ContextMenuItem>();
|
|
|
+ exportMenu.push({ label : "Export (default)", enabled : curEdit != null && canExportSelection(), click : function() exportSelection({forward:"0", forwardSign:"1", up:"2", upSign:"1"}), keys : null });
|
|
|
+ exportMenu.push({ label : "Export (-X Forward, Z Up)", enabled : curEdit != null && canExportSelection(), click : function() exportSelection({forward:"0", forwardSign:"-1", up:"2", upSign:"1"}), keys : null });
|
|
|
+
|
|
|
actionItems = actionItems.concat([
|
|
|
{ label : "Isolate", click : function() isolate(curEdit.elements), keys : view.config.get("key.sceneeditor.isolate") },
|
|
|
{ label : "Group", enabled : curEdit != null && canGroupSelection(), click : groupSelection, keys : view.config.get("key.group") },
|
|
|
- { label : "Export", enabled : curEdit != null && canExportSelection(), click : exportSelection, keys : null },
|
|
|
+ { label : "Export", enabled : curEdit != null && canExportSelection(), menu : exportMenu },
|
|
|
]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if( current != null ) {
|
|
@@ -2759,6 +2764,7 @@ class SceneEditor {
|
|
|
}
|
|
|
|
|
|
function canExportSelection() {
|
|
|
+ return true;
|
|
|
var elts = curEdit.rootElements;
|
|
|
if(elts.length == 0)
|
|
|
return false;
|
|
@@ -2793,13 +2799,185 @@ class SceneEditor {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- function exportSelection() {
|
|
|
+ function exportSelection(?params : Dynamic) {
|
|
|
+ function clone(obj : h3d.scene.Object, ?into : h3d.scene.Mesh) : h3d.scene.Mesh {
|
|
|
+ var o : h3d.scene.Mesh = null;
|
|
|
+ if (into != null) {
|
|
|
+ o = into;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ var m = Std.downcast(obj, h3d.scene.Mesh);
|
|
|
+ o = new h3d.scene.Mesh(m.primitive, m.material, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ o.x = obj.x;
|
|
|
+ o.y = obj.y;
|
|
|
+ o.z = obj.z;
|
|
|
+ o.scaleX = obj.scaleX;
|
|
|
+ o.scaleY = obj.scaleY;
|
|
|
+ o.scaleZ = obj.scaleZ;
|
|
|
+ @:privateAccess o.qRot.load(obj.qRot);
|
|
|
+ o.name = obj.name;
|
|
|
+ o.follow = obj.follow;
|
|
|
+ o.followPositionOnly = obj.followPositionOnly;
|
|
|
+ o.visible = obj.visible;
|
|
|
+ if( obj.defaultTransform != null ) {
|
|
|
+ if (o.defaultTransform != null)
|
|
|
+ o.defaultTransform = o.defaultTransform.multiplied(obj.defaultTransform.clone());
|
|
|
+ else
|
|
|
+ o.defaultTransform = obj.defaultTransform.clone();
|
|
|
+ }
|
|
|
+ return o;
|
|
|
+ }
|
|
|
+
|
|
|
+ function deepClone(m : h3d.scene.Mesh, ?parent : h3d.scene.Mesh) {
|
|
|
+ var copy = clone(m);
|
|
|
+
|
|
|
+ if (parent != null)
|
|
|
+ parent.addChild(copy);
|
|
|
+
|
|
|
+ for (child in @:privateAccess m.children) {
|
|
|
+ var childMesh = Std.downcast(child, h3d.scene.Mesh);
|
|
|
+
|
|
|
+ if (childMesh != null)
|
|
|
+ deepClone(childMesh, copy);
|
|
|
+ }
|
|
|
+
|
|
|
+ return copy;
|
|
|
+ }
|
|
|
+
|
|
|
+ function applyInverseDefaultTransform(obj : h3d.scene.Object, defaultTransform : h3d.Matrix) {
|
|
|
+ if (defaultTransform != null) {
|
|
|
+ var t = obj.getTransform().multiplied(defaultTransform.getInverse());
|
|
|
+ obj.x = t.getPosition().x;
|
|
|
+ obj.y = t.getPosition().y;
|
|
|
+ obj.z = t.getPosition().z;
|
|
|
+
|
|
|
+ var q : h3d.Quat = new h3d.Quat();
|
|
|
+ q.initRotation(t.getEulerAngles().x, t.getEulerAngles().y, t.getEulerAngles().z);
|
|
|
+ @:privateAccess obj.qRot.w = q.w;
|
|
|
+ @:privateAccess obj.qRot.x = q.x;
|
|
|
+ @:privateAccess obj.qRot.y = q.y;
|
|
|
+ @:privateAccess obj.qRot.z = q.z;
|
|
|
+
|
|
|
+ obj.scaleX = t.getScale().x;
|
|
|
+ obj.scaleY = t.getScale().y;
|
|
|
+ obj.scaleZ = t.getScale().z;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var roots : Array<h3d.scene.Mesh> = [];
|
|
|
+ function extractMeshes( o : h3d.scene.Object, ?parent : h3d.scene.Mesh) {
|
|
|
+ var m = Std.downcast(o, h3d.scene.Mesh);
|
|
|
+
|
|
|
+ // If this object isn't a mesh, we have to find into his children which mesh(s)
|
|
|
+ // is/are associated to it
|
|
|
+ if (m == null) {
|
|
|
+ var associatedMesh : h3d.scene.Object = null;
|
|
|
+
|
|
|
+ for (c in @:privateAccess o.children)
|
|
|
+ if (c.name == "root") // Associated mesh is named root (see makeInstance() of Model)
|
|
|
+ associatedMesh = c;
|
|
|
+
|
|
|
+ if (associatedMesh != null) {
|
|
|
+ var m2 = Std.downcast(associatedMesh, h3d.scene.Mesh);
|
|
|
+ var alreadyExtracted = new Array<h3d.scene.Object>();
|
|
|
+
|
|
|
+ if (m2 != null) {
|
|
|
+ // Single mesh object
|
|
|
+ var mesh = clone(m2);
|
|
|
+
|
|
|
+ // Since it's the parent object that is holding informations
|
|
|
+ // apply it on mesh object
|
|
|
+ clone(o, mesh);
|
|
|
+
|
|
|
+ if (parent == null)
|
|
|
+ roots.push(mesh);
|
|
|
+ else {
|
|
|
+ parent.addChild(mesh);
|
|
|
+ applyInverseDefaultTransform(mesh, parent.defaultTransform);
|
|
|
+ }
|
|
|
+
|
|
|
+ parent = cast mesh;
|
|
|
+ alreadyExtracted.push(m2);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Multiple mesh object
|
|
|
+ var randomRootMesh : h3d.scene.Mesh = null;
|
|
|
+ for (c in @:privateAccess associatedMesh.children) {
|
|
|
+ var mesh = Std.downcast(c, h3d.scene.Mesh);
|
|
|
+
|
|
|
+ if (mesh != null) {
|
|
|
+ var copy = deepClone(mesh);
|
|
|
+ clone(o, copy);
|
|
|
+
|
|
|
+ if (parent == null)
|
|
|
+ roots.push(copy);
|
|
|
+ else {
|
|
|
+ parent.addChild(copy);
|
|
|
+ applyInverseDefaultTransform(copy, parent.defaultTransform);
|
|
|
+ }
|
|
|
+ randomRootMesh = copy;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ parent = randomRootMesh;
|
|
|
+ alreadyExtracted.push(associatedMesh);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Continue to extract meshes on other objects
|
|
|
+ for (c in @:privateAccess o.children) {
|
|
|
+ if (alreadyExtracted.contains(c))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ extractMeshes(c, parent);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // We're in the case where there is multiple meshes
|
|
|
+ // and no children
|
|
|
+ for (c in @:privateAccess o.children) {
|
|
|
+ var mesh = Std.downcast(c, h3d.scene.Mesh);
|
|
|
+
|
|
|
+ if (mesh != null) {
|
|
|
+ var copy = deepClone(mesh);
|
|
|
+ clone(o, copy);
|
|
|
+
|
|
|
+ if (parent == null)
|
|
|
+ roots.push(copy);
|
|
|
+ else {
|
|
|
+ parent.addChild(copy);
|
|
|
+ applyInverseDefaultTransform(copy, parent.defaultTransform);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ var copy = clone(m);
|
|
|
+ if (parent == null)
|
|
|
+ roots.push(copy);
|
|
|
+ else {
|
|
|
+ parent.addChild(copy);
|
|
|
+ applyInverseDefaultTransform(copy, parent.defaultTransform);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // We have to extract meshes object from the incomming objects to remove
|
|
|
+ // non-needed parent objects that are created in hide in make instance
|
|
|
+ // of model
|
|
|
+ for (o in @:privateAccess curEdit.rootObjects)
|
|
|
+ extractMeshes(o);
|
|
|
+
|
|
|
// Handle the export of selection into a fbx file
|
|
|
Ide.inst.chooseFileSave("Export.fbx", function(filePath) {
|
|
|
var out = new haxe.io.BytesOutput();
|
|
|
- new hxd.fmt.fbx.Writer(out).write(cast @:privateAccess curEdit.rootObjects);
|
|
|
+ new hxd.fmt.fbx.Writer(out).write(cast curEdit.rootObjects, params);
|
|
|
sys.io.File.saveBytes(filePath, out.getBytes());
|
|
|
+ Ide.inst.message('Successfully exported object at path : ${filePath}');
|
|
|
});
|
|
|
+
|
|
|
}
|
|
|
|
|
|
function groupSelection() {
|