forked from LeenkxTeam/LNXSDK
Patch_1
This commit is contained in:
@ -53,6 +53,21 @@ class App {
|
|||||||
static function update() {
|
static function update() {
|
||||||
if (Scene.active == null || !Scene.active.ready) return;
|
if (Scene.active == null || !Scene.active.ready) return;
|
||||||
|
|
||||||
|
// VR is handling it so we prevent double updates
|
||||||
|
// TODO: avoid js.Syntax
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var vrActive = false;
|
||||||
|
js.Syntax.code("
|
||||||
|
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
|
||||||
|
const vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr && vr.IsPresenting && vr.IsPresenting()) {
|
||||||
|
{0} = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
", vrActive);
|
||||||
|
if (vrActive) return;
|
||||||
|
#end
|
||||||
|
|
||||||
iron.system.Time.update();
|
iron.system.Time.update();
|
||||||
|
|
||||||
if (lastw == -1) {
|
if (lastw == -1) {
|
||||||
@ -138,6 +153,21 @@ class App {
|
|||||||
traitInits.splice(0, traitInits.length);
|
traitInits.splice(0, traitInits.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip for XR callback to handle rendering
|
||||||
|
// TODO: avoid js Syntax
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var vrActive = false;
|
||||||
|
js.Syntax.code("
|
||||||
|
if (typeof kha !== 'undefined' && kha.vr && kha.vr.VrInterface) {
|
||||||
|
const vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr && vr.IsPresenting && vr.IsPresenting()) {
|
||||||
|
{0} = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
", vrActive);
|
||||||
|
|
||||||
|
if (!vrActive) {
|
||||||
|
#end
|
||||||
Scene.active.renderFrame(frame.g4);
|
Scene.active.renderFrame(frame.g4);
|
||||||
|
|
||||||
for (f in traitRenders) {
|
for (f in traitRenders) {
|
||||||
@ -146,6 +176,10 @@ class App {
|
|||||||
|
|
||||||
render2D(frame);
|
render2D(frame);
|
||||||
|
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
renderPathTime = kha.Scheduler.realTime() - startTime;
|
renderPathTime = kha.Scheduler.realTime() - startTime;
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -18,10 +18,44 @@ import iron.object.LightObject;
|
|||||||
import iron.object.MeshObject;
|
import iron.object.MeshObject;
|
||||||
import iron.object.Uniforms;
|
import iron.object.Uniforms;
|
||||||
import iron.object.Clipmap;
|
import iron.object.Clipmap;
|
||||||
|
#if lnx_vr
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Mat4;
|
||||||
|
import iron.math.Quat;
|
||||||
|
#end
|
||||||
|
|
||||||
class RenderPath {
|
class RenderPath {
|
||||||
|
|
||||||
public static var active: RenderPath;
|
public static var active: RenderPath;
|
||||||
|
#if lnx_vr
|
||||||
|
static var vrSimulateMode: Bool = false;
|
||||||
|
static var vrCameraOffsetSet:Bool = false;
|
||||||
|
static var vrCameraOffset:Vec4 = new Vec4();
|
||||||
|
static var wasVRPresenting:Bool = false;
|
||||||
|
public static var vrCalibrationPosition:Vec4 = null;
|
||||||
|
public static var vrCalibrationRotation:iron.math.Quat = null;
|
||||||
|
public static var vrCalibrationSaved:Bool = false;
|
||||||
|
|
||||||
|
public static var vrCenterCameraWorld:Mat4 = null;
|
||||||
|
|
||||||
|
static var vrOriginalSuperSample:Float = -1.0;
|
||||||
|
public static inline function isVRPresenting(): Bool {
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
return kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsPresenting();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function isVRSimulateMode(): Bool {
|
||||||
|
return vrSimulateMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: done remove safely
|
||||||
|
public static inline function debugLog(msg: String, once: Bool = true): Void {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
public var frameScissor = false;
|
public var frameScissor = false;
|
||||||
public var frameScissorX = 0;
|
public var frameScissorX = 0;
|
||||||
@ -43,9 +77,15 @@ class RenderPath {
|
|||||||
public var isProbe = false;
|
public var isProbe = false;
|
||||||
public var currentG: Graphics = null;
|
public var currentG: Graphics = null;
|
||||||
public var frameG: Graphics;
|
public var frameG: Graphics;
|
||||||
|
#if lnx_vr
|
||||||
|
var beginCalled = false;
|
||||||
|
var scissorSet = false;
|
||||||
|
var viewportScaled = false;
|
||||||
|
var renderToXRFramebuffer = false;
|
||||||
|
#end
|
||||||
public var drawOrder = DrawOrder.Distance;
|
public var drawOrder = DrawOrder.Distance;
|
||||||
public var paused = false;
|
public var paused = false;
|
||||||
public var ready(get, null): Bool;
|
public var ready(get, never): Bool;
|
||||||
function get_ready(): Bool { return loading == 0; }
|
function get_ready(): Bool { return loading == 0; }
|
||||||
public var commands: Void->Void = null;
|
public var commands: Void->Void = null;
|
||||||
public var setupDepthTexture: Void->Void = null;
|
public var setupDepthTexture: Void->Void = null;
|
||||||
@ -123,9 +163,93 @@ class RenderPath {
|
|||||||
public function renderFrame(g: Graphics) {
|
public function renderFrame(g: Graphics) {
|
||||||
if (!ready || paused || iron.App.w() == 0 || iron.App.h() == 0) return;
|
if (!ready || paused || iron.App.w() == 0 || iron.App.h() == 0) return;
|
||||||
|
|
||||||
if (lastW > 0 && (lastW != iron.App.w() || lastH != iron.App.h())) resize();
|
var appW = iron.App.w();
|
||||||
lastW = iron.App.w();
|
var appH = iron.App.h();
|
||||||
lastH = iron.App.h();
|
|
||||||
|
// use native XR framebuffer dimensions
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (kha.vr.VrInterface.instance != null) {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
var isPresenting = vr != null && vr.IsPresenting();
|
||||||
|
|
||||||
|
// save/restore camera position between modes
|
||||||
|
if (!wasVRPresenting && isPresenting) {
|
||||||
|
if (Scene.active != null && Scene.active.camera != null) {
|
||||||
|
if (vrCalibrationPosition == null) vrCalibrationPosition = new Vec4();
|
||||||
|
if (vrCalibrationRotation == null) vrCalibrationRotation = new Quat();
|
||||||
|
|
||||||
|
vrCalibrationPosition.setFrom(Scene.active.camera.transform.loc);
|
||||||
|
vrCalibrationRotation.setFrom(Scene.active.camera.transform.rot);
|
||||||
|
vrCalibrationSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save original super sampling for later
|
||||||
|
vrOriginalSuperSample = leenkx.renderpath.Inc.superSample;
|
||||||
|
|
||||||
|
// compositeToXR function handles blitting to VR framebuffer
|
||||||
|
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
var vrWidth = untyped xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
var vrHeight = untyped xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wasVRPresenting && !isPresenting) {
|
||||||
|
// reset VR frame time before anything else
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
iron.system.Time.vrFrameTime = -1.0;
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (vrCalibrationSaved && Scene.active != null && Scene.active.camera != null) {
|
||||||
|
Scene.active.camera.transform.loc.setFrom(vrCalibrationPosition);
|
||||||
|
Scene.active.camera.transform.rot.setFrom(vrCalibrationRotation);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
Scene.active.camera.buildProjection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore original super sampling from simulate mode
|
||||||
|
if (vrOriginalSuperSample >= 0.0) {
|
||||||
|
leenkx.renderpath.Inc.superSample = vrOriginalSuperSample;
|
||||||
|
|
||||||
|
for (rt in renderTargets) {
|
||||||
|
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
||||||
|
rt.raw.scale = vrOriginalSuperSample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
vrOriginalSuperSample = -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset offset for next session
|
||||||
|
vrCameraOffsetSet = false;
|
||||||
|
vrCameraOffset = null;
|
||||||
|
}
|
||||||
|
wasVRPresenting = isPresenting;
|
||||||
|
|
||||||
|
if (isPresenting) {
|
||||||
|
// TODO: re-investigate using super sampling to avoid pixelation in simulate mode while giving max quality in headset
|
||||||
|
if (vrOriginalSuperSample >= 0.0 && leenkx.renderpath.Inc.superSample != 4.0) {
|
||||||
|
leenkx.renderpath.Inc.superSample = 4.0;
|
||||||
|
for (rt in renderTargets) {
|
||||||
|
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
||||||
|
rt.raw.scale = 4.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
appW = xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
appH = xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (lastW > 0 && (lastW != appW || lastH != appH)) resize();
|
||||||
|
lastW = appW;
|
||||||
|
lastH = appH;
|
||||||
|
|
||||||
frameTime = Time.time() - lastFrameTime;
|
frameTime = Time.time() - lastFrameTime;
|
||||||
lastFrameTime = Time.time();
|
lastFrameTime = Time.time();
|
||||||
@ -191,7 +315,9 @@ class RenderPath {
|
|||||||
}
|
}
|
||||||
light = Scene.active.lights[0];
|
light = Scene.active.lights[0];
|
||||||
|
|
||||||
commands();
|
if (commands != null) {
|
||||||
|
commands();
|
||||||
|
}
|
||||||
|
|
||||||
if (!isProbe) frame++;
|
if (!isProbe) frame++;
|
||||||
}
|
}
|
||||||
@ -207,13 +333,13 @@ class RenderPath {
|
|||||||
begin(frameG, Scene.active.camera.currentFace);
|
begin(frameG, Scene.active.camera.currentFace);
|
||||||
}
|
}
|
||||||
else { // Screen, planar probe
|
else { // Screen, planar probe
|
||||||
currentW = iron.App.w();
|
currentW = kha.System.windowWidth();
|
||||||
currentH = iron.App.h();
|
currentH = kha.System.windowHeight();
|
||||||
if (frameScissor) setFrameScissor();
|
if (frameScissor) setFrameScissor();
|
||||||
begin(frameG);
|
begin(frameG);
|
||||||
if (!isProbe) {
|
if (!isProbe) {
|
||||||
setCurrentViewport(iron.App.w(), iron.App.h());
|
setCurrentViewport(kha.System.windowWidth(), kha.System.windowHeight());
|
||||||
setCurrentScissor(iron.App.w(), iron.App.h());
|
setCurrentScissor(kha.System.windowWidth(), kha.System.windowHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,16 +384,42 @@ class RenderPath {
|
|||||||
if (currentG != null) end();
|
if (currentG != null) end();
|
||||||
currentG = g;
|
currentG = g;
|
||||||
additionalTargets = additionalRenderTargets;
|
additionalTargets = additionalRenderTargets;
|
||||||
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
|
||||||
|
// we still bind but skip begin() when explicitly rendering to XR framebuffer (renderToXRFramebuffer flag)
|
||||||
|
#if lnx_vr
|
||||||
|
if (!renderToXRFramebuffer) {
|
||||||
|
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
||||||
|
beginCalled = true;
|
||||||
|
} else {
|
||||||
|
// XR framebuffer is already bound by VrInterface so we dont rebind
|
||||||
|
beginCalled = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
face >= 0 ? g.beginFace(face) : g.begin(additionalRenderTargets);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function end() {
|
inline function end() {
|
||||||
|
if (currentG == null) return;
|
||||||
if (scissorSet) {
|
if (scissorSet) {
|
||||||
currentG.disableScissor();
|
currentG.disableScissor();
|
||||||
scissorSet = false;
|
scissorSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
if (beginCalled) {
|
||||||
|
currentG.end();
|
||||||
|
beginCalled = false;
|
||||||
|
}
|
||||||
|
// persist for rendering both eyes
|
||||||
|
if (!isVRPresenting()) {
|
||||||
|
currentG = null;
|
||||||
|
additionalTargets = null;
|
||||||
|
}
|
||||||
|
#else
|
||||||
currentG.end();
|
currentG.end();
|
||||||
currentG = null;
|
currentG = null;
|
||||||
|
#end
|
||||||
bindParams = null;
|
bindParams = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,8 +493,8 @@ class RenderPath {
|
|||||||
if (a.data.sortingIndex != b.data.sortingIndex) {
|
if (a.data.sortingIndex != b.data.sortingIndex) {
|
||||||
return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1;
|
return a.data.sortingIndex > b.data.sortingIndex ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
return a.data.name >= b.data.name ? 1 : -1;
|
||||||
return a.data.name >= b.data.name ? 1 : -1; });
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function drawMeshes(context: String) {
|
public function drawMeshes(context: String) {
|
||||||
@ -521,44 +673,208 @@ class RenderPath {
|
|||||||
return Reflect.field(kha.Shaders, handle + "_comp");
|
return Reflect.field(kha.Shaders, handle + "_comp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
// blits to each eyes viewport in the XR framebuffer.
|
||||||
|
public function compositeToXR(sourceTarget: String) {
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
|
||||||
|
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || vr._glContext == null || vr.xrGLLayer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gl: js.html.webgl.WebGL2RenderingContext = cast vr._glContext;
|
||||||
|
var source = renderTargets.get(sourceTarget);
|
||||||
|
if (source == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var sourceFB: js.html.webgl.Framebuffer = untyped source.image.g4.renderTargetFrameBuffer;
|
||||||
|
if (sourceFB == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace('Framebuffer OK');
|
||||||
|
|
||||||
|
renderToXRFramebuffer = true;
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER, vr.xrGLLayer.framebuffer);
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER, sourceFB);
|
||||||
|
|
||||||
|
var readStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER);
|
||||||
|
var drawStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER);
|
||||||
|
if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE ||
|
||||||
|
drawStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var halfWidth = Std.int(source.image.width / 2);
|
||||||
|
var fullHeight = source.image.height;
|
||||||
|
|
||||||
|
if (vr._leftViewport != null) {
|
||||||
|
var vp = vr._leftViewport;
|
||||||
|
gl.blitFramebuffer(
|
||||||
|
0, 0, halfWidth, fullHeight,
|
||||||
|
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.LINEAR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (vr._rightViewport != null) {
|
||||||
|
var vp = vr._rightViewport;
|
||||||
|
gl.blitFramebuffer(
|
||||||
|
halfWidth, 0, source.image.width, fullHeight,
|
||||||
|
vp.x, vp.y, vp.x + vp.width, vp.y + vp.height,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.COLOR_BUFFER_BIT,
|
||||||
|
js.html.webgl.WebGL2RenderingContext.LINEAR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER, null);
|
||||||
|
renderToXRFramebuffer = false;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
#end
|
||||||
#if lnx_vr
|
#if lnx_vr
|
||||||
public function drawStereo(drawMeshes: Void->Void) {
|
public function drawStereo(drawMeshes: Void->Void) {
|
||||||
var vr = kha.vr.VrInterface.instance;
|
vrSimulateMode = false;
|
||||||
|
|
||||||
|
if (currentG == null && frameG != null) {
|
||||||
|
currentG = frameG;
|
||||||
|
}
|
||||||
|
|
||||||
var appw = iron.App.w();
|
var appw = iron.App.w();
|
||||||
var apph = iron.App.h();
|
var apph = iron.App.h();
|
||||||
var halfw = Std.int(appw / 2);
|
|
||||||
var g = currentG;
|
var g = currentG;
|
||||||
|
|
||||||
|
// get render target dimensions not App.w/h gbuffer is scaled in simulate mode with supersampling
|
||||||
|
|
||||||
if (vr != null && vr.IsPresenting()) {
|
var gbuffer0 = renderTargets.get("gbuffer0");
|
||||||
// Left eye
|
var actualWidth = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.width : appw;
|
||||||
Scene.active.camera.V.setFrom(Scene.active.camera.leftV);
|
var actualHeight = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.height : apph;
|
||||||
|
var actualHalfWidth = Std.int(actualWidth / 2);
|
||||||
|
|
||||||
|
var vrFBWidth = actualWidth;
|
||||||
|
var vrFBHeight = actualHeight;
|
||||||
|
var vrHalfWidth = actualHalfWidth;
|
||||||
|
var isVRPresenting = false;
|
||||||
|
vrSimulateMode = false;
|
||||||
|
|
||||||
|
var vr:Dynamic = null;
|
||||||
|
var vrExists = false;
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (kha.vr.VrInterface.instance != null) {
|
||||||
|
vr = kha.vr.VrInterface.instance;
|
||||||
|
vrExists = true;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (vrExists && vr != null && vr.IsPresenting()) {
|
||||||
|
vrSimulateMode = false;
|
||||||
|
isVRPresenting = true;
|
||||||
|
|
||||||
|
// get framebuffer dimensions from XR layer
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.xrGLLayer != null) {
|
||||||
|
vrFBWidth = untyped xrVr.xrGLLayer.framebufferWidth;
|
||||||
|
vrFBHeight = untyped xrVr.xrGLLayer.framebufferHeight;
|
||||||
|
vrHalfWidth = Std.int(vrFBWidth / 2);
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (Scene.active == null || Scene.active.camera == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||||
|
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||||
|
#end
|
||||||
|
|
||||||
|
// LEFT EYE
|
||||||
|
// HMD center for room scale position tracking
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||||
|
if (xrVr.currentViewerPose != null) {
|
||||||
|
var viewerTransform = untyped xrVr.currentViewerPose.transform;
|
||||||
|
if (viewerTransform != null && viewerTransform.position != null) {
|
||||||
|
// VR present calibration is used to position objects in world space not the camera
|
||||||
|
var pos = viewerTransform.position;
|
||||||
|
|
||||||
|
// camera follows headset directly in local floor space
|
||||||
|
Scene.active.camera.transform.loc.set(pos.x, pos.y, pos.z);
|
||||||
|
|
||||||
|
if (viewerTransform.orientation != null) {
|
||||||
|
Scene.active.camera.transform.rot.set(
|
||||||
|
viewerTransform.orientation.x,
|
||||||
|
viewerTransform.orientation.y,
|
||||||
|
viewerTransform.orientation.z,
|
||||||
|
viewerTransform.orientation.w
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Scene.active.camera.transform.buildMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iron.system.VRController.updatePoses();
|
||||||
|
#end
|
||||||
|
|
||||||
|
Scene.active.camera.V.self = vr.GetViewMatrix(0);
|
||||||
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
|
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
|
||||||
g.viewport(0, 0, halfw, apph);
|
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||||
|
Scene.active.camera.VP.multmat(Scene.active.camera.V);
|
||||||
|
Scene.active.camera.buildMatrix(); // update frustum for culling
|
||||||
|
|
||||||
|
var renderWidth = actualWidth;
|
||||||
|
var renderHeight = actualHeight;
|
||||||
|
var renderHalfWidth = actualHalfWidth;
|
||||||
|
|
||||||
|
// left half of render target
|
||||||
|
g.viewport(0, 0, renderHalfWidth, renderHeight);
|
||||||
|
g.scissor(0, 0, renderHalfWidth, renderHeight);
|
||||||
drawMeshes();
|
drawMeshes();
|
||||||
|
|
||||||
// Right eye
|
// RIGHT EYE
|
||||||
begin(g, additionalTargets);
|
Scene.active.camera.V.self = vr.GetViewMatrix(1);
|
||||||
Scene.active.camera.V.setFrom(Scene.active.camera.rightV);
|
|
||||||
Scene.active.camera.P.self = vr.GetProjectionMatrix(1);
|
Scene.active.camera.P.self = vr.GetProjectionMatrix(1);
|
||||||
g.viewport(halfw, 0, halfw, apph);
|
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||||
|
Scene.active.camera.VP.multmat(Scene.active.camera.V);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
|
||||||
|
// right half of render target
|
||||||
|
g.viewport(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||||
|
g.scissor(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||||
drawMeshes();
|
drawMeshes();
|
||||||
|
|
||||||
|
// restore for post-processing
|
||||||
|
g.disableScissor();
|
||||||
|
g.viewport(0, 0, renderWidth, renderHeight);
|
||||||
}
|
}
|
||||||
else { // Simulate
|
else { // Simulate
|
||||||
Scene.active.camera.buildProjection(halfw / apph);
|
vrSimulateMode = true;
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
|
||||||
// Left eye
|
#if (kha_webgl && lnx_vr)
|
||||||
g.viewport(0, 0, halfw, apph);
|
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||||
|
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||||
|
#end
|
||||||
|
|
||||||
|
Scene.active.camera.buildProjection(actualHalfWidth / actualHeight);
|
||||||
|
|
||||||
|
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
|
||||||
|
Scene.active.camera.buildMatrix();
|
||||||
|
g.viewport(0, 0, actualHalfWidth, actualHeight);
|
||||||
|
g.scissor(0, 0, actualHalfWidth, actualHeight);
|
||||||
drawMeshes();
|
drawMeshes();
|
||||||
|
|
||||||
// Right eye
|
|
||||||
begin(g, additionalTargets);
|
begin(g, additionalTargets);
|
||||||
Scene.active.camera.transform.move(Scene.active.camera.right(), 0.032);
|
Scene.active.camera.transform.move(Scene.active.camera.right(), ipd_offset * 2.0);
|
||||||
Scene.active.camera.buildMatrix();
|
Scene.active.camera.buildMatrix();
|
||||||
g.viewport(halfw, 0, halfw, apph);
|
g.viewport(actualHalfWidth, 0, actualHalfWidth, actualHeight);
|
||||||
|
g.scissor(actualHalfWidth, 0, actualHalfWidth, actualHeight);
|
||||||
drawMeshes();
|
drawMeshes();
|
||||||
|
|
||||||
Scene.active.camera.transform.move(Scene.active.camera.right(), -0.032);
|
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
|
||||||
Scene.active.camera.buildMatrix();
|
Scene.active.camera.buildMatrix();
|
||||||
|
g.disableScissor();
|
||||||
|
g.viewport(0, 0, actualWidth, actualHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -954,7 +954,12 @@ class Scene {
|
|||||||
static function createTraitClassInstance(traitName: String, args: Array<Dynamic>): Dynamic {
|
static function createTraitClassInstance(traitName: String, args: Array<Dynamic>): Dynamic {
|
||||||
var cname = Type.resolveClass(traitName);
|
var cname = Type.resolveClass(traitName);
|
||||||
if (cname == null) return null;
|
if (cname == null) return null;
|
||||||
return Type.createInstance(cname, args);
|
try {
|
||||||
|
return Type.createInstance(cname, args);
|
||||||
|
} catch() {
|
||||||
|
trace("Error creating trait: " + traitName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadEmbeddedData(datas: Array<String>, done: Void->Void) {
|
function loadEmbeddedData(datas: Array<String>, done: Void->Void) {
|
||||||
|
|||||||
@ -37,7 +37,9 @@ class Armature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getAction(name: String): TAction {
|
public function getAction(name: String): TAction {
|
||||||
for (a in actions) if (a.name == name) return a;
|
for (a in actions) {
|
||||||
|
if (a.name == name) return a;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -141,6 +141,7 @@ class Animation {
|
|||||||
sampler.cacheSet = false;
|
sampler.cacheSet = false;
|
||||||
sampler.trackEnd = false;
|
sampler.trackEnd = false;
|
||||||
|
|
||||||
|
if (anim == null || anim.tracks == null || anim.tracks.length == 0) return;
|
||||||
var track = anim.tracks[0];
|
var track = anim.tracks[0];
|
||||||
|
|
||||||
if (frameIndex == -1) {
|
if (frameIndex == -1) {
|
||||||
@ -442,7 +443,12 @@ class ActionSampler {
|
|||||||
*/
|
*/
|
||||||
public inline function setObjectAction(actionData: TObj) {
|
public inline function setObjectAction(actionData: TObj) {
|
||||||
this.actionData = [actionData];
|
this.actionData = [actionData];
|
||||||
this.totalFrames = actionData.anim.tracks[0].frames.length;
|
if (actionData != null && actionData.anim != null && actionData.anim.tracks != null && actionData.anim.tracks.length > 0) {
|
||||||
|
this.totalFrames = actionData.anim.tracks[0].frames.length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.totalFrames = 0;
|
||||||
|
}
|
||||||
actionDataInit = true;
|
actionDataInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,9 +108,11 @@ class BoneAnimation extends Animation {
|
|||||||
object.transform.rot.set(0, 0, 0, 1);
|
object.transform.rot.set(0, 0, 0, 1);
|
||||||
object.transform.buildMatrix();
|
object.transform.buildMatrix();
|
||||||
|
|
||||||
var refs = mo.parent.raw.bone_actions;
|
if (mo.parent != null && mo.parent.raw != null && mo.parent.raw.bone_actions != null) {
|
||||||
if (refs != null && refs.length > 0) {
|
var refs = mo.parent.raw.bone_actions;
|
||||||
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
|
if (refs.length > 0) {
|
||||||
|
Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
|
if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
|
||||||
@ -183,8 +185,10 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setAction(action: String) {
|
function setAction(action: String) {
|
||||||
|
if (armature == null) return;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
var a = armature.getAction(action);
|
var a = armature.getAction(action);
|
||||||
|
if (a == null) return;
|
||||||
skeletonBones = a.bones;
|
skeletonBones = a.bones;
|
||||||
skeletonMats = a.mats;
|
skeletonMats = a.mats;
|
||||||
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
|
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
|
||||||
@ -193,8 +197,11 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAction(action: String): Array<TObj> {
|
function getAction(action: String): Array<TObj> {
|
||||||
|
if (armature == null) return null;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
return armature.getAction(action).bones;
|
var a = armature.getAction(action);
|
||||||
|
if (a == null) return null;
|
||||||
|
return a.bones;
|
||||||
}
|
}
|
||||||
|
|
||||||
function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
|
function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
|
||||||
@ -225,9 +232,9 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
|
override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
|
||||||
super.play(action, onComplete, blendTime, speed, loop);
|
|
||||||
if (action != "") {
|
if (action != "") {
|
||||||
setAction(action);
|
setAction(action);
|
||||||
|
super.play(action, onComplete, blendTime, speed, loop);
|
||||||
var tempAnimParam = new ActionSampler(action);
|
var tempAnimParam = new ActionSampler(action);
|
||||||
registerAction("tempAction", tempAnimParam);
|
registerAction("tempAction", tempAnimParam);
|
||||||
updateAnimation = function(mats){
|
updateAnimation = function(mats){
|
||||||
@ -239,6 +246,10 @@ class BoneAnimation extends Animation {
|
|||||||
override public function update(delta: FastFloat) {
|
override public function update(delta: FastFloat) {
|
||||||
this.delta = delta;
|
this.delta = delta;
|
||||||
if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
|
if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
|
||||||
|
|
||||||
|
// TODO: double check skip culling for skinned meshes if they need animation updates for bounds
|
||||||
|
// if (object != null && !object.visible) return;
|
||||||
|
|
||||||
if (object != null && (!object.visible || object.culled)) return;
|
if (object != null && (!object.visible || object.culled)) return;
|
||||||
if (skeletonBones == null || skeletonBones.length == 0) return;
|
if (skeletonBones == null || skeletonBones.length == 0) return;
|
||||||
|
|
||||||
@ -248,7 +259,6 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
super.update(delta);
|
super.update(delta);
|
||||||
if(updateAnimation != null) {
|
if(updateAnimation != null) {
|
||||||
|
|
||||||
updateAnimation(skeletonMats);
|
updateAnimation(skeletonMats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,6 +411,7 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bones = sampler.getBoneAction();
|
var bones = sampler.getBoneAction();
|
||||||
|
if (bones == null) return;
|
||||||
for(b in bones){
|
for(b in bones){
|
||||||
if (b.anim != null) {
|
if (b.anim != null) {
|
||||||
updateTrack(b.anim, sampler);
|
updateTrack(b.anim, sampler);
|
||||||
@ -410,13 +421,14 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
|
public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
|
||||||
|
|
||||||
if(! sampler.actionDataInit) {
|
if(! sampler.actionDataInit) {
|
||||||
var bones = getAction(sampler.action);
|
var bones = getAction(sampler.action);
|
||||||
sampler.setBoneAction(bones);
|
sampler.setBoneAction(bones);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bones = sampler.getBoneAction();
|
var bones = sampler.getBoneAction();
|
||||||
|
if (bones == null) return;
|
||||||
|
|
||||||
actionMats[skeletonBones.length].setIdentity();
|
actionMats[skeletonBones.length].setIdentity();
|
||||||
var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
|
var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
|
||||||
for (i in 0...bones.length) {
|
for (i in 0...bones.length) {
|
||||||
@ -427,7 +439,6 @@ class BoneAnimation extends Animation {
|
|||||||
updateAnimSampled(bones[i].anim, actionMats[i], sampler);
|
updateAnimSampled(bones[i].anim, actionMats[i], sampler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
|
function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
|
||||||
@ -588,6 +599,9 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
public override function getTotalFrames(sampler: ActionSampler): Int {
|
public override function getTotalFrames(sampler: ActionSampler): Int {
|
||||||
var bones = getAction(sampler.action);
|
var bones = getAction(sampler.action);
|
||||||
|
if (bones == null){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
var track = bones[0].anim.tracks[0];
|
var track = bones[0].anim.tracks[0];
|
||||||
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
||||||
}
|
}
|
||||||
@ -1048,9 +1062,9 @@ class BoneAnimation extends Animation {
|
|||||||
var rootLen = root.bone_length * rootMat.getScale().x;
|
var rootLen = root.bone_length * rootMat.getScale().x;
|
||||||
|
|
||||||
// Get distance form root to goal
|
// Get distance form root to goal
|
||||||
var goalLen = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
|
var goalLen: FastFloat = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
|
||||||
|
|
||||||
var totalLength = effectorLen + rootLen;
|
var totalLength: FastFloat = effectorLen + rootLen;
|
||||||
|
|
||||||
// Get tip location of effector bone
|
// Get tip location of effector bone
|
||||||
var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
|
var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
|
||||||
@ -1070,7 +1084,7 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
// Get unit vector of effector bone
|
// Get unit vector of effector bone
|
||||||
var vectorEffector = new Vec4().setFrom(effectorMat.look()).normalize();
|
var vectorEffector = new Vec4().setFrom(effectorMat.look()).normalize();
|
||||||
|
|
||||||
// Get dot product of vectors
|
// Get dot product of vectors
|
||||||
var dot = new Vec4().setFrom(vectorRootEffector).dot(vectorRoot);
|
var dot = new Vec4().setFrom(vectorRootEffector).dot(vectorRoot);
|
||||||
// Calmp between -1 and 1
|
// Calmp between -1 and 1
|
||||||
|
|||||||
@ -42,9 +42,10 @@ class CameraObject extends Object {
|
|||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
#if lnx_vr
|
// dont just auto initialize VR button - headset trait controls VR
|
||||||
iron.system.VR.initButton();
|
// #if lnx_vr
|
||||||
#end
|
// iron.system.VR.initButton();
|
||||||
|
// #end
|
||||||
|
|
||||||
buildProjection();
|
buildProjection();
|
||||||
|
|
||||||
@ -85,7 +86,14 @@ class CameraObject extends Object {
|
|||||||
projectionJitter();
|
projectionJitter();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
// matrices are set by VR system so avoid rebuilding transforms unless its in preview/not presenting
|
||||||
|
#if (kha_webgl && lnx_vr)
|
||||||
|
if (@:privateAccess !RenderPath.isVRPresenting()) {
|
||||||
|
buildMatrix();
|
||||||
|
}
|
||||||
|
#else
|
||||||
buildMatrix();
|
buildMatrix();
|
||||||
|
#end
|
||||||
|
|
||||||
RenderPath.active.renderFrame(g);
|
RenderPath.active.renderFrame(g);
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,9 @@ class LightObject extends Object {
|
|||||||
public static var clustersData: kha.Image = null;
|
public static var clustersData: kha.Image = null;
|
||||||
static var lpos = new Vec4();
|
static var lpos = new Vec4();
|
||||||
public static var LWVPMatrixArray: Float32Array = null;
|
public static var LWVPMatrixArray: Float32Array = null;
|
||||||
|
#if lnx_vr
|
||||||
|
static var originalLightPositions: Float32Array = null;
|
||||||
|
#end
|
||||||
#end // lnx_clusters
|
#end // lnx_clusters
|
||||||
|
|
||||||
public var V: Mat4 = Mat4.identity();
|
public var V: Mat4 = Mat4.identity();
|
||||||
@ -519,7 +522,7 @@ class LightObject extends Object {
|
|||||||
updateLightsArray(); // TODO: only update on light change
|
updateLightsArray(); // TODO: only update on light change
|
||||||
}
|
}
|
||||||
|
|
||||||
static function updateLightsArray() {
|
public static function updateLightsArray() {
|
||||||
if (lightsArray == null) { // vec4x3 - 1: pos, a, color, b, 2: dir, c
|
if (lightsArray == null) { // vec4x3 - 1: pos, a, color, b, 2: dir, c
|
||||||
lightsArray = new Float32Array(maxLights * 4 * 3);
|
lightsArray = new Float32Array(maxLights * 4 * 3);
|
||||||
#if lnx_spot
|
#if lnx_spot
|
||||||
@ -578,6 +581,49 @@ class LightObject extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VR deferred stereo we save original light positions before adjusting for per-eye rendering
|
||||||
|
#if lnx_vr
|
||||||
|
public static function saveOriginalLightPositions() {
|
||||||
|
if (lightsArray == null) return;
|
||||||
|
|
||||||
|
if (originalLightPositions == null) {
|
||||||
|
originalLightPositions = new Float32Array(lightsArray.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0...lightsArray.length) {
|
||||||
|
originalLightPositions[i] = lightsArray[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// negative for left eye, positive for right eye
|
||||||
|
public static function adjustLightPositionsForVREye(offsetX: Float, rightVec: Vec4) {
|
||||||
|
if (lightsArray == null) return;
|
||||||
|
|
||||||
|
var lights = Scene.active.lights;
|
||||||
|
var n = lights.length > maxLights ? maxLights : lights.length;
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (l in lights) {
|
||||||
|
if (discardLightCulled(l)) continue;
|
||||||
|
if (i >= n) break;
|
||||||
|
|
||||||
|
lightsArray[i * 12 ] = originalLightPositions[i * 12 ] + rightVec.x * offsetX;
|
||||||
|
lightsArray[i * 12 + 1] = originalLightPositions[i * 12 + 1] + rightVec.y * offsetX;
|
||||||
|
lightsArray[i * 12 + 2] = originalLightPositions[i * 12 + 2] + rightVec.z * offsetX;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function restoreOriginalLightPositions() {
|
||||||
|
if (lightsArray == null || originalLightPositions == null) return;
|
||||||
|
|
||||||
|
for (i in 0...lightsArray.length) {
|
||||||
|
lightsArray[i] = originalLightPositions[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
public static function updateLWVPMatrixArray(object: Object, type: String) {
|
public static function updateLWVPMatrixArray(object: Object, type: String) {
|
||||||
if (LWVPMatrixArray == null) {
|
if (LWVPMatrixArray == null) {
|
||||||
LWVPMatrixArray = new Float32Array(maxLightsCluster * 16);
|
LWVPMatrixArray = new Float32Array(maxLightsCluster * 16);
|
||||||
@ -629,8 +675,8 @@ class LightObject extends Object {
|
|||||||
LWVPMatrixArray[i * 16 + 13] = m._31;
|
LWVPMatrixArray[i * 16 + 13] = m._31;
|
||||||
LWVPMatrixArray[i * 16 + 14] = m._32;
|
LWVPMatrixArray[i * 16 + 14] = m._32;
|
||||||
LWVPMatrixArray[i * 16 + 15] = m._33;
|
LWVPMatrixArray[i * 16 + 15] = m._33;
|
||||||
|
i++; // only increment in light type
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
return LWVPMatrixArray;
|
return LWVPMatrixArray;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,13 @@ class MorphTarget {
|
|||||||
public var morphDataPos: Image;
|
public var morphDataPos: Image;
|
||||||
public var morphDataNor: Image;
|
public var morphDataNor: Image;
|
||||||
public var morphMap: Map<String, Int> = null;
|
public var morphMap: Map<String, Int> = null;
|
||||||
|
|
||||||
|
public var isDirty: Bool = true;
|
||||||
|
var previousWeights: Float32Array;
|
||||||
|
var changeThreshold: FastFloat = 0.001; // skip smaller
|
||||||
|
var pendingUpdates: Map<Int, Float> = null;
|
||||||
|
var batchUpdateEnabled: Bool = true;
|
||||||
|
var lastFlushFrame: Int = 0;
|
||||||
|
|
||||||
public function new(data: TMorphTarget) {
|
public function new(data: TMorphTarget) {
|
||||||
initWeights(data.morph_target_defaults);
|
initWeights(data.morph_target_defaults);
|
||||||
@ -42,6 +49,14 @@ class MorphTarget {
|
|||||||
morphMap.set(name, i);
|
morphMap.set(name, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previousWeights = new Float32Array(morphWeights.length);
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
previousWeights.set(i, morphWeights.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// batch system
|
||||||
|
pendingUpdates = new Map<Int, Float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function initWeights(defaults: Float32Array) {
|
inline function initWeights(defaults: Float32Array) {
|
||||||
@ -54,9 +69,96 @@ class MorphTarget {
|
|||||||
public function setMorphValue(name: String, value: Float) {
|
public function setMorphValue(name: String, value: Float) {
|
||||||
var i = morphMap.get(name);
|
var i = morphMap.get(name);
|
||||||
if (i != null) {
|
if (i != null) {
|
||||||
morphWeights.set(i, value);
|
if (batchUpdateEnabled) {
|
||||||
|
pendingUpdates.set(i, value);
|
||||||
|
} else {
|
||||||
|
setMorphValueDirect(i, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// faster indexed access
|
||||||
|
public inline function setMorphValueDirect(index: Int, value: Float) {
|
||||||
|
var current = morphWeights.get(index);
|
||||||
|
|
||||||
|
// allow explicit zero values to reset
|
||||||
|
if (value == 0.0 && current != 0.0) {
|
||||||
|
morphWeights.set(index, value);
|
||||||
|
isDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = value - current;
|
||||||
|
|
||||||
|
if (delta < -changeThreshold || delta > changeThreshold) {
|
||||||
|
morphWeights.set(index, value);
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush pending batch
|
||||||
|
public function flushBatchedUpdates() {
|
||||||
|
if (pendingUpdates.keys().hasNext()) {
|
||||||
|
var anyChanged = false;
|
||||||
|
var hasZeros = false;
|
||||||
|
|
||||||
|
for (index in pendingUpdates.keys()) {
|
||||||
|
var value = pendingUpdates.get(index);
|
||||||
|
if (value == null) continue;
|
||||||
|
|
||||||
|
if (value == 0.0) hasZeros = true;
|
||||||
|
|
||||||
|
var current = morphWeights.get(index);
|
||||||
|
var delta = value - current;
|
||||||
|
|
||||||
|
if (value == 0.0 && current != 0.0) {
|
||||||
|
try{
|
||||||
|
morphWeights.set(index, cast value);
|
||||||
|
}catch(e){
|
||||||
|
trace("ERROR: " + e);
|
||||||
|
}
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
else if (delta < -changeThreshold || delta > changeThreshold) {
|
||||||
|
morphWeights.set(index, cast value);
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingUpdates.clear();
|
||||||
|
|
||||||
|
if (anyChanged || hasZeros) {
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function markClean() {
|
||||||
|
isDirty = false;
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
previousWeights.set(i, morphWeights.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function markDirty() {
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle batch mode
|
||||||
|
public inline function setBatchMode(enabled: Bool) {
|
||||||
|
if (!enabled && batchUpdateEnabled) {
|
||||||
|
flushBatchedUpdates();
|
||||||
|
}
|
||||||
|
batchUpdateEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetAllWeights() {
|
||||||
|
for (i in 0...morphWeights.length) {
|
||||||
|
morphWeights.set(i, 0.0);
|
||||||
|
}
|
||||||
|
pendingUpdates.clear();
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -210,8 +210,12 @@ class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
public function getBoneAnimation(armatureUid): BoneAnimation {
|
public function getBoneAnimation(armatureUid: Int): BoneAnimation {
|
||||||
for (a in Scene.active.animations) if (a.armature != null && a.armature.uid == armatureUid) return cast a;
|
for (a in Scene.active.animations) {
|
||||||
|
if (a.armature != null && a.armature.uid == armatureUid) {
|
||||||
|
return cast a;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -97,7 +97,9 @@ class ObjectAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override function getTotalFrames(sampler: ActionSampler): Int {
|
public override function getTotalFrames(sampler: ActionSampler): Int {
|
||||||
var track = getAction(sampler.action).anim.tracks[0];
|
var action = getAction(sampler.action);
|
||||||
|
if (action == null || action.anim == null || action.anim.tracks == null || action.anim.tracks.length == 0) return 0;
|
||||||
|
var track = action.anim.tracks[0];
|
||||||
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,16 +7,20 @@ import kha.graphics4.TextureFilter;
|
|||||||
import kha.graphics4.MipMapFilter;
|
import kha.graphics4.MipMapFilter;
|
||||||
import kha.arrays.Float32Array;
|
import kha.arrays.Float32Array;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Mat4;
|
||||||
import iron.math.Quat;
|
import iron.math.Quat;
|
||||||
import iron.math.Mat3;
|
import iron.math.Mat3;
|
||||||
import iron.math.Mat4;
|
|
||||||
import iron.data.WorldData;
|
|
||||||
import iron.data.MaterialData;
|
import iron.data.MaterialData;
|
||||||
import iron.data.ShaderData;
|
import iron.data.ShaderData;
|
||||||
import iron.data.SceneFormat;
|
import iron.data.WorldData;
|
||||||
|
import iron.data.SceneFormat.TShaderConstant;
|
||||||
|
import iron.data.SceneFormat.TBindConstant;
|
||||||
|
import iron.object.Transform;
|
||||||
|
import iron.object.LightObject;
|
||||||
|
import iron.Scene;
|
||||||
|
import iron.RenderPath;
|
||||||
import iron.system.Input;
|
import iron.system.Input;
|
||||||
import iron.system.Time;
|
import iron.system.Time;
|
||||||
import iron.RenderPath;
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
// Structure for setting shader uniforms
|
// Structure for setting shader uniforms
|
||||||
@ -38,6 +42,7 @@ class Uniforms {
|
|||||||
public static var helpMat = Mat4.identity();
|
public static var helpMat = Mat4.identity();
|
||||||
public static var helpMat2 = Mat4.identity();
|
public static var helpMat2 = Mat4.identity();
|
||||||
public static var helpMat3 = Mat3.identity();
|
public static var helpMat3 = Mat3.identity();
|
||||||
|
public static var helpMat4 = Mat4.identity();
|
||||||
public static var helpVec = new Vec4();
|
public static var helpVec = new Vec4();
|
||||||
public static var helpVec2 = new Vec4();
|
public static var helpVec2 = new Vec4();
|
||||||
public static var helpQuat = new Quat(); // Keep at identity
|
public static var helpQuat = new Quat(); // Keep at identity
|
||||||
@ -47,6 +52,10 @@ class Uniforms {
|
|||||||
public static var externalVec4Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec4Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
public static var externalVec3Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec3Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
public static var externalVec2Links: Array<Object->MaterialData->String->Vec4> = null;
|
public static var externalVec2Links: Array<Object->MaterialData->String->Vec4> = null;
|
||||||
|
|
||||||
|
public static var eyeLeftCallCount = 0;
|
||||||
|
public static var lastFrameChecked = -1;
|
||||||
|
|
||||||
public static var externalFloatLinks: Array<Object->MaterialData->String->Null<kha.FastFloat>> = null;
|
public static var externalFloatLinks: Array<Object->MaterialData->String->Null<kha.FastFloat>> = null;
|
||||||
public static var externalFloatsLinks: Array<Object->MaterialData->String->Float32Array> = null;
|
public static var externalFloatsLinks: Array<Object->MaterialData->String->Float32Array> = null;
|
||||||
public static var externalIntLinks: Array<Object->MaterialData->String->Null<Int>> = null;
|
public static var externalIntLinks: Array<Object->MaterialData->String->Null<Int>> = null;
|
||||||
@ -59,6 +68,10 @@ class Uniforms {
|
|||||||
public static var defaultFilter = TextureFilter.LinearFilter;
|
public static var defaultFilter = TextureFilter.LinearFilter;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
#if lnx_morph_target
|
||||||
|
public static var forceUploadMorphWeights: Bool = false;
|
||||||
|
#end
|
||||||
|
|
||||||
public static function setContextConstants(g: Graphics, context: ShaderContext, bindParams: Array<String>) {
|
public static function setContextConstants(g: Graphics, context: ShaderContext, bindParams: Array<String>) {
|
||||||
if (context.raw.constants != null) {
|
if (context.raw.constants != null) {
|
||||||
for (i in 0...context.raw.constants.length) {
|
for (i in 0...context.raw.constants.length) {
|
||||||
@ -181,11 +194,15 @@ class Uniforms {
|
|||||||
// Multiple voxel volumes, always set params
|
// Multiple voxel volumes, always set params
|
||||||
g.setImageTexture(context.textureUnits[j], rt.image); // image2D/3D
|
g.setImageTexture(context.textureUnits[j], rt.image); // image2D/3D
|
||||||
if (rt.raw.name.startsWith("voxels_")) {
|
if (rt.raw.name.startsWith("voxels_")) {
|
||||||
g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
|
g.setTextureParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.LinearMipFilter);
|
||||||
|
}
|
||||||
|
else if (rt.raw.name.startsWith("voxelsSDF"))
|
||||||
|
{
|
||||||
|
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.PointFilter, TextureFilter.PointFilter, MipMapFilter.NoMipFilter);
|
||||||
}
|
}
|
||||||
else if (rt.raw.name.startsWith("voxels"))
|
else if (rt.raw.name.startsWith("voxels"))
|
||||||
{
|
{
|
||||||
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.NoMipFilter);
|
g.setTexture3DParameters(context.textureUnits[j], TextureAddressing.Clamp, TextureAddressing.Clamp, TextureAddressing.Clamp, TextureFilter.LinearFilter, TextureFilter.LinearFilter, MipMapFilter.PointMipFilter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -286,6 +303,89 @@ class Uniforms {
|
|||||||
helpMat.getInverse(helpMat);
|
helpMat.getInverse(helpMat);
|
||||||
m = helpMat;
|
m = helpMat;
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_inverseViewProjectionMatrixLeft": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftView = vr.GetViewMatrix(0);
|
||||||
|
var leftProj = vr.GetProjectionMatrix(0);
|
||||||
|
helpMat._00 = leftView._00; helpMat._01 = leftView._01; helpMat._02 = leftView._02; helpMat._03 = leftView._03;
|
||||||
|
helpMat._10 = leftView._10; helpMat._11 = leftView._11; helpMat._12 = leftView._12; helpMat._13 = leftView._13;
|
||||||
|
helpMat._20 = leftView._20; helpMat._21 = leftView._21; helpMat._22 = leftView._22; helpMat._23 = leftView._23;
|
||||||
|
helpMat._30 = leftView._30; helpMat._31 = leftView._31; helpMat._32 = leftView._32; helpMat._33 = leftView._33;
|
||||||
|
helpMat2._00 = leftProj._00; helpMat2._01 = leftProj._01; helpMat2._02 = leftProj._02; helpMat2._03 = leftProj._03;
|
||||||
|
helpMat2._10 = leftProj._10; helpMat2._11 = leftProj._11; helpMat2._12 = leftProj._12; helpMat2._13 = leftProj._13;
|
||||||
|
helpMat2._20 = leftProj._20; helpMat2._21 = leftProj._21; helpMat2._22 = leftProj._22; helpMat2._23 = leftProj._23;
|
||||||
|
helpMat2._30 = leftProj._30; helpMat2._31 = leftProj._31; helpMat2._32 = leftProj._32; helpMat2._33 = leftProj._33;
|
||||||
|
helpMat.multmat(helpMat2);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0; // Match eye offset
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
|
||||||
|
var eyeLeftX = camera.transform.worldx() - rightVec.x * ipd_offset;
|
||||||
|
var eyeLeftY = camera.transform.worldy() - rightVec.y * ipd_offset;
|
||||||
|
var eyeLeftZ = camera.transform.worldz() - rightVec.z * ipd_offset;
|
||||||
|
|
||||||
|
helpMat.setFrom(camera.transform.world);
|
||||||
|
helpMat._30 = eyeLeftX;
|
||||||
|
helpMat._31 = eyeLeftY;
|
||||||
|
helpMat._32 = eyeLeftZ;
|
||||||
|
helpMat.getInverse(helpMat); // Now it's a view matrix
|
||||||
|
|
||||||
|
helpMat.multmat(camera.P);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
} else {
|
||||||
|
helpMat.setFrom(camera.V);
|
||||||
|
helpMat.multmat(camera.P);
|
||||||
|
helpMat.getInverse(helpMat);
|
||||||
|
}
|
||||||
|
m = helpMat;
|
||||||
|
}
|
||||||
|
case "_inverseViewProjectionMatrixRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightView = vr.GetViewMatrix(1);
|
||||||
|
var rightProj = vr.GetProjectionMatrix(1);
|
||||||
|
// kha.math.FastMatrix4 to iron.math.Mat4
|
||||||
|
helpMat2._00 = rightView._00; helpMat2._01 = rightView._01; helpMat2._02 = rightView._02; helpMat2._03 = rightView._03;
|
||||||
|
helpMat2._10 = rightView._10; helpMat2._11 = rightView._11; helpMat2._12 = rightView._12; helpMat2._13 = rightView._13;
|
||||||
|
helpMat2._20 = rightView._20; helpMat2._21 = rightView._21; helpMat2._22 = rightView._22; helpMat2._23 = rightView._23;
|
||||||
|
helpMat2._30 = rightView._30; helpMat2._31 = rightView._31; helpMat2._32 = rightView._32; helpMat2._33 = rightView._33;
|
||||||
|
helpMat4._00 = rightProj._00; helpMat4._01 = rightProj._01; helpMat4._02 = rightProj._02; helpMat4._03 = rightProj._03;
|
||||||
|
helpMat4._10 = rightProj._10; helpMat4._11 = rightProj._11; helpMat4._12 = rightProj._12; helpMat4._13 = rightProj._13;
|
||||||
|
helpMat4._20 = rightProj._20; helpMat4._21 = rightProj._21; helpMat4._22 = rightProj._22; helpMat4._23 = rightProj._23;
|
||||||
|
helpMat4._30 = rightProj._30; helpMat4._31 = rightProj._31; helpMat4._32 = rightProj._32; helpMat4._33 = rightProj._33;
|
||||||
|
helpMat2.multmat(helpMat4);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
|
||||||
|
// calculate right eye position in world space
|
||||||
|
var eyeRightX = camera.transform.worldx() + rightVec.x * ipd_offset;
|
||||||
|
var eyeRightY = camera.transform.worldy() + rightVec.y * ipd_offset;
|
||||||
|
var eyeRightZ = camera.transform.worldz() + rightVec.z * ipd_offset;
|
||||||
|
|
||||||
|
helpMat2.setFrom(camera.transform.world);
|
||||||
|
helpMat2._30 = eyeRightX;
|
||||||
|
helpMat2._31 = eyeRightY;
|
||||||
|
helpMat2._32 = eyeRightZ;
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
|
||||||
|
helpMat2.multmat(camera.P);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
} else {
|
||||||
|
// fallback to center camera
|
||||||
|
helpMat2.setFrom(camera.V);
|
||||||
|
helpMat2.multmat(camera.P);
|
||||||
|
helpMat2.getInverse(helpMat2);
|
||||||
|
m = helpMat2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
case "_viewProjectionMatrix": {
|
case "_viewProjectionMatrix": {
|
||||||
#if lnx_centerworld
|
#if lnx_centerworld
|
||||||
m = vmat(camera.V);
|
m = vmat(camera.V);
|
||||||
@ -398,6 +498,28 @@ class Uniforms {
|
|||||||
v = helpVec;
|
v = helpVec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_pointPositionLeft": {
|
||||||
|
var point = RenderPath.active.point;
|
||||||
|
if (point != null) {
|
||||||
|
var lightWorldX = point.transform.worldx();
|
||||||
|
var lightWorldY = point.transform.worldy();
|
||||||
|
var lightWorldZ = point.transform.worldz();
|
||||||
|
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "_pointPositionRight": {
|
||||||
|
var point = RenderPath.active.point;
|
||||||
|
if (point != null) {
|
||||||
|
var lightWorldX = point.transform.worldx();
|
||||||
|
var lightWorldY = point.transform.worldy();
|
||||||
|
var lightWorldZ = point.transform.worldz();
|
||||||
|
helpVec.set(lightWorldX, lightWorldY, lightWorldZ);
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
#if lnx_spot
|
#if lnx_spot
|
||||||
case "_spotDirection": {
|
case "_spotDirection": {
|
||||||
var point = RenderPath.active.point;
|
var point = RenderPath.active.point;
|
||||||
@ -484,6 +606,84 @@ class Uniforms {
|
|||||||
helpVec = camera.rightWorld().normalize();
|
helpVec = camera.rightWorld().normalize();
|
||||||
v = helpVec;
|
v = helpVec;
|
||||||
}
|
}
|
||||||
|
#if lnx_vr
|
||||||
|
case "_eyeLeft": {
|
||||||
|
var currentFrame = iron.RenderPath.active.frame;
|
||||||
|
if (currentFrame != lastFrameChecked) {
|
||||||
|
eyeLeftCallCount = 0;
|
||||||
|
lastFrameChecked = currentFrame;
|
||||||
|
}
|
||||||
|
eyeLeftCallCount++;
|
||||||
|
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftViewMatrix = vr.GetViewMatrix(0);
|
||||||
|
var invLeft = leftViewMatrix.inverse();
|
||||||
|
helpVec.set(invLeft._30, invLeft._31, invLeft._32);
|
||||||
|
// trace("eyeLeft: " + helpVec.x + ", " + helpVec.y + ", " + helpVec.z);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
var centerX = camera.transform.worldx();
|
||||||
|
var centerY = camera.transform.worldy();
|
||||||
|
var centerZ = camera.transform.worldz();
|
||||||
|
helpVec.set(
|
||||||
|
centerX - rightVec.x * ipd_offset,
|
||||||
|
centerY - rightVec.y * ipd_offset,
|
||||||
|
centerZ - rightVec.z * ipd_offset
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightViewMatrix = vr.GetViewMatrix(1);
|
||||||
|
var invRight = rightViewMatrix.inverse();
|
||||||
|
helpVec.set(invRight._30, invRight._31, invRight._32);
|
||||||
|
} else if (iron.RenderPath.isVRSimulateMode()) {
|
||||||
|
var ipd_offset = 0.032 * 35.0;
|
||||||
|
var rightVec = camera.rightWorld();
|
||||||
|
var centerX = camera.transform.worldx();
|
||||||
|
var centerY = camera.transform.worldy();
|
||||||
|
var centerZ = camera.transform.worldz();
|
||||||
|
helpVec.set(
|
||||||
|
centerX + rightVec.x * ipd_offset,
|
||||||
|
centerY + rightVec.y * ipd_offset,
|
||||||
|
centerZ + rightVec.z * ipd_offset
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
helpVec.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz());
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeLookLeft": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var leftViewMatrix = vr.GetViewMatrix(0);
|
||||||
|
var invLeft = leftViewMatrix.inverse();
|
||||||
|
helpVec.set(-invLeft._20, -invLeft._21, -invLeft._22);
|
||||||
|
helpVec.normalize();
|
||||||
|
} else {
|
||||||
|
helpVec = camera.lookWorld().normalize();
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
case "_eyeLookRight": {
|
||||||
|
var vr = kha.vr.VrInterface.instance;
|
||||||
|
if (vr != null && vr.IsPresenting()) {
|
||||||
|
var rightViewMatrix = vr.GetViewMatrix(1);
|
||||||
|
var invRight = rightViewMatrix.inverse();
|
||||||
|
helpVec.set(-invRight._20, -invRight._21, -invRight._22);
|
||||||
|
helpVec.normalize();
|
||||||
|
} else {
|
||||||
|
helpVec = camera.lookWorld().normalize();
|
||||||
|
}
|
||||||
|
v = helpVec;
|
||||||
|
}
|
||||||
|
#end
|
||||||
case "_backgroundCol": {
|
case "_backgroundCol": {
|
||||||
if (camera.data.raw.clear_color != null) helpVec.set(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]);
|
if (camera.data.raw.clear_color != null) helpVec.set(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]);
|
||||||
v = helpVec;
|
v = helpVec;
|
||||||
@ -1161,7 +1361,19 @@ class Uniforms {
|
|||||||
#end // lnx_clusters
|
#end // lnx_clusters
|
||||||
#if lnx_morph_target
|
#if lnx_morph_target
|
||||||
case "_morphWeights": {
|
case "_morphWeights": {
|
||||||
fa = cast(object, MeshObject).morphTarget.morphWeights;
|
var morphTarget = cast(object, MeshObject).morphTarget;
|
||||||
|
morphTarget.flushBatchedUpdates();
|
||||||
|
if (forceUploadMorphWeights) {
|
||||||
|
fa = morphTarget.morphWeights;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (morphTarget.isDirty) {
|
||||||
|
fa = morphTarget.morphWeights;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
@ -1175,6 +1387,12 @@ class Uniforms {
|
|||||||
|
|
||||||
if (fa == null) return;
|
if (fa == null) return;
|
||||||
g.setFloats(location, fa);
|
g.setFloats(location, fa);
|
||||||
|
|
||||||
|
#if lnx_morph_target
|
||||||
|
if (c.link == "_morphWeights") {
|
||||||
|
cast(object, MeshObject).morphTarget.markClean();
|
||||||
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
else if (c.type == "int") {
|
else if (c.type == "int") {
|
||||||
var i: Null<Int> = null;
|
var i: Null<Int> = null;
|
||||||
@ -1203,6 +1421,7 @@ class Uniforms {
|
|||||||
if (materialContext.raw.bind_constants != null) {
|
if (materialContext.raw.bind_constants != null) {
|
||||||
for (i in 0...materialContext.raw.bind_constants.length) {
|
for (i in 0...materialContext.raw.bind_constants.length) {
|
||||||
var matc = materialContext.raw.bind_constants[i];
|
var matc = materialContext.raw.bind_constants[i];
|
||||||
|
if (matc == null) continue;
|
||||||
var pos = -1;
|
var pos = -1;
|
||||||
for (i in 0...context.raw.constants.length) {
|
for (i in 0...context.raw.constants.length) {
|
||||||
if (context.raw.constants[i].name == matc.name) {
|
if (context.raw.constants[i].name == matc.name) {
|
||||||
|
|||||||
@ -3,6 +3,14 @@ package iron.system;
|
|||||||
class Time {
|
class Time {
|
||||||
public static var scale = 1.0;
|
public static var scale = 1.0;
|
||||||
|
|
||||||
|
// TODO: VR Frame Time Override - used to sync physics with VR headset refresh rate
|
||||||
|
#if lnx_vr
|
||||||
|
public static var vrFrameTime: Float = -1.0; // VR frame time in seconds (-1 = not in VR)
|
||||||
|
static var lastVRFrameTime: Float = 0.0;
|
||||||
|
static var vrFrameCount: Int = 0;
|
||||||
|
static var normalModeLogged: Bool = false;
|
||||||
|
#end
|
||||||
|
|
||||||
static var frequency: Null<Int> = null;
|
static var frequency: Null<Int> = null;
|
||||||
static function initFrequency() {
|
static function initFrequency() {
|
||||||
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
frequency = kha.Display.primary != null ? kha.Display.primary.frequency : 60;
|
||||||
@ -47,6 +55,24 @@ class Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function update() {
|
public static function update() {
|
||||||
|
#if lnx_vr
|
||||||
|
// TODO: use VR frame time when in VR present mode to sync physics with headset refresh
|
||||||
|
if (vrFrameTime >= 0.0) {
|
||||||
|
if (lastVRFrameTime > 0.0) {
|
||||||
|
_delta = vrFrameTime - lastVRFrameTime;
|
||||||
|
} else {
|
||||||
|
_delta = 1.0 / 90.0; // Default to 90Hz for first VR frame
|
||||||
|
}
|
||||||
|
lastVRFrameTime = vrFrameTime;
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (!normalModeLogged) {
|
||||||
|
normalModeLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
_delta = realTime() - lastTime;
|
_delta = realTime() - lastTime;
|
||||||
lastTime = realTime();
|
lastTime = realTime();
|
||||||
}
|
}
|
||||||
|
|||||||
138
leenkx/Sources/iron/system/VRController.hx
Normal file
138
leenkx/Sources/iron/system/VRController.hx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package iron.system;
|
||||||
|
|
||||||
|
#if lnx_vr
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.math.Quat;
|
||||||
|
|
||||||
|
class VRController {
|
||||||
|
|
||||||
|
public static var leftHandPosition: Vec4 = new Vec4();
|
||||||
|
public static var leftHandRotation: Quat = new Quat();
|
||||||
|
public static var rightHandPosition: Vec4 = new Vec4();
|
||||||
|
public static var rightHandRotation: Quat = new Quat();
|
||||||
|
|
||||||
|
public static var leftHandActive: Bool = false;
|
||||||
|
public static var rightHandActive: Bool = false;
|
||||||
|
|
||||||
|
public static var leftThumbstickX: Float = 0.0;
|
||||||
|
public static var leftThumbstickY: Float = 0.0;
|
||||||
|
public static var rightThumbstickX: Float = 0.0;
|
||||||
|
public static var rightThumbstickY: Float = 0.0;
|
||||||
|
|
||||||
|
public static var leftTrigger: Float = 0.0;
|
||||||
|
public static var rightTrigger: Float = 0.0;
|
||||||
|
public static var leftGrip: Float = 0.0;
|
||||||
|
public static var rightGrip: Float = 0.0;
|
||||||
|
|
||||||
|
public static var leftButtonX: Bool = false;
|
||||||
|
public static var leftButtonY: Bool = false;
|
||||||
|
public static var rightButtonA: Bool = false;
|
||||||
|
public static var rightButtonB: Bool = false;
|
||||||
|
|
||||||
|
public static var debugLog:Bool = false;
|
||||||
|
|
||||||
|
public static function enableDebug() {
|
||||||
|
debugLog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function disableDebug() {
|
||||||
|
debugLog = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function updatePoses() {
|
||||||
|
var vr: kha.js.vr.VrInterface = cast kha.vr.VrInterface.instance;
|
||||||
|
if (vr == null || !vr.IsPresenting()) {
|
||||||
|
if (debugLog) trace("[VRController] Not presenting or VR null");
|
||||||
|
leftHandActive = false;
|
||||||
|
rightHandActive = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
untyped window._vrControllerFrame = (untyped window._vrControllerFrame || 0) + 1;
|
||||||
|
|
||||||
|
leftHandActive = false;
|
||||||
|
rightHandActive = false;
|
||||||
|
|
||||||
|
leftButtonX = false;
|
||||||
|
leftButtonY = false;
|
||||||
|
rightButtonA = false;
|
||||||
|
rightButtonB = false;
|
||||||
|
|
||||||
|
var refSpace = untyped vr.xrRefSpace;
|
||||||
|
if (vr.currentInputSources == null || vr.currentFrame == null || refSpace == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputSources = vr.currentInputSources;
|
||||||
|
var frame = vr.currentFrame;
|
||||||
|
|
||||||
|
var sourceCount:Int = untyped inputSources.length;
|
||||||
|
|
||||||
|
for (i in 0...sourceCount) {
|
||||||
|
var inputSource = untyped inputSources[i];
|
||||||
|
if (inputSource == null) continue;
|
||||||
|
|
||||||
|
var handedness = untyped inputSource.handedness; // "left", "right", or "none"
|
||||||
|
|
||||||
|
var gripSpace = untyped inputSource.gripSpace;
|
||||||
|
var targetRaySpace = untyped inputSource.targetRaySpace;
|
||||||
|
// use targetRaySpace first laser/pointer and fall back to gripSpace
|
||||||
|
var space = (targetRaySpace != null) ? targetRaySpace : gripSpace;
|
||||||
|
|
||||||
|
if (space == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pose = untyped frame.getPose(space, refSpace);
|
||||||
|
if (pose == null || pose.transform == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transform = pose.transform;
|
||||||
|
var pos = transform.position;
|
||||||
|
var orient = transform.orientation;
|
||||||
|
|
||||||
|
if (handedness == "left") {
|
||||||
|
leftHandPosition.set(pos.x, pos.y, pos.z);
|
||||||
|
leftHandRotation.set(orient.x, orient.y, orient.z, orient.w);
|
||||||
|
leftHandActive = true;
|
||||||
|
|
||||||
|
var gamepad = untyped inputSource.gamepad;
|
||||||
|
if (gamepad != null) {
|
||||||
|
// [0]=thumbstickX [1]=thumbstickY [2]=touchpadX [3]=touchpadY
|
||||||
|
if (gamepad.axes != null && gamepad.axes.length >= 2) {
|
||||||
|
leftThumbstickX = gamepad.axes[0];
|
||||||
|
leftThumbstickY = gamepad.axes[1];
|
||||||
|
}
|
||||||
|
// [0]=trigger [1]=grip [4]=X [5]=Y
|
||||||
|
if (gamepad.buttons != null) {
|
||||||
|
if (gamepad.buttons.length > 0) leftTrigger = gamepad.buttons[0].value;
|
||||||
|
if (gamepad.buttons.length > 1) leftGrip = gamepad.buttons[1].value;
|
||||||
|
if (gamepad.buttons.length > 4) leftButtonX = gamepad.buttons[4].pressed;
|
||||||
|
if (gamepad.buttons.length > 5) leftButtonY = gamepad.buttons[5].pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (handedness == "right") {
|
||||||
|
rightHandPosition.set(pos.x, pos.y, pos.z);
|
||||||
|
rightHandRotation.set(orient.x, orient.y, orient.z, orient.w);
|
||||||
|
rightHandActive = true;
|
||||||
|
|
||||||
|
var gamepad = untyped inputSource.gamepad;
|
||||||
|
if (gamepad != null) {
|
||||||
|
if (gamepad.axes != null && gamepad.axes.length >= 2) {
|
||||||
|
rightThumbstickX = gamepad.axes[0];
|
||||||
|
rightThumbstickY = gamepad.axes[1];
|
||||||
|
}
|
||||||
|
if (gamepad.buttons != null) {
|
||||||
|
if (gamepad.buttons.length > 0) rightTrigger = gamepad.buttons[0].value;
|
||||||
|
if (gamepad.buttons.length > 1) rightGrip = gamepad.buttons[1].value;
|
||||||
|
if (gamepad.buttons.length > 4) rightButtonA = gamepad.buttons[4].pressed;
|
||||||
|
if (gamepad.buttons.length > 5) rightButtonB = gamepad.buttons[5].pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
Reference in New Issue
Block a user