浏览代码

WebGL 2 export per WebAssembly or asm.js

eska 8 年之前
父节点
当前提交
7df7e9cc8b

+ 4 - 1
editor/editor_export.cpp

@@ -75,11 +75,14 @@ bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const {
 
 	return false;
 }
+
 void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
 
 	for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
 
-		p_list->push_back(E->get());
+		if (platform->get_option_visibility(E->get().name, values)) {
+			p_list->push_back(E->get());
+		}
 	}
 }
 

+ 3 - 0
editor/editor_export.h

@@ -76,6 +76,7 @@ protected:
 
 public:
 	Ref<EditorExportPlatform> get_platform() const;
+
 	bool has(const StringName &p_property) const { return values.has(p_property); }
 
 	Vector<String> get_files_to_export() const;
@@ -170,6 +171,8 @@ public:
 	virtual Ref<EditorExportPreset> create_preset();
 
 	virtual void get_export_options(List<ExportOption> *r_options) = 0;
+	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { return true; }
+
 	virtual String get_name() const = 0;
 	virtual Ref<Texture> get_logo() const = 0;
 

+ 10 - 2
editor/project_export.cpp

@@ -229,12 +229,12 @@ void ProjectExportDialog::_edit_preset(int p_index) {
 		if (needs_templates)
 			export_templates_error->show();
 
-		get_ok()->set_disabled(true);
+		export_button->set_disabled(true);
 
 	} else {
 		export_error->show();
 		export_templates_error->hide();
-		get_ok()->set_disabled(false);
+		export_button->set_disabled(false);
 	}
 
 	updating = false;
@@ -313,6 +313,12 @@ void ProjectExportDialog::_patch_deleted() {
 	}
 }
 
+void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
+
+	_edit_preset(presets->get_current());
+	parameters->update_tree();
+}
+
 void ProjectExportDialog::_runnable_pressed() {
 
 	if (updating)
@@ -676,6 +682,7 @@ void ProjectExportDialog::_bind_methods() {
 
 	ClassDB::bind_method("_add_preset", &ProjectExportDialog::_add_preset);
 	ClassDB::bind_method("_edit_preset", &ProjectExportDialog::_edit_preset);
+	ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters);
 	ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed);
 	ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed);
 	ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset);
@@ -753,6 +760,7 @@ ProjectExportDialog::ProjectExportDialog() {
 	parameters->hide_top_label();
 	parameters->set_v_size_flags(SIZE_EXPAND_FILL);
 	parameters->set_hide_script(true);
+	parameters->connect("property_edited", this, "_update_parameters");
 
 	VBoxContainer *resources_vb = memnew(VBoxContainer);
 	sections->add_child(resources_vb);

+ 1 - 0
editor/project_export.h

@@ -97,6 +97,7 @@ private:
 	void _patch_deleted();
 
 	void _runnable_pressed();
+	void _update_parameters(const String &p_edited_property);
 	void _name_changed(const String &p_string);
 	void _add_preset(int p_platform);
 	void _edit_preset(int p_index);

+ 7 - 5
misc/dist/html_fs/godotfs.js

@@ -8,6 +8,8 @@ if (!Module.expectedDataFileDownloads) {
 Module.expectedDataFileDownloads++;
 (function() {
 
+    const PACK_FILE_NAME = '$GODOT_PACK_NAME';
+    const PACK_FILE_SIZE = $GODOT_PACK_SIZE;
     function fetchRemotePackage(packageName, callback, errback) {
       var xhr = new XMLHttpRequest();
       xhr.open('GET', packageName, true);
@@ -52,7 +54,7 @@ Module.expectedDataFileDownloads++;
     };
 
       var fetched = null, fetchedCallback = null;
-      fetchRemotePackage('data.pck', function(data) {
+      fetchRemotePackage(PACK_FILE_NAME, function(data) {
         if (fetchedCallback) {
           fetchedCallback(data);
           fetchedCallback = null;
@@ -101,7 +103,7 @@ function assert(check, msg) {
         this.requests[this.name] = null;
       },
     };
-      new DataRequest(0, $DPLEN, 0, 0).open('GET', '/data.pck');
+      new DataRequest(0, PACK_FILE_SIZE, 0, 0).open('GET', '/' + PACK_FILE_NAME);
 
     var PACKAGE_PATH;
     if (typeof window === 'object') {
@@ -110,8 +112,8 @@ function assert(check, msg) {
       // worker
       PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
     }
-    var PACKAGE_NAME = 'data.pck';
-    var REMOTE_PACKAGE_NAME = 'data.pck';
+    var PACKAGE_NAME = PACK_FILE_NAME;
+    var REMOTE_PACKAGE_NAME = PACK_FILE_NAME;
     var PACKAGE_UUID = 'b39761ce-0348-4959-9b16-302ed8e1592e';
 
     function processPackageData(arrayBuffer) {
@@ -122,7 +124,7 @@ function assert(check, msg) {
 
       // Reuse the bytearray from the XHR as the source for file reads.
       DataRequest.prototype.byteArray = byteArray;
-          DataRequest.prototype.requests["/data.pck"].onload();
+          DataRequest.prototype.requests['/' + PACK_FILE_NAME].onload();
           Module['removeRunDependency']('datafile_datapack');
 
     };

+ 1 - 1
platform/javascript/detect.py

@@ -73,7 +73,7 @@ def configure(env):
         env.Append(LINKFLAGS=['-O3'])
     elif (env["target"] == "release_debug"):
         env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED'])
-        env.Append(LINKFLAGS=['-O2'])
+        env.Append(LINKFLAGS=['-O2', '-s', 'ASSERTIONS=1'])
         # retain function names at the cost of file size, for backtraces and profiling
         env.Append(LINKFLAGS=['--profiling-funcs'])
     elif (env["target"] == "debug"):

+ 180 - 276
platform/javascript/export/export.cpp

@@ -26,399 +26,303 @@
 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
-#include "export.h"
-#include "editor/editor_export.h"
 #include "editor/editor_node.h"
-#include "editor/editor_settings.h"
-#include "global_config.h"
-#include "io/marshalls.h"
+#include "editor_export.h"
 #include "io/zip_io.h"
-#include "os/file_access.h"
-#include "os/os.h"
 #include "platform/javascript/logo.h"
-#include "string.h"
-#include "version.h"
 
-#if 0
-class EditorExportPlatformJavaScript : public EditorExportPlatform {
-
-	GDCLASS( EditorExportPlatformJavaScript,EditorExportPlatform );
-
-	String custom_release_package;
-	String custom_debug_package;
-
-	enum PackMode {
-		PACK_SINGLE_FILE,
-		PACK_MULTIPLE_FILES
-	};
-
-	void _fix_html(Vector<uint8_t>& p_html, const String& p_name, bool p_debug);
+#define EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE "webassembly_release.zip"
+#define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip"
+#define EXPORT_TEMPLATE_ASMJS_RELEASE "javascript_release.zip"
+#define EXPORT_TEMPLATE_ASMJS_DEBUG "javascript_debug.zip"
 
-	PackMode pack_mode;
-
-	bool show_run;
-
-	int max_memory;
-	int version_code;
+class EditorExportPlatformJavaScript : public EditorExportPlatform {
 
-	String html_title;
-	String html_head_include;
-	String html_font_family;
-	String html_style_include;
-	bool html_controls_enabled;
+	GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform)
 
 	Ref<ImageTexture> logo;
 
-protected:
-
-	bool _set(const StringName& p_name, const Variant& p_value);
-	bool _get(const StringName& p_name,Variant &r_ret) const;
-	void _get_property_list( List<PropertyInfo> *p_list) const;
+	void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug);
+	void _fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size);
 
 public:
+	enum Target {
+		TARGET_WEBASSEMBLY,
+		TARGET_ASMJS
+	};
 
-	virtual String get_name() const { return "HTML5"; }
-	virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; }
-	virtual Ref<Texture> get_logo() const { return logo; }
+	virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
 
+	virtual void get_export_options(List<ExportOption> *r_options);
+	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
 
-	virtual bool poll_devices() { return show_run?true:false;}
-	virtual int get_device_count() const { return show_run?1:0; };
-	virtual String get_device_name(int p_device) const  { return "Run in Browser"; }
-	virtual String get_device_info(int p_device) const { return "Run exported HTML in the system's default browser."; }
-	virtual Error run(int p_device,int p_flags=0);
+	virtual String get_name() const;
+	virtual Ref<Texture> get_logo() const;
 
-	virtual bool requires_password(bool p_debug) const { return false; }
-	virtual String get_binary_extension() const { return "html"; }
-	virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
+	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
+	virtual String get_binary_extension() const;
+	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
 
-	virtual bool can_export(String *r_error=NULL) const;
+	virtual int get_device_count() const { return 1; }
+	virtual String get_device_name(int p_device) const { return TTR("Run in Browser"); }
+	virtual String get_device_info(int p_device) const { return TTR("Run exported HTML in the system's default browser."); }
+	virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags);
 
 	EditorExportPlatformJavaScript();
-	~EditorExportPlatformJavaScript();
 };
 
-bool EditorExportPlatformJavaScript::_set(const StringName& p_name, const Variant& p_value) {
-
-	String n=p_name;
-
-	if (n=="custom_package/debug")
-		custom_debug_package=p_value;
-	else if (n=="custom_package/release")
-		custom_release_package=p_value;
-	else if (n=="browser/enable_run")
-		show_run=p_value;
-	else if (n=="options/memory_size")
-		max_memory=p_value;
-	else if (n=="html/title")
-		html_title=p_value;
-	else if (n=="html/head_include")
-		html_head_include=p_value;
-	else if (n=="html/font_family")
-		html_font_family=p_value;
-	else if (n=="html/style_include")
-		html_style_include=p_value;
-	else if (n=="html/controls_enabled")
-		html_controls_enabled=p_value;
-	else
-		return false;
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug) {
 
-	return true;
-}
+	String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
+	String str_export;
+	Vector<String> lines = str_template.split("\n");
 
-bool EditorExportPlatformJavaScript::_get(const StringName& p_name,Variant &r_ret) const{
-
-	String n=p_name;
-
-	if (n=="custom_package/debug")
-		r_ret=custom_debug_package;
-	else if (n=="custom_package/release")
-		r_ret=custom_release_package;
-	else if (n=="browser/enable_run")
-		r_ret=show_run;
-	else if (n=="options/memory_size")
-		r_ret=max_memory;
-	else if (n=="html/title")
-		r_ret=html_title;
-	else if (n=="html/head_include")
-		r_ret=html_head_include;
-	else if (n=="html/font_family")
-		r_ret=html_font_family;
-	else if (n=="html/style_include")
-		r_ret=html_style_include;
-	else if (n=="html/controls_enabled")
-		r_ret=html_controls_enabled;
+	int memory_mb;
+	if (p_preset->get("options/target").operator int() != TARGET_ASMJS)
+		// WebAssembly allows memory growth, so start with a reasonable default
+		memory_mb = 1 << 4;
 	else
-		return false;
-
-	return true;
-}
-void EditorExportPlatformJavaScript::_get_property_list( List<PropertyInfo> *p_list) const{
-
-	p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE,"zip"));
-	p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE,"zip"));
-	p_list->push_back( PropertyInfo( Variant::INT, "options/memory_size",PROPERTY_HINT_ENUM,"32mb,64mb,128mb,256mb,512mb,1024mb"));
-	p_list->push_back( PropertyInfo( Variant::BOOL, "browser/enable_run"));
-	p_list->push_back( PropertyInfo( Variant::STRING, "html/title"));
-	p_list->push_back( PropertyInfo( Variant::STRING, "html/head_include",PROPERTY_HINT_MULTILINE_TEXT));
-	p_list->push_back( PropertyInfo( Variant::STRING, "html/font_family"));
-	p_list->push_back( PropertyInfo( Variant::STRING, "html/style_include",PROPERTY_HINT_MULTILINE_TEXT));
-	p_list->push_back( PropertyInfo( Variant::BOOL, "html/controls_enabled"));
+		memory_mb = 1 << (p_preset->get("options/memory_size").operator int() + 5);
 
+	for (int i = 0; i < lines.size(); i++) {
 
-	//p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)"));
+		String current_line = lines[i];
+		current_line = current_line.replace("$GODOT_TMEM", itos(memory_mb * 1024 * 1024));
+		current_line = current_line.replace("$GODOT_BASE", p_name);
+		current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
+		current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
+		str_export += current_line + "\n";
+	}
 
+	CharString cs = str_export.utf8();
+	p_html.resize(cs.length());
+	for (int i = 0; i < cs.length(); i++) {
+		p_html[i] = cs[i];
+	}
 }
 
+void EditorExportPlatformJavaScript::_fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size) {
 
-void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const String& p_name, bool p_debug) {
+	String str_template = String::utf8(reinterpret_cast<const char *>(p_js.ptr()), p_js.size());
+	String str_export;
+	Vector<String> lines = str_template.split("\n");
+	for (int i = 0; i < lines.size(); i++) {
+		if (lines[i].find("$GODOT_PACK_NAME") != -1) {
+			str_export += lines[i].replace("$GODOT_PACK_NAME", p_pack_name);
+		} else if (lines[i].find("$GODOT_PACK_SIZE") != -1) {
+			str_export += lines[i].replace("$GODOT_PACK_SIZE", itos(p_pack_size));
+		} else {
+			str_export += lines[i] + "\n";
+		}
+	}
 
+	CharString cs = str_export.utf8();
+	p_js.resize(cs.length());
+	for (int i = 0; i < cs.length(); i++) {
+		p_js[i] = cs[i];
+	}
+}
 
-	String str;
-	String strnew;
-	str.parse_utf8((const char*)p_html.ptr(),p_html.size());
-	Vector<String> lines=str.split("\n");
-	for(int i=0;i<lines.size();i++) {
+void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
 
-		String current_line = lines[i];
-		current_line = current_line.replace("$GODOT_TMEM",itos((1<<(max_memory+5))*1024*1024));
-		current_line = current_line.replace("$GODOT_BASE",p_name);
-		current_line = current_line.replace("$GODOT_CANVAS_WIDTH",GlobalConfig::get_singleton()->get("display/window/width"));
-		current_line = current_line.replace("$GODOT_CANVAS_HEIGHT",GlobalConfig::get_singleton()->get("display/window/height"));
-		current_line = current_line.replace("$GODOT_HEAD_TITLE",!html_title.empty()?html_title:(String) GlobalConfig::get_singleton()->get("application/name"));
-		current_line = current_line.replace("$GODOT_HEAD_INCLUDE",html_head_include);
-		current_line = current_line.replace("$GODOT_STYLE_FONT_FAMILY",html_font_family);
-		current_line = current_line.replace("$GODOT_STYLE_INCLUDE",html_style_include);
-		current_line = current_line.replace("$GODOT_CONTROLS_ENABLED",html_controls_enabled?"true":"false");
-		current_line = current_line.replace("$GODOT_DEBUG_ENABLED",p_debug?"true":"false");
-		strnew += current_line+"\n";
+	if (p_preset->get("texture_format/s3tc")) {
+		r_features->push_back("s3tc");
 	}
-
-	CharString cs = strnew.utf8();
-	p_html.resize(cs.length());
-	for(int i=9;i<cs.length();i++) {
-		p_html[i]=cs[i];
+	if (p_preset->get("texture_format/etc")) {
+		r_features->push_back("etc");
+	}
+	if (p_preset->get("texture_format/etc2")) {
+		r_features->push_back("etc2");
 	}
 }
 
-static void _fix_files(Vector<uint8_t>& html,uint64_t p_data_size) {
+void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) {
 
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/target", PROPERTY_HINT_ENUM, "WebAssembly,asm.js"), TARGET_WEBASSEMBLY));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/memory_size", PROPERTY_HINT_ENUM, "32 MB,64 MB,128 MB,256 MB,512 MB,1 GB"), 3));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
+}
 
-	String str;
-	String strnew;
-	str.parse_utf8((const char*)html.ptr(),html.size());
-	Vector<String> lines=str.split("\n");
-	for(int i=0;i<lines.size();i++) {
-		if (lines[i].find("$DPLEN")!=-1) {
-			strnew+=lines[i].replace("$DPLEN",itos(p_data_size));
-		} else {
-			strnew+=lines[i]+"\n";
-		}
-	}
+bool EditorExportPlatformJavaScript::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
 
-	CharString cs = strnew.utf8();
-	html.resize(cs.length());
-	for(int i=9;i<cs.length();i++) {
-		html[i]=cs[i];
+	if (p_option == "options/memory_size") {
+		return p_options["options/target"].operator int() == TARGET_ASMJS;
 	}
+	return true;
+}
 
+String EditorExportPlatformJavaScript::get_name() const {
+
+	return "HTML5";
 }
 
-struct JSExportData {
+Ref<Texture> EditorExportPlatformJavaScript::get_logo() const {
 
-	EditorProgress *ep;
-	FileAccess *f;
+	return logo;
+}
 
-};
+bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
 
+	r_missing_templates = false;
 
+	if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) {
+		if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String())
+			r_missing_templates = true;
+		else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String())
+			r_missing_templates = true;
+	} else {
+		if (find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE) == String())
+			r_missing_templates = true;
+		else if (find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG) == String())
+			r_missing_templates = true;
+	}
 
-Error EditorExportPlatformJavaScript::export_project(const String& p_path, bool p_debug, int p_flags) {
+	return !r_missing_templates;
+}
 
+String EditorExportPlatformJavaScript::get_binary_extension() const {
 
-	String src_template;
+	return "html";
+}
 
-	EditorProgress ep("export","Exporting for javascript",104);
+Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
 
-	if (p_debug)
-		src_template=custom_debug_package;
-	else
-		src_template=custom_release_package;
+	String custom_debug = p_preset->get("custom_template/debug");
+	String custom_release = p_preset->get("custom_template/release");
+
+	String template_path = p_debug ? custom_debug : custom_release;
 
-	if (src_template=="") {
-		String err;
-		if (p_debug) {
-			src_template=find_export_template("javascript_debug.zip", &err);
+	template_path = template_path.strip_edges();
+
+	if (template_path == String()) {
+
+		if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) {
+			if (p_debug)
+				template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG);
+			else
+				template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE);
 		} else {
-			src_template=find_export_template("javascript_release.zip", &err);
-		}
-		if (src_template=="") {
-			EditorNode::add_io_error(err);
-			return ERR_FILE_NOT_FOUND;
+			if (p_debug)
+				template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG);
+			else
+				template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE);
 		}
 	}
 
-	FileAccess *src_f=NULL;
-	zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
-
-	ep.step("Exporting to HTML5",0);
+	if (template_path != String() && !FileAccess::exists(template_path)) {
+		EditorNode::get_singleton()->show_warning(TTR("Template file not found:\n") + template_path);
+		return ERR_FILE_NOT_FOUND;
+	}
 
-	ep.step("Finding Files..",1);
+	String pck_path = p_path.get_basename() + ".pck";
+	Error error = save_pack(p_preset, pck_path);
+	if (error != OK) {
+		EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + pck_path);
+		return error;
+	}
 
-	FileAccess *f=FileAccess::open(p_path.get_base_dir()+"/data.pck",FileAccess::WRITE);
+	FileAccess *f = FileAccess::open(pck_path, FileAccess::READ);
 	if (!f) {
-		EditorNode::add_io_error("Could not create file for writing:\n"+p_path.get_basename()+"_files.js");
-		return ERR_FILE_CANT_WRITE;
+		EditorNode::get_singleton()->show_warning(TTR("Could not read file:\n") + pck_path);
+		return ERR_FILE_CANT_READ;
 	}
-	Error err = save_pack(f);
-	size_t len = f->get_len();
+	size_t pack_size = f->get_len();
 	memdelete(f);
-	if (err)
-		return err;
 
+	FileAccess *src_f = NULL;
+	zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+	unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io);
 
-	unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io);
 	if (!pkg) {
 
-		EditorNode::add_io_error("Could not find template HTML5 to export:\n"+src_template);
+		EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:\n") + template_path);
 		return ERR_FILE_NOT_FOUND;
 	}
 
-	ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN);
 	int ret = unzGoToFirstFile(pkg);
-
-
-	while(ret==UNZ_OK) {
+	while (ret == UNZ_OK) {
 
 		//get filename
 		unz_file_info info;
 		char fname[16384];
-		ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0);
+		ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
 
-		String file=fname;
+		String file = fname;
 
 		Vector<uint8_t> data;
 		data.resize(info.uncompressed_size);
 
 		//read
 		unzOpenCurrentFile(pkg);
-		unzReadCurrentFile(pkg,data.ptr(),data.size());
+		unzReadCurrentFile(pkg, data.ptr(), data.size());
 		unzCloseCurrentFile(pkg);
 
 		//write
 
-		if (file=="godot.html") {
+		if (file == "godot.html") {
 
-			_fix_html(data,p_path.get_file().get_basename(), p_debug);
-			file=p_path.get_file();
-		}
-		if (file=="godotfs.js") {
-
-			_fix_files(data,len);
-			file=p_path.get_file().get_basename()+"fs.js";
-		}
-		if (file=="godot.js") {
+			_fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug);
+			file = p_path.get_file();
+		} else if (file == "godotfs.js") {
 
-			file=p_path.get_file().get_basename()+".js";
-		}
+			_fix_fsloader_js(data, pck_path.get_file(), pack_size);
+			file = p_path.get_file().get_basename() + "fs.js";
+		} else if (file == "godot.js") {
 
-		if (file=="godot.asm.js") {
+			file = p_path.get_file().get_basename() + ".js";
+		} else if (file == "godot.wasm") {
 
-			file=p_path.get_file().get_basename()+".asm.js";
-		}
+			file = p_path.get_file().get_basename() + ".wasm";
+		} else if (file == "godot.asm.js") {
 
-		if (file=="godot.mem") {
+			file = p_path.get_file().get_basename() + ".asm.js";
+		} else if (file == "godot.mem") {
 
-			file=p_path.get_file().get_basename()+".mem";
-		}
-
-		if (file=="godot.wasm") {
-
-			file=p_path.get_file().get_basename()+".wasm";
+			file = p_path.get_file().get_basename() + ".mem";
 		}
 
 		String dst = p_path.get_base_dir().plus_file(file);
-		FileAccess *f=FileAccess::open(dst,FileAccess::WRITE);
+		FileAccess *f = FileAccess::open(dst, FileAccess::WRITE);
 		if (!f) {
-			EditorNode::add_io_error("Could not create file for writing:\n"+dst);
+			EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + dst);
 			unzClose(pkg);
 			return ERR_FILE_CANT_WRITE;
 		}
-		f->store_buffer(data.ptr(),data.size());
+		f->store_buffer(data.ptr(), data.size());
 		memdelete(f);
 
-
 		ret = unzGoToNextFile(pkg);
 	}
 
-
-
 	return OK;
-
 }
 
+Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
 
-Error EditorExportPlatformJavaScript::run(int p_device, int p_flags) {
-
-	String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmp_export.html";
-	Error err = export_project(path,true,p_flags);
-	if (err)
+	String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmp_export.html";
+	Error err = export_project(p_preset, true, path, p_debug_flags);
+	if (err) {
 		return err;
-
+	}
 	OS::get_singleton()->shell_open(path);
-
 	return OK;
 }
 
-
 EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
 
-	show_run=false;
-	Image img( _javascript_logo );
-	logo = Ref<ImageTexture>( memnew( ImageTexture ));
+	Image img(_javascript_logo);
+	logo.instance();
 	logo->create_from_image(img);
-	max_memory=3;
-	html_title="";
-	html_font_family="'Droid Sans',arial,sans-serif";
-	html_controls_enabled=true;
-	pack_mode=PACK_SINGLE_FILE;
-}
-
-bool EditorExportPlatformJavaScript::can_export(String *r_error) const {
-
-
-	bool valid=true;
-	String err;
-
-	if (!exists_export_template("javascript_debug.zip") || !exists_export_template("javascript_release.zip")) {
-		valid=false;
-		err+="No export templates found.\nDownload and install export templates.\n";
-	}
-
-	if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) {
-		valid=false;
-		err+="Custom debug package not found.\n";
-	}
-
-	if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) {
-		valid=false;
-		err+="Custom release package not found.\n";
-	}
-
-	if (r_error)
-		*r_error=err;
-
-	return valid;
-}
-
-
-EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
-
 }
 
-#endif
 void register_javascript_exporter() {
 
-	//Ref<EditorExportPlatformJavaScript> exporter = Ref<EditorExportPlatformJavaScript>( memnew(EditorExportPlatformJavaScript) );
-	//EditorImportExport::get_singleton()->add_export_platform(exporter);
+	Ref<EditorExportPlatformJavaScript> platform;
+	platform.instance();
+	EditorExport::get_singleton()->add_export_platform(platform);
 }

+ 58 - 141
platform/javascript/godot_shell.html

@@ -2,8 +2,7 @@
 <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
 <head>
 	<meta charset="utf-8" />
-	<title>$GODOT_HEAD_TITLE</title>
-$GODOT_HEAD_INCLUDE
+	<title></title>
 	<style type="text/css">
 		body {
 			margin: 0;
@@ -11,7 +10,7 @@ $GODOT_HEAD_INCLUDE
 			padding: 0;
 			text-align: center;
 			background-color: #222226;
-			font-family: $GODOT_STYLE_FONT_FAMILY;
+			font-family: 'Droid Sans', Arial, sans-serif;
 		}
 
 
@@ -27,7 +26,7 @@ $GODOT_HEAD_INCLUDE
 		}
 
 		button.godot {
-			font-family: $GODOT_STYLE_FONT_FAMILY; /* override user agent style */
+			font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */
 			padding: 1px 5px;
 			background-color: #37353f;
 			background-image: linear-gradient(to bottom, #413e49, #3a3842);
@@ -109,53 +108,12 @@ $GODOT_HEAD_INCLUDE
 		}
 
 
-		/* On-hover controls
-		 * ================= */
-
-		#controls {
-			visibility: hidden;
-			opacity: 0.0;
-			transition: opacity 500ms ease-in-out 200ms;
-			position: absolute;
-			right: 16px;
-			top: 16px;
-			padding: 3px 5px;
-			font-size: small;
-			-moz-user-select: none;
-			-webkit-user-select: none;
-			-ms-user-select: none;
-		}
-
-		:hover > #controls {
-			opacity: 1.0;
-			transition: opacity 60ms ease-in-out;
-		}
-
-		#controls > button,
-		#controls > label {
-			vertical-align: middle;
-			margin-left: 2px;
-			margin-right: 2px;
-		}
-
-		#controls > label > input {
-			vertical-align: middle;
-		}
-
-		#controls > label > input[type="checkbox"] {
-			/* override user agent style */
-			margin-left: 0;
-		}
-
-		#output-toggle { display: none; }
-
-
 		/* Debug output
 		 * ============ */
 
 		#output-panel {
 			display: none;
-			max-width: $GODOT_CANVAS_WIDTHpx;
+			max-width: 700px;
 			font-size: small;
 			margin: 6px auto 0;
 			padding: 0 4px 4px;
@@ -184,32 +142,18 @@ $GODOT_HEAD_INCLUDE
 			font-size: small;
 			font-family: "Lucida Console", Monaco, monospace;
 		}
-
-
-/* Export style include
- * ==================== */
-
-$GODOT_STYLE_INCLUDE
-
 	</style>
+$GODOT_HEAD_INCLUDE
 </head>
 <body>
 	<div id="container">
-		<canvas id="canvas" width="$GODOT_CANVAS_WIDTH" height="$GODOT_CANVAS_HEIGHT" onclick="canvas.ownerDocument.defaultView.focus();" oncontextmenu="event.preventDefault();">
+		<canvas id="canvas" width="640" height="480" onclick="canvas.ownerDocument.defaultView.focus();" oncontextmenu="event.preventDefault();">
 			HTML5 canvas appears to be unsupported in the current browser.<br />
 			Please try updating or use a different browser.
 		</canvas>
 		<div id="status-container">
 			<span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span>
 		</div>
-		<div id="controls" class="godot">
-			<label id="output-toggle"><input type="checkbox" checked="checked" autocomplete="off" onchange="Presentation.setOutputVisible(this.checked);" />Display Output</label>
-			<!-- hidden until implemented
-			<label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off" />lock cursor</label>
-			<label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off"  onchange="Presentation.setCanvasMaximized(this.checked);" />maximize</label>
-			-->
-			<button id="fullscreen" class="godot postRun-enable" type="button" disabled="disabled" autocomplete="off" onclick="Presentation.requestFullscreen();">Fullscreen</button>
-		</div>
 	</div>
 	<div id="output-panel" class="godot">
 		<div id="output-header">
@@ -226,33 +170,9 @@ $GODOT_STYLE_INCLUDE
 			var canvasElement = document.getElementById("canvas");
 
 			var presentation = {
-				postRun: [
-					function() {
-						var elements = document.getElementsByClassName("postRun-enable");
-						Array.prototype.slice.call(elements).forEach(function(element) {
-							element.disabled = false;
-						});
-					}
-				],
-				requestFullscreen: function requestFullscreen() {
-					if (typeof Module !== "undefined" && Module.requestFullscreen) {
-						Module.requestFullscreen(false, false);
-					}
-				},
-				/*
-				requestPointerlock: function requestPointerlock() {
-					if (typeof Module !== "undefined" && Module.requestPointerlock) {
-						Module.requestPointerlock(false, false);
-					}
-				},
-				setCanvasMaximized: function setCanvasMaximized(enabled) {
-					if (typeof Module !== "undefined" && Module.setCanvasMaximized) {
-						Module.setCanvasMaximized(enabled);
-					}
-				},
-				*/
+				postRun: [],
 				setStatusVisible: function setStatusVisible(visible) {
-					statusElement.style.visibility = (visible?"visible":"hidden");
+					statusElement.style.visibility = (visible ? "visible" : "hidden");
 				},
 				setStatus: function setStatus(text) {
 					if (text.length === 0) {
@@ -288,18 +208,13 @@ $GODOT_STYLE_INCLUDE
 
 			window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") };
 
-			if ($GODOT_CONTROLS_ENABLED) { // controls enabled
-				document.getElementById("controls").style.visibility="visible";
-			}
-
 			if ($GODOT_DEBUG_ENABLED) { // debugging enabled
 				var outputRoot = document.getElementById("output-panel");
 				var outputElement = document.getElementById("output-scroll");
-				var outputToggle = document.getElementById("output-toggle");
 				const maxOutputMessages = 400;
 
 				presentation.setOutputVisible = function setOutputVisible(visible) {
-					outputRoot.style.display = (visible?"block":"none");
+					outputRoot.style.display = (visible ? "block" : "none");
 				};
 				presentation.clearOutput = function clearOutput() {
 					while (outputElement.firstChild) {
@@ -308,7 +223,6 @@ $GODOT_STYLE_INCLUDE
 				};
 
 				presentation.setOutputVisible(true);
-				outputToggle.style.display = "inline";
 
 				presentation.print = function print(text) {
 					if (arguments.length > 1) {
@@ -347,56 +261,59 @@ $GODOT_STYLE_INCLUDE
 		})();
 
 		// Emscripten interface
-		var Module = {
-			TOTAL_MEMORY: $GODOT_TMEM,
-			postRun: (function() {
-				if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) {
-					return Presentation.postRun;
-				}
-			})(),
-			print: function print(text) {
-				if (arguments.length > 1) {
-					text = Array.prototype.slice.call(arguments).join(" ");
-				}
-				console.log(text);
-				if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
-					Presentation.print(text);
-				}
-			},
-			printErr: function printErr(text) {
-				if (arguments.length > 1) {
-					text = Array.prototype.slice.call(arguments).join(" ");
-				}
-				console.error(text);
-				if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
-					Presentation.print("**ERROR**:", text)
-				}
-			},
-			canvas: (function() {
-				var canvas = document.getElementById("canvas");
-				// As a default initial behavior, pop up an alert when WebGL context is lost. To make your
-				// application robust, you may want to override this behavior before shipping!
-				// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
-				canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false);
-				return canvas;
-
-			})(),
-			setStatus: function setStatus(text) {
-				var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
-				var now = Date.now();
-				if (m) {
-					if (now - Date.now() < 30) // if this is a progress update, skip it if too soon
-						return;
-					text = m[1];
-				}
-				if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") {
-					Presentation.setStatus(text);
+		var Module = (function() {
+			const BASE_NAME = '$GODOT_BASE';
+			var module = {
+				thisProgram: BASE_NAME,
+				wasmBinaryFile: BASE_NAME + '.wasm',
+				TOTAL_MEMORY: $GODOT_TMEM,
+				print: function print(text) {
+					if (arguments.length > 1) {
+						text = Array.prototype.slice.call(arguments).join(" ");
+					}
+					console.log(text);
+					if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
+						Presentation.print(text);
+					}
+				},
+				printErr: function printErr(text) {
+					if (arguments.length > 1) {
+						text = Array.prototype.slice.call(arguments).join(" ");
+					}
+					console.error(text);
+					if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") {
+						Presentation.print("**ERROR**:", text)
+					}
+				},
+				canvas: document.getElementById("canvas"),
+				setStatus: function setStatus(text) {
+					var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+					var now = Date.now();
+					if (m) {
+						if (now - Date.now() < 30) // if this is a progress update, skip it if too soon
+							return;
+						text = m[1];
+					}
+					if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") {
+						Presentation.setStatus(text);
+					}
 				}
+			};
+
+			// As a default initial behavior, pop up an alert when WebGL context is lost. To make your
+			// application robust, you may want to override this behavior before shipping!
+			// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+			module.canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false);
+
+			if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) {
+				module.postRun = Presentation.postRun;
 			}
-		};
+
+			return module;
+		})();
 
 		if (!Presentation.isWebGL2Available()) {
-			Presentation.setStatus("WebGL2 appears to be unsupported in the current browser.\nPlease try updating or use a different browser.");
+			Presentation.setStatus("WebGL 2 appears to be unsupported.\nPlease update browser and drivers.");
 			Presentation.preventLoading = true;
 		} else {
 			Presentation.setStatus("Downloading...");

+ 4 - 9
platform/javascript/javascript_main.cpp

@@ -145,15 +145,10 @@ int main(int argc, char *argv[]) {
 	/* Initialize the window */
 	printf("let it go dude!\n");
 	glutInit(&argc, argv);
-	os = new OS_JavaScript(_gfx_init, NULL, NULL);
-#if 0
-	char *args[]={"-test","gui","-v",NULL};
-	Error err  = Main::setup("apk",3,args);
-#else
-	char *args[] = { "-main_pack", "data.pck", NULL }; //pass location of main pack manually, because it wont get an executable name
-	Error err = Main::setup("", 2, args);
-
-#endif
+	os = new OS_JavaScript(argv[0], _gfx_init, NULL, NULL);
+
+	Error err = Main::setup(argv[0], argc - 1, &argv[1]);
+
 	ResourceLoader::set_abort_on_missing_resources(false); //ease up compatibility
 
 	glutMouseFunc(_glut_mouse_button);

+ 3 - 2
platform/javascript/os_javascript.cpp

@@ -761,7 +761,7 @@ String OS_JavaScript::get_data_dir() const {
 
 String OS_JavaScript::get_executable_path() const {
 
-	return String();
+	return OS::get_executable_path();
 }
 
 void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) {
@@ -839,7 +839,8 @@ int OS_JavaScript::get_power_percent_left() {
 	return power_manager->get_power_percent_left();
 }
 
-OS_JavaScript::OS_JavaScript(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func) {
+OS_JavaScript::OS_JavaScript(const char *p_execpath, GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func) {
+	set_cmdline(p_execpath, get_cmdline_args());
 	gfx_init_func = p_gfx_init_func;
 	gfx_init_ud = p_gfx_init_ud;
 	last_button_mask = 0;

+ 1 - 1
platform/javascript/os_javascript.h

@@ -177,7 +177,7 @@ public:
 	virtual int get_power_seconds_left();
 	virtual int get_power_percent_left();
 
-	OS_JavaScript(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func);
+	OS_JavaScript(const char *p_execpath, GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func);
 	~OS_JavaScript();
 };