forked from LeenkxTeam/LNXSDK
Next patch
This commit is contained in:
@ -227,7 +227,7 @@ class SystemImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline var maxGamepads: Int = 4;
|
static inline var maxGamepads: Int = 4;
|
||||||
static var frame: Framebuffer;
|
public static var frame: Framebuffer;
|
||||||
static var keyboard: Keyboard = null;
|
static var keyboard: Keyboard = null;
|
||||||
static var mouse: kha.input.Mouse;
|
static var mouse: kha.input.Mouse;
|
||||||
static var surface: Surface;
|
static var surface: Surface;
|
||||||
@ -388,7 +388,8 @@ class SystemImpl {
|
|||||||
{
|
{
|
||||||
alpha: false,
|
alpha: false,
|
||||||
antialias: options.framebuffer.samplesPerPixel > 1,
|
antialias: options.framebuffer.samplesPerPixel > 1,
|
||||||
stencil: true
|
stencil: true,
|
||||||
|
xrCompatible: true
|
||||||
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
|
}); // preserveDrawingBuffer: true } ); Warning: preserveDrawingBuffer can cause huge performance issues on mobile browsers
|
||||||
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
||||||
|
|
||||||
@ -417,7 +418,8 @@ class SystemImpl {
|
|||||||
{
|
{
|
||||||
alpha: false,
|
alpha: false,
|
||||||
antialias: options.framebuffer.samplesPerPixel > 1,
|
antialias: options.framebuffer.samplesPerPixel > 1,
|
||||||
stencil: true
|
stencil: true,
|
||||||
|
xrCompatible: true
|
||||||
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
|
}); // preserveDrawingBuffer: true } ); WARNING: preserveDrawingBuffer causes huge performance issues (on mobile browser)!
|
||||||
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
SystemImpl.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
||||||
SystemImpl.gl.getExtension("OES_texture_float");
|
SystemImpl.gl.getExtension("OES_texture_float");
|
||||||
@ -547,6 +549,12 @@ class SystemImpl {
|
|||||||
];
|
];
|
||||||
|
|
||||||
function animate(timestamp) {
|
function animate(timestamp) {
|
||||||
|
if (untyped Browser.window._khaSkipWindowRender == true) {
|
||||||
|
if (requestAnimationFrame != null)
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (requestAnimationFrame == null)
|
if (requestAnimationFrame == null)
|
||||||
Browser.window.setTimeout(animate, 1000.0 / 60.0);
|
Browser.window.setTimeout(animate, 1000.0 / 60.0);
|
||||||
else
|
else
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class Graphics implements kha.graphics4.Graphics {
|
|||||||
|
|
||||||
static var current: Graphics = null;
|
static var current: Graphics = null;
|
||||||
static var useVertexAttributes: Int = 0;
|
static var useVertexAttributes: Int = 0;
|
||||||
|
public static var vrFramebufferBound: Bool = false;
|
||||||
|
|
||||||
public function new(renderTarget: Canvas = null) {
|
public function new(renderTarget: Canvas = null) {
|
||||||
this.renderTarget = renderTarget;
|
this.renderTarget = renderTarget;
|
||||||
@ -89,8 +90,10 @@ class Graphics implements kha.graphics4.Graphics {
|
|||||||
SystemImpl.gl.enable(GL.BLEND);
|
SystemImpl.gl.enable(GL.BLEND);
|
||||||
SystemImpl.gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
|
SystemImpl.gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
|
||||||
if (renderTarget == null) {
|
if (renderTarget == null) {
|
||||||
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null);
|
if (!vrFramebufferBound) {
|
||||||
SystemImpl.gl.viewport(0, 0, System.windowWidth(), System.windowHeight());
|
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, null);
|
||||||
|
SystemImpl.gl.viewport(0, 0, System.windowWidth(), System.windowHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, renderTargetFrameBuffer);
|
SystemImpl.gl.bindFramebuffer(GL.FRAMEBUFFER, renderTargetFrameBuffer);
|
||||||
|
|||||||
@ -13,10 +13,29 @@ import kha.SystemImpl;
|
|||||||
|
|
||||||
class VrInterface extends kha.vr.VrInterface {
|
class VrInterface extends kha.vr.VrInterface {
|
||||||
var vrEnabled: Bool = false;
|
var vrEnabled: Bool = false;
|
||||||
|
var isWebXR: Bool = false;
|
||||||
|
|
||||||
var vrDisplay: Dynamic;
|
var vrDisplay: Dynamic;
|
||||||
var frameData: Dynamic;
|
var frameData: Dynamic;
|
||||||
|
|
||||||
|
var xrSession: Dynamic;
|
||||||
|
var xrRefSpace: Dynamic;
|
||||||
|
public var xrGLLayer: Dynamic;
|
||||||
|
public var currentFrame: Dynamic;
|
||||||
|
public var currentViews: Dynamic;
|
||||||
|
public var currentViewerPose: Dynamic;
|
||||||
|
public var currentInputSources: Dynamic;
|
||||||
|
var xrAnimationFrameHandle: Int = -1;
|
||||||
|
|
||||||
|
public var _glContext: Dynamic;
|
||||||
|
public var _leftViewport: Dynamic;
|
||||||
|
public var _rightViewport: Dynamic;
|
||||||
|
public var _cachedViewsLength: Int = 0;
|
||||||
|
|
||||||
|
var savedCanvasWidth: Int = 0;
|
||||||
|
var savedCanvasHeight: Int = 0;
|
||||||
|
var browserRAFId: Int = -1;
|
||||||
|
|
||||||
var leftProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
var leftProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
var rightProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
var rightProjectionMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
var leftViewMatrix: FastMatrix4 = FastMatrix4.identity();
|
var leftViewMatrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
@ -30,7 +49,20 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
#if kha_webvr
|
#if kha_webvr
|
||||||
var displayEnabled: Bool = Syntax.code("navigator.getVRDisplays");
|
var webXREnabled: Bool = Syntax.code("navigator.xr");
|
||||||
|
if (webXREnabled) {
|
||||||
|
isWebXR = true;
|
||||||
|
vrEnabled = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var displayEnabled: Bool = Syntax.code("navigator.getVRDisplays");
|
||||||
|
if (displayEnabled) {
|
||||||
|
isWebXR = false;
|
||||||
|
vrEnabled = true;
|
||||||
|
getVRDisplays();
|
||||||
|
trace("WebVR 1.1 API detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
var displayEnabled = false;
|
var displayEnabled = false;
|
||||||
#end
|
#end
|
||||||
@ -64,27 +96,475 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function onVRRequestPresent() {
|
public override function onVRRequestPresent() {
|
||||||
|
if (isWebXR) {
|
||||||
|
requestWebXRSession();
|
||||||
|
} else {
|
||||||
|
// WebVR 1.1
|
||||||
|
try {
|
||||||
|
vrDisplay.requestPresent([{source: SystemImpl.khanvas}]).then(function() {
|
||||||
|
onResize();
|
||||||
|
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to requestPresent WebVR: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestWebXRSession() {
|
||||||
|
var vrScaleFactor = 1.0;
|
||||||
|
#if lnx_vr
|
||||||
|
vrScaleFactor = leenkx.renderpath.Inc.getSuperSampling();
|
||||||
|
trace("[VR] Using renderpath superSample as framebufferScaleFactor: " + vrScaleFactor);
|
||||||
|
#end
|
||||||
|
|
||||||
try {
|
try {
|
||||||
vrDisplay.requestPresent([{source: SystemImpl.khanvas}]).then(function() {
|
Syntax.code("
|
||||||
onResize();
|
|
||||||
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
|
||||||
});
|
let gl = null;
|
||||||
|
let canvas = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof kha_SystemImpl !== 'undefined') {
|
||||||
|
gl = kha_SystemImpl.gl;
|
||||||
|
canvas = kha_SystemImpl.khanvas;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
trace('kha_SystemImpl access failed: ' + e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.querySelector('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas && !gl) {
|
||||||
|
const contextAttributes = { xrCompatible: true, antialias: true, alpha: false };
|
||||||
|
gl = canvas.getContext('webgl2', contextAttributes) ||
|
||||||
|
canvas.getContext('webgl', contextAttributes) ||
|
||||||
|
canvas.getContext('experimental-webgl', contextAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.getElementById('khanvas');
|
||||||
|
if (canvas && !gl) {
|
||||||
|
gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const self = this;
|
||||||
|
const glContext = gl;
|
||||||
|
|
||||||
|
self._glContext = glContext;
|
||||||
|
");
|
||||||
|
|
||||||
|
Syntax.code("
|
||||||
|
self._vrRenderCallback = function() {
|
||||||
|
self.vrRenderCallback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAndRequestSession = async () => {
|
||||||
|
try {
|
||||||
|
const supported = await navigator.xr.isSessionSupported('immersive-vr');
|
||||||
|
if (!supported) {
|
||||||
|
trace('immersive-vr session not supported');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
trace('WARN: isSessionSupported failed: ' + e.message);
|
||||||
|
// Continue anyway as some browsers do not support the check itself
|
||||||
|
}
|
||||||
|
|
||||||
|
return await navigator.xr.requestSession('immersive-vr', {
|
||||||
|
optionalFeatures: ['local-floor', 'hand-tracking', 'bounded-floor']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAndRequestSession().then(async (session) => {
|
||||||
|
self.xrSession = session;
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window._khaSkipWindowRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextAttributes = glContext.getContextAttributes();
|
||||||
|
if (!contextAttributes || !contextAttributes.xrCompatible) {
|
||||||
|
await glContext.makeXRCompatible();
|
||||||
|
}
|
||||||
|
self.xrGLLayer = new XRWebGLLayer(session, glContext, {
|
||||||
|
depth: true, // Essential for depth testing
|
||||||
|
stencil: false, // Not needed, wastes memory
|
||||||
|
alpha: false, // Not needed in VR wastes
|
||||||
|
antialias: true, // Smooth rendering
|
||||||
|
framebufferScaleFactor: {0} // VR resolution quality from renderpath
|
||||||
|
});
|
||||||
|
|
||||||
|
if (self.xrGLLayer.framebufferWidth === 0 || self.xrGLLayer.framebufferHeight === 0) {
|
||||||
|
trace('XRWebGLLayer framebuffer has invalid dimensions');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
session.updateRenderState({
|
||||||
|
baseLayer: self.xrGLLayer
|
||||||
|
});
|
||||||
|
const handlers = {};
|
||||||
|
|
||||||
|
handlers.end = () => {
|
||||||
|
self.onSessionEnd();
|
||||||
|
};
|
||||||
|
session.addEventListener('end', handlers.end);
|
||||||
|
|
||||||
|
handlers.select = (event) => {
|
||||||
|
if (self.onSelect) self.onSelect(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('select', handlers.select);
|
||||||
|
|
||||||
|
handlers.selectstart = (event) => {
|
||||||
|
if (self.onSelectStart) self.onSelectStart(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('selectstart', handlers.selectstart);
|
||||||
|
|
||||||
|
handlers.selectend = (event) => {
|
||||||
|
if (self.onSelectEnd) self.onSelectEnd(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('selectend', handlers.selectend);
|
||||||
|
|
||||||
|
handlers.squeeze = (event) => {
|
||||||
|
if (self.onSqueeze) self.onSqueeze(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeeze', handlers.squeeze);
|
||||||
|
|
||||||
|
handlers.squeezestart = (event) => {
|
||||||
|
if (self.onSqueezeStart) self.onSqueezeStart(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeezestart', handlers.squeezestart);
|
||||||
|
|
||||||
|
handlers.squeezeend = (event) => {
|
||||||
|
if (self.onSqueezeEnd) self.onSqueezeEnd(event);
|
||||||
|
};
|
||||||
|
session.addEventListener('squeezeend', handlers.squeezeend);
|
||||||
|
|
||||||
|
session.addEventListener('inputsourceschange', handlers.inputsourceschange);
|
||||||
|
|
||||||
|
handlers.visibilitychange = (event) => {
|
||||||
|
const state = event.session.visibilityState;
|
||||||
|
|
||||||
|
};
|
||||||
|
session.addEventListener('visibilitychange', handlers.visibilitychange);
|
||||||
|
|
||||||
|
self._eventHandlers = handlers;
|
||||||
|
|
||||||
|
const requestRefSpace = async () => {
|
||||||
|
const spaces = ['local-floor', 'local'];
|
||||||
|
for (const space of spaces) {
|
||||||
|
try {
|
||||||
|
const refSpace = await session.requestReferenceSpace(space);
|
||||||
|
return refSpace;
|
||||||
|
} catch (e) {
|
||||||
|
trace(space + ' not supported');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace('No reference space supported');
|
||||||
|
};
|
||||||
|
|
||||||
|
requestRefSpace().then((refSpace) => {
|
||||||
|
self.xrRefSpace = refSpace;
|
||||||
|
|
||||||
|
if (canvas && canvas.width) {
|
||||||
|
self.savedCanvasWidth = canvas.width;
|
||||||
|
self.savedCanvasHeight = canvas.height;
|
||||||
|
} else {
|
||||||
|
const canvasFallback = document.querySelector('canvas');
|
||||||
|
if (canvasFallback) {
|
||||||
|
self.savedCanvasWidth = canvasFallback.width;
|
||||||
|
self.savedCanvasHeight = canvasFallback.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFrame = (time, frame) => {
|
||||||
|
try {
|
||||||
|
if (self.xrSession) {
|
||||||
|
self.xrAnimationFrameHandle = self.xrSession.requestAnimationFrame(onFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self._lastFrameTime) self._lastFrameTime = time;
|
||||||
|
const deltaTime = time - self._lastFrameTime;
|
||||||
|
self._lastFrameTime = time;
|
||||||
|
|
||||||
|
if (!window._xrFrameCount) window._xrFrameCount = 0;
|
||||||
|
window._xrFrameCount++;
|
||||||
|
|
||||||
|
if (glContext && self.xrSession && self.xrSession.renderState && self.xrSession.renderState.baseLayer) {
|
||||||
|
const layer = self.xrSession.renderState.baseLayer;
|
||||||
|
if (layer.framebuffer) {
|
||||||
|
const pose = frame.getViewerPose(self.xrRefSpace);
|
||||||
|
if (pose && pose.views && pose.views.length > 0) {
|
||||||
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, layer.framebuffer);
|
||||||
|
|
||||||
|
let bgR = 0, bgG = 0, bgB = 0;
|
||||||
|
if (typeof iron !== 'undefined' && iron.Scene && iron.Scene.active && iron.Scene.active.world && iron.Scene.active.world.raw) {
|
||||||
|
const bgColor = iron.Scene.active.world.raw.background_color;
|
||||||
|
if (bgColor !== undefined) {
|
||||||
|
bgR = ((bgColor >> 16) & 255) / 255;
|
||||||
|
bgG = ((bgColor >> 8) & 255) / 255;
|
||||||
|
bgB = (bgColor & 255) / 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const view of pose.views) {
|
||||||
|
const vp = layer.getViewport(view);
|
||||||
|
glContext.viewport(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
glContext.scissor(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
glContext.enable(glContext.SCISSOR_TEST);
|
||||||
|
|
||||||
|
glContext.clearColor(bgR, bgG, bgB, 1.0);
|
||||||
|
glContext.clear(glContext.COLOR_BUFFER_BIT | glContext.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
}
|
||||||
|
glContext.disable(glContext.SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.xrSession) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pose = frame.getViewerPose(self.xrRefSpace);
|
||||||
|
if (!pose) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pose.emulatedPosition && !self._emulatedPosLogged) {
|
||||||
|
self._emulatedPosLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const views = pose.views;
|
||||||
|
|
||||||
|
if (!self.xrSession.renderState || !self.xrSession.renderState.baseLayer) {
|
||||||
|
if (!self._noRenderStateLogged) {
|
||||||
|
self._noRenderStateLogged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const glLayer = self.xrSession.renderState.baseLayer;
|
||||||
|
|
||||||
|
if (!views || views.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.xrSession.visibilityState === 'hidden') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentFrame = frame;
|
||||||
|
self.currentViews = views;
|
||||||
|
self.currentViewerPose = pose; /
|
||||||
|
if (self.xrSession && self.xrSession.inputSources) {
|
||||||
|
self.currentInputSources = self.xrSession.inputSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glContext.isContextLost()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glContext || !glLayer || !glLayer.framebuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glContext.bindVertexArray) {
|
||||||
|
glContext.bindVertexArray(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (glContext.getError() !== glContext.NO_ERROR) {
|
||||||
|
// Drain error queue
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, glLayer.framebuffer);
|
||||||
|
|
||||||
|
|
||||||
|
const bindError = glContext.getError();
|
||||||
|
if (bindError !== glContext.NO_ERROR && !self._bindErrorLogged) {
|
||||||
|
self._bindErrorLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fbStatus = glContext.checkFramebufferStatus(glContext.FRAMEBUFFER);
|
||||||
|
if (fbStatus !== glContext.FRAMEBUFFER_COMPLETE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glContext.enable(glContext.DEPTH_TEST);
|
||||||
|
glContext.depthFunc(glContext.LEQUAL);
|
||||||
|
glContext.depthMask(true);
|
||||||
|
glContext.colorMask(true, true, true, true);
|
||||||
|
glContext.disable(glContext.BLEND);
|
||||||
|
glContext.enable(glContext.CULL_FACE);
|
||||||
|
glContext.cullFace(glContext.BACK);
|
||||||
|
glContext.frontFace(glContext.CCW);
|
||||||
|
glContext.disable(glContext.STENCIL_TEST);
|
||||||
|
glContext.disable(glContext.POLYGON_OFFSET_FILL);
|
||||||
|
|
||||||
|
|
||||||
|
if (!self._fbLogged && p) {
|
||||||
|
const depthTest = glContext.isEnabled(glContext.DEPTH_TEST);
|
||||||
|
const cullFace = glContext.isEnabled(glContext.CULL_FACE);
|
||||||
|
const blend = glContext.isEnabled(glContext.BLEND);
|
||||||
|
self._fbLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length >= 1) {
|
||||||
|
try {
|
||||||
|
self._leftViewport = glLayer.getViewport(views[0]);
|
||||||
|
self._rightViewport = views.length >= 2 ? glLayer.getViewport(views[1]) : null;
|
||||||
|
self._cachedViewsLength = views.length;
|
||||||
|
|
||||||
|
if (!self._leftViewport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (views.length >= 1) {
|
||||||
|
self.leftProjectionMatrix = self.createMatrixFromArray(views[0].projectionMatrix);
|
||||||
|
self.leftViewMatrix = self.createMatrixFromArray(views[0].transform.inverse.matrix);
|
||||||
|
}
|
||||||
|
if (views.length >= 2) {
|
||||||
|
self.rightProjectionMatrix = self.createMatrixFromArray(views[1].projectionMatrix);
|
||||||
|
self.rightViewMatrix = self.createMatrixFromArray(views[1].transform.inverse.matrix);
|
||||||
|
} else if (views.length === 1) {
|
||||||
|
self.rightProjectionMatrix = self.leftProjectionMatrix;
|
||||||
|
self.rightViewMatrix = self.leftViewMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self._vrRenderCallback) {
|
||||||
|
self._vrRenderCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('XR Frame Error:', err);
|
||||||
|
} finally {
|
||||||
|
self.currentFrame = null;
|
||||||
|
self.currentViews = null;
|
||||||
|
self.currentInputSources = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.xrAnimationFrameHandle = session.requestAnimationFrame(onFrame);
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('REF SPACE FAILED: ' + err.message );
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('SESSION FAILED: ' + err);
|
||||||
|
});
|
||||||
|
", vrScaleFactor);
|
||||||
}
|
}
|
||||||
catch (err:Dynamic) {
|
catch (err:Dynamic) {
|
||||||
trace("Failed to requestPresent.");
|
trace("Failed to requestSession (WebXR).");
|
||||||
trace(err);
|
trace(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function onVRExitPresent() {
|
function onSessionEnd() {
|
||||||
try {
|
var canvas = SystemImpl.khanvas;
|
||||||
vrDisplay.exitPresent([{source: SystemImpl.khanvas}]).then(function() {
|
if (canvas == null) {
|
||||||
onResize();
|
canvas = Syntax.code("document.querySelector('canvas')");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (err:Dynamic) {
|
|
||||||
trace("Failed to exitPresent.");
|
if (canvas != null && savedCanvasWidth > 0 && savedCanvasHeight > 0) {
|
||||||
trace(err);
|
canvas.width = savedCanvasWidth;
|
||||||
|
canvas.height = savedCanvasHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xrSession != null) {
|
||||||
|
Syntax.code("
|
||||||
|
if (this.xrAnimationFrameHandle !== -1 && this.xrSession) {
|
||||||
|
this.xrSession.cancelAnimationFrame(this.xrAnimationFrameHandle);
|
||||||
|
this.xrAnimationFrameHandle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._eventHandlers && this.xrSession) {
|
||||||
|
const handlers = this._eventHandlers;
|
||||||
|
const events = ['end', 'select', 'selectstart', 'selectend', 'squeeze', 'squeezestart', 'squeezeend', 'inputsourceschange', 'visibilitychange'];
|
||||||
|
for (const event of events) {
|
||||||
|
if (handlers[event]) {
|
||||||
|
this.xrSession.removeEventListener(event, handlers[event]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._eventHandlers = null;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
Syntax.code("
|
||||||
|
const gl = this._glContext;
|
||||||
|
if (gl) {
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // Restore default framebuffer
|
||||||
|
gl.disable(gl.SCISSOR_TEST);
|
||||||
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window._khaSkipWindowRender = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.xrSession = null;
|
||||||
|
this.xrRefSpace = null;
|
||||||
|
this.xrGLLayer = null;
|
||||||
|
this.currentFrame = null;
|
||||||
|
this.currentViews = null;
|
||||||
|
this.currentInputSources = null;
|
||||||
|
this._lastFrameTime = null;
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
delete window._xrFrameCount;
|
||||||
|
delete window._ironRenderCount;
|
||||||
|
delete window._slowFrameCount;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function onVRExitPresent() {
|
||||||
|
if (isWebXR) {
|
||||||
|
try {
|
||||||
|
if (xrSession != null) {
|
||||||
|
Syntax.code("
|
||||||
|
if (this.xrSession) {
|
||||||
|
this.xrSession.end().then(() => {
|
||||||
|
trace('Session ended');
|
||||||
|
}).catch((err) => {
|
||||||
|
trace('Session.end() failed:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
");
|
||||||
|
xrSession = null;
|
||||||
|
xrRefSpace = null;
|
||||||
|
xrGLLayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to exitPresent in WebXR");
|
||||||
|
trace(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// WebVR 1.1
|
||||||
|
try {
|
||||||
|
vrDisplay.exitPresent([{source: SystemImpl.khanvas}]).then(function() {
|
||||||
|
onResize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err:Dynamic) {
|
||||||
|
trace("Failed to exitPresent");
|
||||||
|
trace(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +596,25 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onResize() {
|
function onResize() {
|
||||||
if (vrDisplay != null && vrDisplay.isPresenting) {
|
if (isWebXR) {
|
||||||
SystemImpl.khanvas.width = vrWidth;
|
return;
|
||||||
SystemImpl.khanvas.height = vrHeight;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SystemImpl.khanvas.width = width;
|
// WebVR 1.1
|
||||||
SystemImpl.khanvas.height = height;
|
if (vrDisplay != null && vrDisplay.isPresenting) {
|
||||||
|
var canvas = SystemImpl.khanvas;
|
||||||
|
if (canvas != null) {
|
||||||
|
canvas.width = vrWidth;
|
||||||
|
canvas.height = vrHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var canvas = SystemImpl.khanvas;
|
||||||
|
if (canvas != null) {
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,9 +666,11 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function IsPresenting(): Bool {
|
public override function IsPresenting(): Bool {
|
||||||
|
var presenting = false;
|
||||||
if (vrDisplay != null)
|
if (vrDisplay != null)
|
||||||
return vrDisplay.isPresenting;
|
presenting = vrDisplay.isPresenting;
|
||||||
return false;
|
}
|
||||||
|
return presenting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function IsVrEnabled(): Bool {
|
public override function IsVrEnabled(): Bool {
|
||||||
@ -207,6 +701,10 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
|
|
||||||
function createMatrixFromArray(array: Float32Array): FastMatrix4 {
|
function createMatrixFromArray(array: Float32Array): FastMatrix4 {
|
||||||
var matrix: FastMatrix4 = FastMatrix4.identity();
|
var matrix: FastMatrix4 = FastMatrix4.identity();
|
||||||
|
if (array == null || array.length < 16) {
|
||||||
|
trace("Warning: Invalid matrix array, using identity");
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
matrix._00 = array[0];
|
matrix._00 = array[0];
|
||||||
matrix._01 = array[1];
|
matrix._01 = array[1];
|
||||||
matrix._02 = array[2];
|
matrix._02 = array[2];
|
||||||
@ -246,4 +744,63 @@ class VrInterface extends kha.vr.VrInterface {
|
|||||||
}
|
}
|
||||||
return quaternion;
|
return quaternion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vrRenderCallback(): Void {
|
||||||
|
var g4 = kha.SystemImpl.frame != null ? kha.SystemImpl.frame.g4 : null;
|
||||||
|
|
||||||
|
|
||||||
|
if (g4 != null && iron.Scene.active != null && iron.RenderPath.active != null) {
|
||||||
|
|
||||||
|
if (untyped window._vrUpdateStarted == null) {
|
||||||
|
untyped window._vrUpdateStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
iron.system.Time.update();
|
||||||
|
|
||||||
|
iron.Scene.active.updateFrame();
|
||||||
|
|
||||||
|
|
||||||
|
js.Syntax.code("
|
||||||
|
const App = iron.App;
|
||||||
|
if (App) {
|
||||||
|
const frame = window._vrCallbackCount;
|
||||||
|
const inits = App.traitInits;
|
||||||
|
if (inits && inits.length > 0) {
|
||||||
|
for (let i = 0; i < inits.length; i++) {
|
||||||
|
inits[i]();
|
||||||
|
}
|
||||||
|
inits.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixedUpdates = App.traitFixedUpdates;
|
||||||
|
if (fixedUpdates) {
|
||||||
|
for (let i = 0; i < fixedUpdates.length; i++) {
|
||||||
|
fixedUpdates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = App.traitUpdates;
|
||||||
|
if (updates) {
|
||||||
|
for (let i = 0; i < updates.length; i++) {
|
||||||
|
updates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lateUpdates = App.traitLateUpdates;
|
||||||
|
if (lateUpdates) {
|
||||||
|
for (let i = 0; i < lateUpdates.length; i++) {
|
||||||
|
lateUpdates[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
");
|
||||||
|
iron.Scene.active.renderFrame(g4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (untyped window._vrSkipLogged == null) {
|
||||||
|
untyped window._vrSkipLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -157,4 +157,5 @@ extern class Krom {
|
|||||||
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
|
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
|
||||||
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
|
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
|
||||||
static function compute(x: Int, y: Int, z: Int): Void;
|
static function compute(x: Int, y: Int, z: Int): Void;
|
||||||
|
static function viewportSetCamera(posX: Float, posY: Float, posZ: Float, rotX: Float, rotY: Float, rotZ: Float, rotW: Float): Void;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user