Compare commits
1 Commits
master
...
batch-uplo
Author | SHA1 | Date | |
---|---|---|---|
|
5fea32b0a2 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['paypal.me/laame22'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,7 +3,7 @@
|
|||||||
.import/
|
.import/
|
||||||
export.cfg
|
export.cfg
|
||||||
export_presets.cfg
|
export_presets.cfg
|
||||||
.godot/
|
|
||||||
# Mono-specific ignores
|
# Mono-specific ignores
|
||||||
.mono/
|
.mono/
|
||||||
data_*/
|
data_*/
|
||||||
|
12
README.md
12
README.md
@ -1,11 +1 @@
|
|||||||
# HTML5 File Exchange for Godot 3.4
|
# HTML5 File Exchange for Godot 3.2
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Check sample.tscn as a upload & download example.
|
|
||||||
|
|
||||||
### Without sample
|
|
||||||
|
|
||||||
* Copy addons to your project.
|
|
||||||
* Enable the plugin. (A `HTML5File` singleton should be added into autoload list).
|
|
||||||
* If the HTML5 debugging option is not enabled, add a HTML5 export item.
|
|
||||||
|
20
Sample.gd
20
Sample.gd
@ -1,20 +0,0 @@
|
|||||||
extends TextureRect
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
pass # Replace with function body.
|
|
||||||
|
|
||||||
func _on_UploadButton_pressed():
|
|
||||||
HTML5File.load_image()
|
|
||||||
|
|
||||||
var image = await HTML5File.load_completed
|
|
||||||
|
|
||||||
var tex = ImageTexture.new()
|
|
||||||
tex.create_from_image(image) #,0
|
|
||||||
|
|
||||||
texture = tex;
|
|
||||||
|
|
||||||
func _on_DownloadButton_pressed():
|
|
||||||
var image : Image
|
|
||||||
image = get_viewport().get_texture().get_image()
|
|
||||||
HTML5File.save_image(image, "image.png")
|
|
@ -1,21 +1,24 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
signal read_completed
|
signal InFocus
|
||||||
signal load_completed(image)
|
|
||||||
|
|
||||||
var js_callback = JavaScriptBridge.create_callback(load_handler);
|
|
||||||
var js_interface;
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
if OS.get_name() == "Web":
|
if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'):
|
||||||
_define_js()
|
_define_js()
|
||||||
js_interface = JavaScriptBridge.get_interface("_HTML5FileExchange");
|
|
||||||
|
|
||||||
|
func _notification(notification:int) -> void:
|
||||||
|
if notification == MainLoop.NOTIFICATION_WM_FOCUS_IN:
|
||||||
|
emit_signal("InFocus")
|
||||||
|
|
||||||
func _define_js()->void:
|
func _define_js()->void:
|
||||||
#Define JS script
|
#Define JS script
|
||||||
JavaScriptBridge.eval("""
|
JavaScript.eval("""
|
||||||
var _HTML5FileExchange = {};
|
var fileData;
|
||||||
_HTML5FileExchange.upload = function(gd_callback) {
|
var fileType;
|
||||||
|
var fileName;
|
||||||
|
var canceled;
|
||||||
|
function upload() {
|
||||||
canceled = true;
|
canceled = true;
|
||||||
var input = document.createElement('INPUT');
|
var input = document.createElement('INPUT');
|
||||||
input.setAttribute("type", "file");
|
input.setAttribute("type", "file");
|
||||||
@ -26,32 +29,131 @@ func _define_js()->void:
|
|||||||
canceled = false;}
|
canceled = false;}
|
||||||
var file = event.target.files[0];
|
var file = event.target.files[0];
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
this.fileType = file.type;
|
fileType = file.type;
|
||||||
// var fileName = file.name;
|
fileName = file.name;
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
reader.onloadend = (evt) => { // Since here's it's arrow function, "this" still refers to _HTML5FileExchange
|
reader.onloadend = function (evt) {
|
||||||
if (evt.target.readyState == FileReader.DONE) {
|
if (evt.target.readyState == FileReader.DONE) {
|
||||||
this.result = evt.target.result;
|
fileData = evt.target.result;
|
||||||
gd_callback(); // It's hard to retrieve value from callback argument, so it's just for notification
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
var fileDataArray = [];
|
||||||
|
var fileTypeArray = [];
|
||||||
|
var fileNameArray = [];
|
||||||
|
var numberOfFiles = 0;
|
||||||
|
function multiupload() {
|
||||||
|
canceled = true;
|
||||||
|
var input = document.createElement('INPUT');
|
||||||
|
input.setAttribute("type", "file");
|
||||||
|
input.setAttribute("accept", "image/png, image/jpeg, image/webp");
|
||||||
|
input.setAttribute("multiple","true")
|
||||||
|
input.click();
|
||||||
|
input.addEventListener('change', event => {
|
||||||
|
if (event.target.files.length > 0){
|
||||||
|
canceled = false;}
|
||||||
|
numberOfFiles = event.target.files.length
|
||||||
|
for(var i = 0; i < event.target.files.length; i++){
|
||||||
|
var file = event.target.files[i];
|
||||||
|
var reader = new FileReader();
|
||||||
|
fileTypeArray.push(file.type);
|
||||||
|
fileNameArray.push(file.name);
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
reader.onloadend = function (evt) {
|
||||||
|
if (evt.target.readyState == FileReader.DONE) {
|
||||||
|
fileDataArray.push(evt.target.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function download(fileName, byte) {
|
||||||
|
var buffer = Uint8Array.from(byte);
|
||||||
|
var blob = new Blob([buffer], { type: 'image/png'});
|
||||||
|
var link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
link.download = fileName;
|
||||||
|
link.click();
|
||||||
|
};
|
||||||
""", true)
|
""", true)
|
||||||
|
|
||||||
func load_handler(_args):
|
func load_image_array()->Array:
|
||||||
emit_signal("read_completed")
|
if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'):
|
||||||
|
|
||||||
func load_image():
|
|
||||||
if OS.get_name() != "Web" or !OS.has_feature('JavaScript'):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
js_interface.upload(js_callback);
|
JavaScript.eval("multiupload();", true)
|
||||||
|
|
||||||
await self.read_completed
|
yield(self, "InFocus")
|
||||||
|
|
||||||
var imageType = js_interface.fileType;
|
yield(get_tree().create_timer(0.1), "timeout")
|
||||||
var imageData = JavaScriptBridge.eval("_HTML5FileExchange.result", true) # interface doesn't work as expected for some reason
|
|
||||||
|
if JavaScript.eval("canceled;", true):
|
||||||
|
return
|
||||||
|
|
||||||
|
var imageDataArray = []
|
||||||
|
var imageTypeArray = []
|
||||||
|
var imageNameArray = []
|
||||||
|
for i in range(JavaScript.eval("numberOfFiles;", true)):
|
||||||
|
while true:
|
||||||
|
imageDataArray.push_back(JavaScript.eval("fileDataArray["+String(i)+"];", true))
|
||||||
|
if imageDataArray.size() == i+1:
|
||||||
|
imageTypeArray.push_back(JavaScript.eval("fileTypeArray["+String(i)+"];", true))
|
||||||
|
imageNameArray.push_back(JavaScript.eval("fileNameArray["+String(i)+"];", true))
|
||||||
|
break
|
||||||
|
|
||||||
|
yield(get_tree().create_timer(1.0), "timeout")
|
||||||
|
|
||||||
|
var imageArray = []
|
||||||
|
|
||||||
|
for i in range(imageDataArray.size()):
|
||||||
|
var image = Image.new()
|
||||||
|
var image_error
|
||||||
|
match imageTypeArray[i]:
|
||||||
|
"image/png":
|
||||||
|
image_error = image.load_png_from_buffer(imageDataArray[i])
|
||||||
|
"image/jpeg":
|
||||||
|
image_error = image.load_jpg_from_buffer(imageDataArray[i])
|
||||||
|
"image/webp":
|
||||||
|
image_error = image.load_webp_from_buffer(imageDataArray[i])
|
||||||
|
var invalidType:
|
||||||
|
return
|
||||||
|
if image_error:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
imageArray.push_back(image)
|
||||||
|
return imageArray
|
||||||
|
|
||||||
|
|
||||||
|
func load_image()->Image:
|
||||||
|
if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'):
|
||||||
|
return
|
||||||
|
|
||||||
|
#Execute js function
|
||||||
|
JavaScript.eval("upload();", true) #opens promt for choosing file
|
||||||
|
|
||||||
|
#label.text = "Wait for focus"
|
||||||
|
yield(self, "InFocus") #wait until js promt is closed
|
||||||
|
|
||||||
|
#label.text = "Timer on for loading"
|
||||||
|
yield(get_tree().create_timer(0.1), "timeout") #give some time for async js data load
|
||||||
|
|
||||||
|
if JavaScript.eval("canceled;", true): # if File Dialog closed w/o file
|
||||||
|
#label.text = "Canceled prompt"
|
||||||
|
return
|
||||||
|
|
||||||
|
# use data from png data
|
||||||
|
#label.text = "Load image"
|
||||||
|
var imageData
|
||||||
|
while true:
|
||||||
|
imageData = JavaScript.eval("fileData;", true)
|
||||||
|
if imageData != null:
|
||||||
|
break
|
||||||
|
#label.text = "No image yet"
|
||||||
|
yield(get_tree().create_timer(1.0), "timeout") #need more time to load data
|
||||||
|
|
||||||
|
var imageType = JavaScript.eval("fileType;", true)
|
||||||
|
var imageName = JavaScript.eval("fileName;", true)
|
||||||
|
|
||||||
var image = Image.new()
|
var image = Image.new()
|
||||||
var image_error
|
var image_error
|
||||||
@ -63,18 +165,39 @@ func load_image():
|
|||||||
"image/webp":
|
"image/webp":
|
||||||
image_error = image.load_webp_from_buffer(imageData)
|
image_error = image.load_webp_from_buffer(imageData)
|
||||||
var invalidType:
|
var invalidType:
|
||||||
print("Unsupported file format - %s." % invalidType)
|
#label.text = "Unsupported file format - %s." % invalidType
|
||||||
return
|
return
|
||||||
|
|
||||||
if image_error:
|
if image_error:
|
||||||
print("An error occurred while trying to display the image.")
|
#label.text = "An error occurred while trying to display the image."
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return image
|
||||||
|
# Display texture
|
||||||
|
var tex = ImageTexture.new()
|
||||||
|
tex.create_from_image(image, 0) # Flag = 0 or else export is fucked!
|
||||||
|
Sprite.texture = tex
|
||||||
|
#loadedImage = image # Keep Image for later, just in case...
|
||||||
|
#loadedImageName = imageName
|
||||||
|
#label.text = "Image %s loaded as %s." % [imageName, imageType]
|
||||||
|
return
|
||||||
|
#label.text = "Something went wrong"
|
||||||
|
|
||||||
emit_signal("load_completed", image)
|
|
||||||
|
|
||||||
func save_image(image:Image, fileName:String = "export.png")->void:
|
func save_image(image:Image, fileName:String = "export")->void:
|
||||||
if OS.get_name() != "Web" or !OS.has_feature('JavaScript'):
|
if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'):
|
||||||
return
|
return
|
||||||
|
|
||||||
image.clear_mipmaps()
|
image.clear_mipmaps()
|
||||||
var buffer = image.save_png_to_buffer()
|
if image.save_png("user://export_temp.png"):
|
||||||
JavaScriptBridge.download_buffer(buffer, fileName)
|
#label.text = "Error saving temp file"
|
||||||
|
return
|
||||||
|
var file:File = File.new()
|
||||||
|
if file.open("user://export_temp.png", File.READ):
|
||||||
|
#label.text = "Error opening file"
|
||||||
|
return
|
||||||
|
var pngData = Array(file.get_buffer(file.get_len())) #read data as PoolByteArray and convert it to Array for JS
|
||||||
|
file.close()
|
||||||
|
var dir = Directory.new()
|
||||||
|
dir.remove("user://export_temp.png")
|
||||||
|
JavaScript.eval("download('%s', %s);" % [fileName, str(pngData)], true)
|
||||||
|
#label.text = "Saving DONE"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@tool
|
tool
|
||||||
extends EditorPlugin
|
extends EditorPlugin
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
[gd_resource type="Environment" load_steps=2 format=3 uid="uid://baopyhxl0883"]
|
|
||||||
|
|
||||||
[sub_resource type="Sky" id="1"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
background_mode = 2
|
|
||||||
sky = SubResource("1")
|
|
@ -1,34 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://dchvsb8f80an3"
|
|
||||||
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://icon.png"
|
|
||||||
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
@ -1,32 +0,0 @@
|
|||||||
; Engine configuration file.
|
|
||||||
; It's best edited using the editor UI and not directly,
|
|
||||||
; since the parameters that go here are not all obvious.
|
|
||||||
;
|
|
||||||
; Format:
|
|
||||||
; [section] ; section goes between []
|
|
||||||
; param=value ; assign values to parameters
|
|
||||||
|
|
||||||
config_version=5
|
|
||||||
|
|
||||||
[application]
|
|
||||||
|
|
||||||
config/name="HTML5-File-Exchange-for-Godot"
|
|
||||||
run/main_scene="res://sample.tscn"
|
|
||||||
config/features=PackedStringArray("4.2")
|
|
||||||
config/icon="res://icon.png"
|
|
||||||
|
|
||||||
[autoload]
|
|
||||||
|
|
||||||
HTML5File="*res://addons/HTML5FileExchange/HTML5FileExchange.gd"
|
|
||||||
|
|
||||||
[editor_plugins]
|
|
||||||
|
|
||||||
enabled=PackedStringArray("res://addons/HTML5FileExchange/plugin.cfg")
|
|
||||||
|
|
||||||
[physics]
|
|
||||||
|
|
||||||
common/enable_pause_aware_picking=true
|
|
||||||
|
|
||||||
[rendering]
|
|
||||||
|
|
||||||
environment/defaults/default_environment="res://default_env.tres"
|
|
34
sample.tscn
34
sample.tscn
@ -1,34 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://dk0u6daqunn2k"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://Sample.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="Control" type="Control"]
|
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_right = 40.0
|
|
||||||
offset_bottom = 40.0
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="UploadButton" type="Button" parent="VBoxContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Upload"
|
|
||||||
|
|
||||||
[node name="DownloadButton" type="Button" parent="VBoxContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Download"
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
script = ExtResource("1")
|
|
||||||
|
|
||||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/UploadButton" to="VBoxContainer/TextureRect" method="_on_UploadButton_pressed"]
|
|
||||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/DownloadButton" to="VBoxContainer/TextureRect" method="_on_DownloadButton_pressed"]
|
|
Loading…
Reference in New Issue
Block a user