This commit is contained in:
Dante
2026-05-21 23:40:20 -07:00
parent 3e2915dff7
commit 877a69d844
5737 changed files with 29796 additions and 1589684 deletions

View File

@ -27,7 +27,7 @@ class CanvasImage extends Image {
var g2canvas: CanvasGraphics = null;
public static function init() {
var canvas: Dynamic = Browser.document.createElement("canvas");
final canvas = Browser.document.createCanvasElement();
if (canvas != null) {
context = canvas.getContext("2d");
canvas.width = 2048;
@ -56,7 +56,7 @@ class CanvasImage extends Image {
override function get_g2(): kha.graphics2.Graphics {
if (g2canvas == null) {
var canvas: Dynamic = Browser.document.createElement("canvas");
final canvas = Browser.document.createCanvasElement();
image = canvas;
var context = canvas.getContext("2d");
canvas.width = width;

View File

@ -66,7 +66,7 @@ class Display {
public var pixelsPerInch(get, never): Int;
function get_pixelsPerInch(): Int {
var dpiElement = Browser.document.createElement("div");
final dpiElement = Browser.document.createDivElement();
dpiElement.style.position = "absolute";
dpiElement.style.width = "1in";
dpiElement.style.height = "1in";

View File

@ -1,5 +1,6 @@
package kha;
import js.html.FileReader;
import haxe.io.Bytes;
import js.html.ImageElement;
import js.html.CanvasElement;
@ -90,11 +91,29 @@ class Image implements Canvas implements Resource {
public static function fromEncodedBytes(bytes: Bytes, fileExtention: String, doneCallback: Image->Void, errorCallback: String->Void,
readable: Bool = false): Void {
var dataUrl = "data:image;base64," + haxe.crypto.Base64.encode(bytes);
var imageElement = cast(js.Browser.document.createElement("img"), ImageElement);
imageElement.onload = function() doneCallback(fromImage(imageElement, readable));
imageElement.onerror = function() errorCallback("Image was not created");
imageElement.src = dataUrl;
final mime = switch fileExtention {
case "jpg": 'image/jpeg';
case ext: 'image/$ext';
}
bufferToBase64(cast bytes.getData(), dataUrl -> {
final imageElement = js.Browser.document.createImageElement();
imageElement.onload = () -> doneCallback(fromImage(imageElement, readable));
imageElement.onerror = () -> errorCallback("Image was not created");
imageElement.src = 'data:$mime;base64,$dataUrl';
}, () -> {
errorCallback("Image was not created");
});
}
static function bufferToBase64(buffer:js.lib.Uint8Array, onLoad:(base64:String)->Void, onError:()->Void) {
final reader = new FileReader();
reader.onload = () -> {
final result:String = reader.result;
// remove the `data:application/octet-stream;base64,` part from the start
onLoad(result.substr(result.indexOf(',') + 1));
}
reader.onerror = () -> onError();
reader.readAsDataURL(new js.html.Blob([buffer]));
}
public static function fromVideo(video: kha.Video): Image {

View File

@ -32,7 +32,7 @@ class LoaderImpl {
}, failed);
}
else {
var img: ImageElement = cast Browser.document.createElement("img");
final img = Browser.document.createImageElement();
img.onerror = function(event: Dynamic) failed({url: desc.files[0], error: event});
img.onload = function(event: Dynamic) done(Image.fromImage(img, readable));
img.crossOrigin = "";
@ -159,9 +159,9 @@ class LoaderImpl {
}
public static function loadRemote(desc: Dynamic, done: Blob->Void, failed: AssetError->Void) {
var request = untyped new XMLHttpRequest();
var request = new XMLHttpRequest();
request.open("GET", desc.files[0], true);
request.responseType = "arraybuffer";
request.responseType = ARRAYBUFFER;
request.onreadystatechange = function() {
if (request.readyState != 4)
@ -174,12 +174,6 @@ class LoaderImpl {
var byteArray: Dynamic = Syntax.code("new Uint8Array(arrayBuffer)");
bytes = Bytes.ofData(byteArray);
}
else if (request.responseBody != null) {
var data: Dynamic = untyped Syntax.code("VBArray(request.responseBody).toArray()");
bytes = Bytes.alloc(data.length);
for (i in 0...data.length)
bytes.set(i, data[i]);
}
else {
failed({url: desc.files[0]});
return;

View File

@ -6,8 +6,10 @@ import js.html.CanvasElement;
import js.html.ClipboardEvent;
import js.html.DeviceMotionEvent;
import js.html.DeviceOrientationEvent;
import js.html.DragEvent;
import js.html.KeyboardEvent;
import js.html.MouseEvent;
import js.html.PointerEvent;
import js.html.Touch;
import js.html.TouchEvent;
import js.html.WebSocket;
@ -213,6 +215,7 @@ class SystemImpl {
}
public static function vibrate(ms: Int): Void {
if (Browser.navigator.vibrate == null) return;
Browser.navigator.vibrate(ms);
}
@ -227,7 +230,7 @@ class SystemImpl {
}
static inline var maxGamepads: Int = 4;
public static var frame: Framebuffer;
static var frame: Framebuffer;
static var keyboard: Keyboard = null;
static var mouse: kha.input.Mouse;
static var surface: Surface;
@ -311,14 +314,14 @@ class SystemImpl {
}
public static function copyToClipboard(text: String) {
var textArea = Browser.document.createElement("textarea");
untyped textArea.value = text;
var textArea = Browser.document.createTextAreaElement();
textArea.value = text;
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
Browser.document.body.appendChild(textArea);
textArea.focus();
untyped textArea.select();
textArea.select();
try {
Browser.document.execCommand("copy");
}
@ -388,8 +391,7 @@ class SystemImpl {
{
alpha: false,
antialias: options.framebuffer.samplesPerPixel > 1,
stencil: true,
xrCompatible: true
stencil: true
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
@ -418,8 +420,7 @@ class SystemImpl {
{
alpha: false,
antialias: options.framebuffer.samplesPerPixel > 1,
stencil: true,
xrCompatible: true
stencil: true
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
SystemImpl.gl.getExtension("OES_texture_float");
@ -462,7 +463,7 @@ class SystemImpl {
}
// canvas.getContext("2d").scale(transform, transform);
if (!mobile && kha.audio2.Audio._init()) {
if ((!mobile || options.audio.allowMobileWebAudio) && kha.audio2.Audio._init()) {
SystemImpl._hasWebAudio = true;
kha.audio2.Audio1._init();
}
@ -482,7 +483,7 @@ class SystemImpl {
canvas.focus();
#if kha_disable_context_menu
canvas.oncontextmenu = function(event: Dynamic) {
canvas.oncontextmenu = function(event: PointerEvent) {
event.stopPropagation();
event.preventDefault();
}
@ -505,12 +506,12 @@ class SystemImpl {
canvas.addEventListener("touchend", touchUp, false);
canvas.addEventListener("touchmove", touchMove, false);
canvas.addEventListener("touchcancel", touchCancel, false);
// prevent dragging canvas like images in Firefox
canvas.addEventListener("dragstart", (e: DragEvent) -> e.preventDefault());
// prevent dropping local files on page and replacing page with them
Browser.document.addEventListener("dragover", (e: DragEvent) -> e.preventDefault());
Browser.document.addEventListener("dragover", function(event) {
event.preventDefault();
});
Browser.document.addEventListener("drop", function(event: js.html.DragEvent) {
Browser.document.addEventListener("drop", function(event: DragEvent) {
event.preventDefault();
if (event.dataTransfer != null && event.dataTransfer.files != null) {
for (file in event.dataTransfer.files) {
@ -531,14 +532,7 @@ class SystemImpl {
static function initAnimate(callback: Window->Void) {
var canvas: CanvasElement = getCanvasElement();
var window: Dynamic = Browser.window;
var requestAnimationFrame = window.requestAnimationFrame;
if (requestAnimationFrame == null)
requestAnimationFrame = window.mozRequestAnimationFrame;
if (requestAnimationFrame == null)
requestAnimationFrame = window.webkitRequestAnimationFrame;
if (requestAnimationFrame == null)
requestAnimationFrame = window.msRequestAnimationFrame;
final window = Browser.window;
var isRefreshRateDetectionActive = false;
var lastTimestamp = 0.0;
@ -549,16 +543,7 @@ class SystemImpl {
];
function animate(timestamp) {
if (untyped Browser.window._khaSkipWindowRender == true) {
if (requestAnimationFrame != null)
requestAnimationFrame(animate);
return;
}
if (requestAnimationFrame == null)
Browser.window.setTimeout(animate, 1000.0 / 60.0);
else
requestAnimationFrame(animate);
window.requestAnimationFrame(animate);
var sysGamepads = getGamepads();
if (sysGamepads != null) {
@ -639,64 +624,38 @@ class SystemImpl {
}, 500);
Scheduler.start();
requestAnimationFrame(animate);
window.requestAnimationFrame(animate);
callback(SystemImpl.window);
}
public static function lockMouse(): Void {
untyped if (SystemImpl.khanvas.requestPointerLock) {
if (SystemImpl.khanvas.requestPointerLock != null) {
SystemImpl.khanvas.requestPointerLock();
}
else if (SystemImpl.khanvas.mozRequestPointerLock) {
SystemImpl.khanvas.mozRequestPointerLock();
}
else if (SystemImpl.khanvas.webkitRequestPointerLock) {
SystemImpl.khanvas.webkitRequestPointerLock();
}
}
public static function unlockMouse(): Void {
untyped if (document.exitPointerLock) {
document.exitPointerLock();
}
else if (document.mozExitPointerLock) {
document.mozExitPointerLock();
}
else if (document.webkitExitPointerLock) {
document.webkitExitPointerLock();
if (Browser.document.exitPointerLock != null) {
Browser.document.exitPointerLock();
}
}
public static function canLockMouse(): Bool {
return Syntax.code("'pointerLockElement' in document ||
'mozPointerLockElement' in document ||
'webkitPointerLockElement' in document");
return Syntax.code("'pointerLockElement' in document");
}
public static function isMouseLocked(): Bool {
return Syntax.code("document.pointerLockElement === kha_SystemImpl.khanvas ||
document.mozPointerLockElement === kha_SystemImpl.khanvas ||
document.webkitPointerLockElement === kha_SystemImpl.khanvas");
return Syntax.code("document.pointerLockElement === kha_SystemImpl.khanvas");
}
public static function notifyOfMouseLockChange(func: Void->Void, error: Void->Void): Void {
js.Browser.document.addEventListener("pointerlockchange", func, false);
js.Browser.document.addEventListener("mozpointerlockchange", func, false);
js.Browser.document.addEventListener("webkitpointerlockchange", func, false);
js.Browser.document.addEventListener("pointerlockerror", error, false);
js.Browser.document.addEventListener("mozpointerlockerror", error, false);
js.Browser.document.addEventListener("webkitpointerlockerror", error, false);
}
public static function removeFromMouseLockChange(func: Void->Void, error: Void->Void): Void {
js.Browser.document.removeEventListener("pointerlockchange", func, false);
js.Browser.document.removeEventListener("mozpointerlockchange", func, false);
js.Browser.document.removeEventListener("webkitpointerlockchange", func, false);
js.Browser.document.removeEventListener("pointerlockerror", error, false);
js.Browser.document.removeEventListener("mozpointerlockerror", error, false);
js.Browser.document.removeEventListener("webkitpointerlockerror", error, false);
}
static function setMouseXY(event: MouseEvent): Void {
@ -902,13 +861,6 @@ class SystemImpl {
var movementX = event.movementX;
var movementY = event.movementY;
if (event.movementX == null) {
movementX = (untyped event.mozMovementX != null) ? untyped event.mozMovementX : ((untyped event.webkitMovementX != null) ? untyped event.webkitMovementX : (mouseX
- lastMouseX));
movementY = (untyped event.mozMovementY != null) ? untyped event.mozMovementY : ((untyped event.webkitMovementY != null) ? untyped event.webkitMovementY : (mouseY
- lastMouseY));
}
// this ensures same behaviour across browser until they fix it
if (firefox) {
movementX = Std.int(movementX * Browser.window.devicePixelRatio);
@ -956,7 +908,9 @@ class SystemImpl {
}
setTouchXY(touch);
mouse.sendDownEvent(0, 0, touchX, touchY);
if (!Surface.listenedEventsBefore) {
mouse.sendDownEvent(0, 0, touchX, touchY);
}
surface.sendTouchStartEvent(id, touchX, touchY);
if (index == 0) {
lastFirstTouchX = touchX;
@ -979,7 +933,9 @@ class SystemImpl {
}
setTouchXY(touch);
mouse.sendUpEvent(0, 0, touchX, touchY);
if (!Surface.listenedEventsBefore) {
mouse.sendUpEvent(0, 0, touchX, touchY);
}
surface.sendTouchEndEvent(id, touchX, touchY);
}
insideInputEvent = false;
@ -998,7 +954,9 @@ class SystemImpl {
lastFirstTouchX = touchX;
lastFirstTouchY = touchY;
mouse.sendMoveEvent(0, touchX, touchY, movementX, movementY);
if (!Surface.listenedEventsBefore) {
mouse.sendMoveEvent(0, touchX, touchY, movementX, movementY);
}
}
var id = touch.identifier;
if (ios)
@ -1020,7 +978,9 @@ class SystemImpl {
id = iosTouchs.indexOf(id);
setTouchXY(touch);
mouse.sendUpEvent(0, 0, touchX, touchY);
if (!Surface.listenedEventsBefore) {
mouse.sendUpEvent(0, 0, touchX, touchY);
}
surface.sendTouchEndEvent(id, touchX, touchY);
}
iosTouchs = [];
@ -1311,15 +1271,45 @@ class SystemImpl {
return "unknown";
}
public static function setGamepadRumble(index: Int, leftAmount: Float, rightAmount: Float) {}
public static function setGamepadRumble(index: Int, leftAmount: Float, rightAmount: Float): Void {
final sysGamepads = getGamepads();
if (sysGamepads == null || sysGamepads[index] == null) {
return;
}
final gamepad = sysGamepads[index];
final duration = 10000; // 10 seconds
if (untyped gamepad.vibrationActuator) {
if (leftAmount == 0 && rightAmount == 0) {
untyped gamepad.vibrationActuator.reset();
}
else {
untyped gamepad.vibrationActuator.playEffect('dual-rumble', {
duration: duration,
strongMagnitude: leftAmount,
weakMagnitude: rightAmount
});
}
}
else if (untyped gamepad.hapticActuators && untyped gamepad.hapticActuators.length > 0) {
final hapticActuator = untyped gamepad.hapticActuators[0];
if (leftAmount == 0 && rightAmount == 0) {
untyped gamepad.hapticActuators[0].pulse(0, 0);
}
else {
untyped gamepad.hapticActuators[0].pulse(leftAmount, duration);
}
}
}
static function getGamepads(): Array<js.html.Gamepad> {
if (chrome && kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsVrEnabled()) {
return null; // Chrome crashes if navigator.getGamepads() is called when using VR
}
if (untyped navigator.getGamepads) {
return js.Browser.navigator.getGamepads();
if (Browser.navigator.getGamepads != null) {
return Browser.navigator.getGamepads();
}
else {
return null;

View File

@ -21,7 +21,6 @@ class Window {
this.defaultHeight = defaultHeight;
windows.push(this);
resizeCallbacks[num] = [];
windows.push(this);
final observer: MutationObserver = new MutationObserver(function(mutations: Array<js.html.MutationRecord>, observer: MutationObserver) {
var isResize = false;
for (mutation in mutations) {

View File

@ -1,14 +1,14 @@
package kha.audio2;
import js.Syntax;
import js.Browser;
import js.Syntax;
import js.html.URL;
import js.html.audio.AudioContext;
import js.html.audio.AudioProcessingEvent;
import js.html.audio.ScriptProcessorNode;
import kha.Sound;
import kha.internal.IntBox;
import kha.js.AEAudioChannel;
import kha.Sound;
class Audio {
public static var disableGcInteractions = false;
@ -70,7 +70,7 @@ class Audio {
public static var samplesPerSecond: Int;
public static var audioCallback: kha.internal.IntBox->Buffer->Void;
public static var audioCallback: (outputBufferLength: IntBox, buffer: Buffer) -> Void;
static var virtualChannels: Array<VirtualStreamChannel> = [];

View File

@ -1,5 +1,6 @@
package kha.capture;
import js.Browser.navigator;
import js.html.audio.AudioProcessingEvent;
import kha.audio2.Buffer;
@ -16,8 +17,8 @@ class AudioCapture {
return;
}
var getUserMedia = untyped __js__("navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia");
getUserMedia.call(js.Browser.navigator, {audio: true}, function(stream: Dynamic) {
final getUserMedia = (navigator : Dynamic).getUserMedia;
getUserMedia.call(navigator, {audio: true}, function(stream: Dynamic) {
input = kha.audio2.Audio._context.createMediaStreamSource(stream);
var bufferSize = 1024 * 2;

View File

@ -1,12 +1,13 @@
package kha.capture;
import js.Browser.navigator;
import js.Browser;
class VideoCapture {
public static function init(initialized: kha.Video->Void, error: Void->Void): Void {
var getUserMedia = untyped __js__("navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia");
getUserMedia.call(js.Browser.navigator, {audio: true, video: true}, function(stream: Dynamic) {
var element: js.html.VideoElement = cast Browser.document.createElement("video");
final getUserMedia = (navigator : Dynamic).getUserMedia;
getUserMedia.call(navigator, {audio: true, video: true}, function(stream: Dynamic) {
final element = Browser.document.createVideoElement();
element.srcObject = stream;
element.onloadedmetadata = function(e) {
initialized(kha.js.Video.fromElement(element));

View File

@ -0,0 +1,22 @@
package kha.graphics4;
import haxe.io.Bytes;
import kha.Blob;
class ComputeShader {
public function new(sources: Array<Blob>, files: Array<String>) {
}
public function delete(): Void {
}
public function getConstantLocation(name: String): ConstantLocation {
return null;
}
public function getTextureUnit(name: String): TextureUnit {
return null;
}
}

View File

@ -0,0 +1,24 @@
package kha.graphics4;
class ShaderStorageBuffer {
var data: Array<Int>;
var myCount: Int;
public function new(indexCount: Int, type: VertexData) {
myCount = indexCount;
data = new Array<Int>();
data[myCount - 1] = 0;
}
public function delete(): Void {}
public function lock(): Array<Int> {
return data;
}
public function unlock(): Void {}
public function count(): Int {
return myCount;
}
}

View File

@ -105,18 +105,7 @@ class CanvasGraphics extends Graphics {
}
override function set_imageScaleQuality(value: ImageScaleQuality): ImageScaleQuality {
if (value == ImageScaleQuality.Low) {
untyped canvas.mozImageSmoothingEnabled = false;
untyped canvas.webkitImageSmoothingEnabled = false;
untyped canvas.msImageSmoothingEnabled = false;
canvas.imageSmoothingEnabled = false;
}
else {
untyped canvas.mozImageSmoothingEnabled = true;
untyped canvas.webkitImageSmoothingEnabled = true;
untyped canvas.msImageSmoothingEnabled = true;
canvas.imageSmoothingEnabled = true;
}
canvas.imageSmoothingEnabled = value == ImageScaleQuality.High;
return scaleQuality = value;
}

View File

@ -0,0 +1,28 @@
package kha.js;
import js.html.MessageChannel;
class Microtask {
static var messageChannel: MessageChannel;
static final callbacks: Array<() -> Void> = [];
static function init(): Void {
if (messageChannel != null) {
return;
}
messageChannel = new MessageChannel();
messageChannel.port1.onmessage = _ -> {
final copy = callbacks.copy();
callbacks.resize(0);
for (callback in copy) {
callback();
}
};
}
public static function queueMicrotask(callback: () -> Void): Void {
init();
callbacks.push(callback);
messageChannel.port2.postMessage(js.Lib.undefined);
}
}

View File

@ -32,7 +32,7 @@ class Video extends kha.Video {
video.done = done;
video.element = cast Browser.document.createElement("video");
video.element = Browser.document.createVideoElement();
video.filenames = [];
for (filename in filenames) {

View File

@ -16,7 +16,7 @@ import kha.audio2.Audio;
super();
this.offset = 0;
this.buffer = buffer;
this.startTime = Audio._context.currentTime;
this.startTime = Audio._context.currentTime;
this.source = Audio._context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect(Audio._context.destination);
@ -91,7 +91,7 @@ class WebAudioSound extends kha.Sound {
var i = 0;
final lidx = len * 2;
function uncompressInner() {
var chk_len = idx + 11025;
var chk_len = idx + 44100;
var next_chk = chk_len > lidx ? lidx : chk_len;
while (idx < next_chk) {
uncompressedData[idx] = ch0[i];
@ -100,7 +100,7 @@ class WebAudioSound extends kha.Sound {
++i;
}
if (idx < lidx)
js.Browser.window.setTimeout(uncompressInner, 0);
Microtask.queueMicrotask(uncompressInner);
else {
compressedData = null;
done();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff