forked from LeenkxTeam/LNXSDK
Merge pull request 'main' (#150) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#150
This commit is contained in:
@ -3,8 +3,8 @@
|
|||||||
{
|
{
|
||||||
"name": "probe_cubemap",
|
"name": "probe_cubemap",
|
||||||
"depth_write": false,
|
"depth_write": false,
|
||||||
"compare_mode": "less",
|
"compare_mode": "greater",
|
||||||
"cull_mode": "clockwise",
|
"cull_mode": "counter_clockwise",
|
||||||
"blend_source": "blend_one",
|
"blend_source": "blend_one",
|
||||||
"blend_destination": "blend_one",
|
"blend_destination": "blend_one",
|
||||||
"blend_operation": "add",
|
"blend_operation": "add",
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
{
|
{
|
||||||
"name": "probe_planar",
|
"name": "probe_planar",
|
||||||
"depth_write": false,
|
"depth_write": false,
|
||||||
"compare_mode": "less",
|
"compare_mode": "greater",
|
||||||
"cull_mode": "clockwise",
|
"cull_mode": "counter_clockwise",
|
||||||
"blend_source": "blend_one",
|
"blend_source": "blend_one",
|
||||||
"blend_destination": "blend_one",
|
"blend_destination": "blend_one",
|
||||||
"blend_operation": "add",
|
"blend_operation": "add",
|
||||||
|
|||||||
@ -34,7 +34,6 @@ import iron.math.Quat;
|
|||||||
class RenderPath {
|
class RenderPath {
|
||||||
public static var active: RenderPath;
|
public static var active: RenderPath;
|
||||||
#if lnx_vr
|
#if lnx_vr
|
||||||
static var vrSimulateMode: Bool = false;
|
|
||||||
static var vrCameraOffsetSet:Bool = false;
|
static var vrCameraOffsetSet:Bool = false;
|
||||||
static var vrCameraOffset:Vec4 = new Vec4();
|
static var vrCameraOffset:Vec4 = new Vec4();
|
||||||
static var wasVRPresenting:Bool = false;
|
static var wasVRPresenting:Bool = false;
|
||||||
@ -45,6 +44,9 @@ class RenderPath {
|
|||||||
public static var vrCenterCameraWorld:Mat4 = null;
|
public static var vrCenterCameraWorld:Mat4 = null;
|
||||||
|
|
||||||
static var vrOriginalSuperSample:Float = -1.0;
|
static var vrOriginalSuperSample:Float = -1.0;
|
||||||
|
#end
|
||||||
|
|
||||||
|
static var vrSimulateMode: Bool = false;
|
||||||
public static inline function isVRPresenting(): Bool {
|
public static inline function isVRPresenting(): Bool {
|
||||||
#if (kha_webgl && lnx_vr)
|
#if (kha_webgl && lnx_vr)
|
||||||
return kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsPresenting();
|
return kha.vr.VrInterface.instance != null && kha.vr.VrInterface.instance.IsPresenting();
|
||||||
@ -57,8 +59,6 @@ class RenderPath {
|
|||||||
return vrSimulateMode;
|
return vrSimulateMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
public var frameScissor = false;
|
public var frameScissor = false;
|
||||||
public var frameScissorX = 0;
|
public var frameScissorX = 0;
|
||||||
public var frameScissorY = 0;
|
public var frameScissorY = 0;
|
||||||
|
|||||||
@ -1003,7 +1003,7 @@ class Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (o.vertex_groups != null) {
|
if (o.vertex_groups != null) {
|
||||||
cast(object, MeshObject).vertexGroups = new Map();
|
object.vertexGroups = new Map();
|
||||||
for (p in o.vertex_groups) {
|
for (p in o.vertex_groups) {
|
||||||
var verts:Array<iron.math.Vec4> = [];
|
var verts:Array<iron.math.Vec4> = [];
|
||||||
|
|
||||||
@ -1014,7 +1014,7 @@ class Scene {
|
|||||||
verts.push(new iron.math.Vec4(data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 1.0));
|
verts.push(new iron.math.Vec4(data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 1.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cast(object, MeshObject).vertexGroups.set(p.name, verts);
|
object.vertexGroups.set(p.name, verts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,7 @@ class Geometry {
|
|||||||
// Skinned
|
// Skinned
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
public var skeletonTransformsI: Array<Mat4> = null;
|
public var skeletonTransformsI: Array<Mat4> = null;
|
||||||
|
public var skeletonBoneRefs: Array<String> = null;
|
||||||
public var actions: Map<String, Array<iron.data.SceneFormat.TObj>> = new Map();
|
public var actions: Map<String, Array<iron.data.SceneFormat.TObj>> = new Map();
|
||||||
public var mats: Map<String, Array<Mat4>> = new Map();
|
public var mats: Map<String, Array<Mat4>> = new Map();
|
||||||
#end
|
#end
|
||||||
@ -326,6 +327,33 @@ class Geometry {
|
|||||||
|
|
||||||
// Skinned
|
// Skinned
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
|
public function addArmature(armature: iron.data.Armature) {
|
||||||
|
for (a in armature.actions) {
|
||||||
|
addAction(a.bones, a.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAction(bones: Array<TObj>, name: String) {
|
||||||
|
if (bones == null || skeletonBoneRefs == null) return;
|
||||||
|
if (actions.get(name) != null) return;
|
||||||
|
var actionBones: Array<TObj> = [];
|
||||||
|
|
||||||
|
for (s in skeletonBoneRefs) {
|
||||||
|
for (b in bones) {
|
||||||
|
if (b.name == s) {
|
||||||
|
actionBones.push(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions.set(name, actionBones);
|
||||||
|
|
||||||
|
var actionMats: Array<Mat4> = [];
|
||||||
|
for (b in actionBones) {
|
||||||
|
actionMats.push(Mat4.fromFloat32Array(b.transform.values));
|
||||||
|
}
|
||||||
|
mats.set(name, actionMats);
|
||||||
|
}
|
||||||
|
|
||||||
public function initSkeletonTransforms(transformsI: Array<Float32Array>) {
|
public function initSkeletonTransforms(transformsI: Array<Float32Array>) {
|
||||||
skeletonTransformsI = [];
|
skeletonTransformsI = [];
|
||||||
for (t in transformsI) {
|
for (t in transformsI) {
|
||||||
|
|||||||
@ -108,6 +108,7 @@ class MeshData {
|
|||||||
// Skinned
|
// Skinned
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
if (raw.skin != null) {
|
if (raw.skin != null) {
|
||||||
|
dat.geom.skeletonBoneRefs = raw.skin.bone_ref_array;
|
||||||
dat.geom.initSkeletonTransforms(raw.skin.transformsI);
|
dat.geom.initSkeletonTransforms(raw.skin.transformsI);
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -49,7 +49,7 @@ class Reader {
|
|||||||
* @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
|
* @see https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
|
||||||
* @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
|
* @see https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
|
||||||
*/
|
*/
|
||||||
public function read() : format.bmp.Data {
|
public function read() : iron.format.bmp.Data {
|
||||||
// Read Header
|
// Read Header
|
||||||
for (b in ["B".code, "M".code]) {
|
for (b in ["B".code, "M".code]) {
|
||||||
if (input.readByte() != b) throw "Invalid header";
|
if (input.readByte() != b) throw "Invalid header";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package format.gif;
|
package iron.format.gif;
|
||||||
import format.gif.Data;
|
import iron.format.gif.Data;
|
||||||
import haxe.ds.Vector;
|
import haxe.ds.Vector;
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
import haxe.io.Output;
|
import haxe.io.Output;
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
package iron.format.jpg;
|
package iron.format.jpg;
|
||||||
|
|
||||||
class Writer {
|
class WriterOriginal {
|
||||||
var ZigZag: Array<Int>;
|
var ZigZag: Array<Int>;
|
||||||
|
|
||||||
// Static table initialization
|
// Static table initialization
|
||||||
|
|||||||
@ -40,11 +40,6 @@ class Animation {
|
|||||||
public var paused = false;
|
public var paused = false;
|
||||||
var frameTime: FastFloat = 1 / 60;
|
var frameTime: FastFloat = 1 / 60;
|
||||||
|
|
||||||
var blendTime: FastFloat = 0.0;
|
|
||||||
var blendCurrent: FastFloat = 0.0;
|
|
||||||
var blendFactor: FastFloat = 0.0;
|
|
||||||
|
|
||||||
var lastFrameIndex = -1;
|
|
||||||
var markerEvents: Map<ActionSampler, Map<String, Array<Void->Void>>> = null;
|
var markerEvents: Map<ActionSampler, Map<String, Array<Void->Void>>> = null;
|
||||||
|
|
||||||
public var activeActions: Map<String, ActionSampler> = null;
|
public var activeActions: Map<String, ActionSampler> = null;
|
||||||
@ -63,8 +58,6 @@ class Animation {
|
|||||||
|
|
||||||
public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) {
|
public function play(action = "", onComplete: Void->Void = null, blendTime = 0.0, speed = 1.0, loop = true) {
|
||||||
if (blendTime > 0) {
|
if (blendTime > 0) {
|
||||||
this.blendTime = blendTime;
|
|
||||||
this.blendCurrent = 0.0;
|
|
||||||
frameIndex = 0;
|
frameIndex = 0;
|
||||||
time = 0.0;
|
time = 0.0;
|
||||||
}
|
}
|
||||||
@ -93,6 +86,7 @@ class Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function update(delta: FastFloat) {
|
public function update(delta: FastFloat) {
|
||||||
|
if (paused) return;
|
||||||
if(activeActions == null) return;
|
if(activeActions == null) return;
|
||||||
|
|
||||||
for(sampler in activeActions){
|
for(sampler in activeActions){
|
||||||
@ -160,26 +154,27 @@ class Animation {
|
|||||||
while (checkFrameIndex(track.frames, time, frameIndex, speed)) frameIndex += sign;
|
while (checkFrameIndex(track.frames, time, frameIndex, speed)) frameIndex += sign;
|
||||||
|
|
||||||
// Marker events
|
// Marker events
|
||||||
if (markerEvents != null && anim.marker_names != null && frameIndex != lastFrameIndex) {
|
if (markerEvents != null && anim.marker_names != null && frameIndex != sampler.offsetOld) {
|
||||||
if(markerEvents.get(sampler) != null){
|
var markerListeners = markerEvents.get(sampler);
|
||||||
|
if (markerListeners != null) {
|
||||||
|
var fromFrame = sampler.offsetOld < 0 ?
|
||||||
|
(speed > 0 ? -1 : track.frames[track.frames.length - 1] + 1) :
|
||||||
|
track.frames[sampler.offsetOld];
|
||||||
|
var toFrame = track.frames[frameIndex];
|
||||||
for (i in 0...anim.marker_frames.length) {
|
for (i in 0...anim.marker_frames.length) {
|
||||||
if (frameIndex == anim.marker_frames[i]) {
|
var markerFrame = anim.marker_frames[i];
|
||||||
var markerAct = markerEvents.get(sampler);
|
var crossed =
|
||||||
var ar = markerAct.get(anim.marker_names[i]);
|
(speed > 0 && toFrame >= fromFrame && markerFrame > fromFrame && markerFrame <= toFrame) ||
|
||||||
if (ar != null) for (f in ar) f();
|
(speed < 0 && toFrame <= fromFrame && markerFrame <= fromFrame && markerFrame > toFrame) ||
|
||||||
} else {
|
(speed > 0 && toFrame < fromFrame && (markerFrame > fromFrame || markerFrame <= toFrame)) ||
|
||||||
for (j in 0...(frameIndex - lastFrameIndex)) {
|
(speed < 0 && toFrame > fromFrame && (markerFrame >= toFrame || markerFrame < fromFrame));
|
||||||
if (lastFrameIndex + j + 1 == anim.marker_frames[i]) {
|
if (crossed) {
|
||||||
var markerAct = markerEvents.get(sampler);
|
var listeners = markerListeners.get(anim.marker_names[i]);
|
||||||
var ar = markerAct.get(anim.marker_names[i]);
|
if (listeners != null) for (listener in listeners) listener();
|
||||||
if (ar != null) for (f in ar) f();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastFrameIndex = frameIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of track
|
// End of track
|
||||||
if (isTrackEnd(track, frameIndex, speed)) {
|
if (isTrackEnd(track, frameIndex, speed)) {
|
||||||
@ -191,8 +186,8 @@ class Animation {
|
|||||||
else {
|
else {
|
||||||
frameIndex -= sign;
|
frameIndex -= sign;
|
||||||
sampler.paused = true;
|
sampler.paused = true;
|
||||||
}
|
|
||||||
if (sampler.onComplete != null) for(func in sampler.onComplete){ func();};
|
if (sampler.onComplete != null) for(func in sampler.onComplete){ func();};
|
||||||
|
}
|
||||||
sampler.trackEnd = true;
|
sampler.trackEnd = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -112,6 +112,10 @@ class BoneAnimation extends Animation {
|
|||||||
this.data = mo != null ? mo.data : null;
|
this.data = mo != null ? mo.data : null;
|
||||||
this.isSkinned = data != null ? data.isSkinned : false;
|
this.isSkinned = data != null ? data.isSkinned : false;
|
||||||
if (this.isSkinned) {
|
if (this.isSkinned) {
|
||||||
|
if (armature != null && data.geom.skeletonBoneRefs != null
|
||||||
|
&& data.geom.actions.keys().hasNext() == false) {
|
||||||
|
data.geom.addArmature(armature);
|
||||||
|
}
|
||||||
var boneSize = 12; // Dual-quat skinning + scaling
|
var boneSize = 12; // Dual-quat skinning + scaling
|
||||||
this.skinBuffer = new Float32Array(skinMaxBones * boneSize);
|
this.skinBuffer = new Float32Array(skinMaxBones * boneSize);
|
||||||
for (i in 0...this.skinBuffer.length) this.skinBuffer[i] = 0;
|
for (i in 0...this.skinBuffer.length) this.skinBuffer[i] = 0;
|
||||||
@ -205,18 +209,37 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setAction(action: String) {
|
function setAction(action: String) {
|
||||||
|
if (isSkinned) {
|
||||||
|
skeletonBones = data.geom.actions.get(action);
|
||||||
|
var geomMats = data.geom.mats.get(action);
|
||||||
|
if (geomMats != null) {
|
||||||
|
skeletonMats = [];
|
||||||
|
for (i in 0...geomMats.length) {
|
||||||
|
var m = Mat4.identity();
|
||||||
|
m.setFrom(geomMats[i]);
|
||||||
|
skeletonMats.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else skeletonMats = null;
|
||||||
|
}
|
||||||
|
if (skeletonBones == null) {
|
||||||
if (armature == null) return;
|
if (armature == null) return;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
var a = armature.getAction(action);
|
var a = armature.getAction(action);
|
||||||
if (a == null) return;
|
if (a == null) return;
|
||||||
skeletonBones = a.bones;
|
skeletonBones = a.bones;
|
||||||
skeletonMats = a.mats;
|
skeletonMats = a.mats;
|
||||||
if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
|
}
|
||||||
|
if (skeletonMats.length == skeletonBones.length) skeletonMats.push(Mat4.identity()); // Root motion cache
|
||||||
rootMotionCacheInit = true;
|
rootMotionCacheInit = true;
|
||||||
setMats();
|
setMats();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAction(action: String): Array<TObj> {
|
function getAction(action: String): Array<TObj> {
|
||||||
|
if (isSkinned) {
|
||||||
|
var bones = data.geom.actions.get(action);
|
||||||
|
if (bones != null) return bones;
|
||||||
|
}
|
||||||
if (armature == null) return null;
|
if (armature == null) return null;
|
||||||
armature.initMats();
|
armature.initMats();
|
||||||
var a = armature.getAction(action);
|
var a = armature.getAction(action);
|
||||||
@ -251,15 +274,91 @@ class BoneAnimation extends Animation {
|
|||||||
updateAnimation = f;
|
updateAnimation = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var blendSampler1: ActionSampler = null;
|
||||||
|
var blendSampler2: ActionSampler = null;
|
||||||
|
var blendMats: Array<Mat4> = null;
|
||||||
|
|
||||||
|
public function blend(action1: String, action2: String, factor: FastFloat) {
|
||||||
|
var actionName1: String = getName(action1);
|
||||||
|
var actionName2: String = getName(action2);
|
||||||
|
if (blendSampler1 == null || blendSampler1.action != actionName1) {
|
||||||
|
blendSampler1 = new ActionSampler(actionName1);
|
||||||
|
registerAction("blendSampler1", blendSampler1);
|
||||||
|
}
|
||||||
|
if (blendSampler2 == null || blendSampler2.action != actionName2) {
|
||||||
|
blendSampler2 = new ActionSampler(actionName2);
|
||||||
|
registerAction("blendSampler2", blendSampler2);
|
||||||
|
}
|
||||||
|
blendSampler1.paused = false;
|
||||||
|
blendSampler2.paused = false;
|
||||||
|
if (transitionSampler != null) {
|
||||||
|
transitionSampler.paused = true;
|
||||||
|
transitionSampler = null;
|
||||||
|
}
|
||||||
|
if (currentPlaySampler != null) currentPlaySampler.paused = true;
|
||||||
|
currentPlaySampler = blendSampler1;
|
||||||
|
if (skeletonMats == null) setAction(actionName1);
|
||||||
|
if (skeletonMats == null) return;
|
||||||
|
if (blendMats == null) blendMats = initMatsEmpty();
|
||||||
|
updateAnimation = function(mats) {
|
||||||
|
sampleAction(blendSampler1, mats);
|
||||||
|
sampleAction(blendSampler2, blendMats);
|
||||||
|
blendAction(mats, blendMats, mats, factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var playSamplers: Map<String, ActionSampler> = [];
|
||||||
|
var currentPlaySampler: ActionSampler = null;
|
||||||
|
var transitionSampler: ActionSampler = null;
|
||||||
|
var transitionMats: Array<Mat4> = null;
|
||||||
|
var transitionTime: FastFloat = 0.0;
|
||||||
|
var transitionDuration: FastFloat = 0.0;
|
||||||
|
|
||||||
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) {
|
||||||
var actionName: String = getName(action);
|
var actionName: String = getName(action);
|
||||||
if (actionName != "") {
|
if (actionName != "") {
|
||||||
setAction(actionName);
|
setAction(actionName);
|
||||||
super.play(actionName, onComplete, blendTime, speed, loop);
|
super.play(actionName, onComplete, blendTime, speed, loop);
|
||||||
var tempAnimParam = new ActionSampler(actionName);
|
var sampler = playSamplers.get(actionName);
|
||||||
registerAction("tempAction", tempAnimParam);
|
if (sampler == null) {
|
||||||
|
if (activeActions != null) sampler = activeActions.get(actionName);
|
||||||
|
if (sampler == null) sampler = new ActionSampler(actionName);
|
||||||
|
playSamplers.set(actionName, sampler);
|
||||||
|
}
|
||||||
|
if (transitionSampler != null) {
|
||||||
|
transitionSampler.paused = true;
|
||||||
|
transitionSampler = null;
|
||||||
|
}
|
||||||
|
if (blendTime > 0 && currentPlaySampler != null && currentPlaySampler != sampler && skeletonMats != null) {
|
||||||
|
transitionSampler = currentPlaySampler;
|
||||||
|
if (transitionMats == null) transitionMats = initMatsEmpty();
|
||||||
|
transitionTime = 0.0;
|
||||||
|
transitionDuration = blendTime;
|
||||||
|
if (blendSampler1 != null && blendSampler1 != transitionSampler) blendSampler1.paused = true;
|
||||||
|
if (blendSampler2 != null && blendSampler2 != transitionSampler) blendSampler2.paused = true;
|
||||||
|
}
|
||||||
|
else if (currentPlaySampler != null) currentPlaySampler.paused = true;
|
||||||
|
currentPlaySampler = sampler;
|
||||||
|
sampler.speed = speed;
|
||||||
|
sampler.loop = loop;
|
||||||
|
sampler.onComplete = onComplete != null ? [onComplete] : null;
|
||||||
|
sampler.restartAction();
|
||||||
|
registerAction(actionName, sampler);
|
||||||
updateAnimation = function(mats){
|
updateAnimation = function(mats){
|
||||||
sampleAction(tempAnimParam, mats);
|
if (transitionSampler != null) {
|
||||||
|
sampleAction(transitionSampler, transitionMats);
|
||||||
|
sampleAction(sampler, mats);
|
||||||
|
var f = transitionTime / transitionDuration;
|
||||||
|
if (f > 1.0) f = 1.0;
|
||||||
|
f = f * f * (3.0 - 2.0 * f); // Smoothstep
|
||||||
|
blendAction(transitionMats, mats, mats, f, -1, 0.0);
|
||||||
|
transitionTime += delta;
|
||||||
|
if (transitionTime >= transitionDuration) {
|
||||||
|
transitionSampler.paused = true;
|
||||||
|
transitionSampler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else sampleAction(sampler, mats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,6 +378,7 @@ class BoneAnimation extends Animation {
|
|||||||
#end
|
#end
|
||||||
|
|
||||||
super.update(delta);
|
super.update(delta);
|
||||||
|
if (paused) return;
|
||||||
if(updateAnimation != null) {
|
if(updateAnimation != null) {
|
||||||
updateAnimation(skeletonMats);
|
updateAnimation(skeletonMats);
|
||||||
}
|
}
|
||||||
@ -540,12 +640,14 @@ class BoneAnimation extends Animation {
|
|||||||
|
|
||||||
|
|
||||||
inline function interpolateSample(track: TTrack, m: Mat4, t: FastFloat, ti: Int, sign: Int) {
|
inline function interpolateSample(track: TTrack, m: Mat4, t: FastFloat, ti: Int, sign: Int) {
|
||||||
|
var tj = ti + sign;
|
||||||
|
if (tj < 0 || tj >= track.frames.length) tj = ti;
|
||||||
var t1 = track.frames[ti] * frameTime;
|
var t1 = track.frames[ti] * frameTime;
|
||||||
var t2 = track.frames[ti + sign] * frameTime;
|
var t2 = track.frames[tj] * frameTime;
|
||||||
var s: FastFloat = (t - t1) / (t2 - t1); // Linear
|
var s: FastFloat = t2 != t1 ? (t - t1) / (t2 - t1) : 0.0; // Linear
|
||||||
|
|
||||||
m1.setF32(track.values, ti * 16); // Offset to 4x4 matrix array
|
m1.setF32(track.values, ti * 16); // Offset to 4x4 matrix array
|
||||||
m2.setF32(track.values, (ti + sign) * 16);
|
m2.setF32(track.values, tj * 16);
|
||||||
|
|
||||||
// Decompose
|
// Decompose
|
||||||
m1.decompose(vpos, q1, vscl);
|
m1.decompose(vpos, q1, vscl);
|
||||||
@ -643,7 +745,7 @@ class BoneAnimation extends Animation {
|
|||||||
return actionMats != null ? actionMats[getBoneIndex(bone)] : null;
|
return actionMats != null ? actionMats[getBoneIndex(bone)] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWorldMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
|
public function getWorldMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
|
||||||
if (applyParent == null) {
|
if (applyParent == null) {
|
||||||
applyParent = [];
|
applyParent = [];
|
||||||
for (m in actionMats) applyParent.push(true);
|
for (m in actionMats) applyParent.push(true);
|
||||||
@ -728,7 +830,7 @@ class BoneAnimation extends Animation {
|
|||||||
// Skip if root motion is disabled
|
// Skip if root motion is disabled
|
||||||
if(j < 0) continue;
|
if(j < 0) continue;
|
||||||
// Use Action 2
|
// Use Action 2
|
||||||
if(skeletonBones[j].bone_layers[layerMask] || layerMask < 0){
|
if(layerMask < 0 || skeletonBones[j].bone_layers[layerMask]){
|
||||||
resultMat[i].setFrom(actionMats2[i]);
|
resultMat[i].setFrom(actionMats2[i]);
|
||||||
}
|
}
|
||||||
// Use Action 1 if not in layer
|
// Use Action 1 if not in layer
|
||||||
@ -744,7 +846,7 @@ class BoneAnimation extends Animation {
|
|||||||
// Skip if root motion is disabled
|
// Skip if root motion is disabled
|
||||||
if(j < 0) continue;
|
if(j < 0) continue;
|
||||||
// Blend
|
// Blend
|
||||||
if(skeletonBones[j].bone_layers[j] || layerMask < 0) {
|
if(layerMask < 0 || skeletonBones[j].bone_layers[layerMask]) {
|
||||||
// Decompose
|
// Decompose
|
||||||
m.setFrom(actionMats1[i]);
|
m.setFrom(actionMats1[i]);
|
||||||
m1.setFrom(actionMats2[i]);
|
m1.setFrom(actionMats2[i]);
|
||||||
@ -786,8 +888,10 @@ class BoneAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
for(i in 0...baseActionMats.length){
|
for(i in 0...baseActionMats.length){
|
||||||
|
var j = i == skeletonBones.length ? rootMotionIndex : i;
|
||||||
|
if(j < 0) continue;
|
||||||
|
|
||||||
if(skeletonBones[i].bone_layers[layerMask] || layerMask < 0) {
|
if(layerMask < 0 || skeletonBones[j].bone_layers[layerMask]) {
|
||||||
// Decompose
|
// Decompose
|
||||||
m.setFrom(baseActionMats[i]);
|
m.setFrom(baseActionMats[i]);
|
||||||
m1.setFrom(addActionMats[i]);
|
m1.setFrom(addActionMats[i]);
|
||||||
|
|||||||
@ -13,7 +13,10 @@ class Constraint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function apply(transform: Transform) {
|
public function apply(transform: Transform) {
|
||||||
if (target == null && raw.target != null) target = Scene.active.getChild(raw.target).transform;
|
if (target == null && raw.target != null) {
|
||||||
|
var o = Scene.active.getChild(raw.target);
|
||||||
|
if (o != null) target = o.transform;
|
||||||
|
}
|
||||||
|
|
||||||
if (target == null && raw.type != "LIMIT_LOCATION" && raw.type != "LIMIT_ROTATION" && raw.type != "LIMIT_SCALE") return;
|
if (target == null && raw.type != "LIMIT_LOCATION" && raw.type != "LIMIT_ROTATION" && raw.type != "LIMIT_SCALE") return;
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,6 @@ class MeshObject extends Object {
|
|||||||
#if lnx_morph_target
|
#if lnx_morph_target
|
||||||
public var morphTarget: MorphTarget = null;
|
public var morphTarget: MorphTarget = null;
|
||||||
#end
|
#end
|
||||||
public var vertexGroups: Map<String, Array<Vec4>> = null;
|
|
||||||
|
|
||||||
#if lnx_veloc
|
#if lnx_veloc
|
||||||
public var prevMatrix = Mat4.identity();
|
public var prevMatrix = Mat4.identity();
|
||||||
@ -240,6 +239,7 @@ class MeshObject extends Object {
|
|||||||
return setCulled(isShadow, true);
|
return setCulled(isShadow, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if rp_shadowmap
|
||||||
if (isShadow && light.data.raw.type == "sun") {
|
if (isShadow && light.data.raw.type == "sun") {
|
||||||
var frame = RenderPath.active.frame;
|
var frame = RenderPath.active.frame;
|
||||||
if (shadowCameraCullFrame == frame && shadowCameraCullCamera == camera &&
|
if (shadowCameraCullFrame == frame && shadowCameraCullCamera == camera &&
|
||||||
@ -270,10 +270,8 @@ class MeshObject extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
//culled = false;
|
|
||||||
//return culled;
|
|
||||||
return setCulled(isShadow, false);
|
return setCulled(isShadow, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class Object {
|
|||||||
public var transform: Transform;
|
public var transform: Transform;
|
||||||
public var constraints: Array<Constraint> = null;
|
public var constraints: Array<Constraint> = null;
|
||||||
public var traits: Array<Trait> = [];
|
public var traits: Array<Trait> = [];
|
||||||
|
public var vertexGroups: Map<String, Array<iron.math.Vec4>> = null;
|
||||||
|
|
||||||
public var parent: Object = null;
|
public var parent: Object = null;
|
||||||
public var children: Array<Object> = [];
|
public var children: Array<Object> = [];
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class ObjectAnimation extends Animation {
|
|||||||
public var oactions: Array<TSceneFormat>;
|
public var oactions: Array<TSceneFormat>;
|
||||||
var oaction: TObj;
|
var oaction: TObj;
|
||||||
var s0: FastFloat = 0.0;
|
var s0: FastFloat = 0.0;
|
||||||
|
var delta: FastFloat = 0;
|
||||||
var bezierFrameIndex = -1;
|
var bezierFrameIndex = -1;
|
||||||
|
|
||||||
var updateAnimation: Map<String, FastFloat> -> Void;
|
var updateAnimation: Map<String, FastFloat> -> Void;
|
||||||
@ -26,7 +27,11 @@ class ObjectAnimation extends Animation {
|
|||||||
public var transformMap: Map<String, FastFloat>;
|
public var transformMap: Map<String, FastFloat>;
|
||||||
|
|
||||||
var defaultSampler: ActionSampler = null;
|
var defaultSampler: ActionSampler = null;
|
||||||
static inline var DEFAULT_SAMPLER_ID = "__object_default_action__";
|
var playSamplers: Map<String, ActionSampler> = [];
|
||||||
|
var transitionSampler: ActionSampler = null;
|
||||||
|
var transitionMap: Map<String, FastFloat> = null;
|
||||||
|
var transitionTime: FastFloat = 0.0;
|
||||||
|
var transitionDuration: FastFloat = 0.0;
|
||||||
|
|
||||||
public static var trackNames: Array<String> = [ "xloc", "yloc", "zloc",
|
public static var trackNames: Array<String> = [ "xloc", "yloc", "zloc",
|
||||||
"xrot", "yrot", "zrot",
|
"xrot", "yrot", "zrot",
|
||||||
@ -67,27 +72,59 @@ class ObjectAnimation extends Animation {
|
|||||||
oaction = getAction(this.action);
|
oaction = getAction(this.action);
|
||||||
if (oaction != null) {
|
if (oaction != null) {
|
||||||
isSampled = oaction.sampled != null && oaction.sampled;
|
isSampled = oaction.sampled != null && oaction.sampled;
|
||||||
if (defaultSampler != null) {
|
var sampler = playSamplers.get(this.action);
|
||||||
deRegisterAction(DEFAULT_SAMPLER_ID);
|
if (sampler == null) {
|
||||||
|
if (activeActions != null) sampler = activeActions.get(this.action);
|
||||||
|
if (sampler == null) sampler = new ActionSampler(this.action);
|
||||||
|
playSamplers.set(this.action, sampler);
|
||||||
}
|
}
|
||||||
var callbacks = onComplete != null ? [onComplete] : null;
|
if (transitionSampler != null) {
|
||||||
defaultSampler = new ActionSampler(this.action, speed, loop, false, callbacks);
|
transitionSampler.paused = true;
|
||||||
registerAction(DEFAULT_SAMPLER_ID, defaultSampler);
|
transitionSampler = null;
|
||||||
if (paused) defaultSampler.paused = true;
|
}
|
||||||
|
if (blendTime > 0 && defaultSampler != null && defaultSampler != sampler) {
|
||||||
|
transitionSampler = defaultSampler;
|
||||||
|
transitionMap = initTransformMap();
|
||||||
|
transitionTime = 0.0;
|
||||||
|
transitionDuration = blendTime;
|
||||||
|
}
|
||||||
|
else if (defaultSampler != null) defaultSampler.paused = true;
|
||||||
|
defaultSampler = sampler;
|
||||||
|
sampler.speed = speed;
|
||||||
|
sampler.loop = loop;
|
||||||
|
sampler.onComplete = onComplete != null ? [onComplete] : null;
|
||||||
|
sampler.restartAction();
|
||||||
|
registerAction(this.action, sampler);
|
||||||
updateAnimation = function(map: Map<String, FastFloat>) {
|
updateAnimation = function(map: Map<String, FastFloat>) {
|
||||||
sampleAction(defaultSampler, map);
|
if (transitionSampler != null) {
|
||||||
|
sampleAction(transitionSampler, transitionMap);
|
||||||
|
sampleAction(sampler, map);
|
||||||
|
var f = transitionTime / transitionDuration;
|
||||||
|
if (f > 1.0) f = 1.0;
|
||||||
|
f = f * f * (3.0 - 2.0 * f); // Smoothstep
|
||||||
|
blendActionObject(transitionMap, map, map, f);
|
||||||
|
transitionTime += delta;
|
||||||
|
if (transitionTime >= transitionDuration) {
|
||||||
|
transitionSampler.paused = true;
|
||||||
|
transitionSampler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else sampleAction(sampler, map);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (defaultSampler != null) {
|
if (transitionSampler != null) {
|
||||||
deRegisterAction(DEFAULT_SAMPLER_ID);
|
transitionSampler.paused = true;
|
||||||
defaultSampler = null;
|
transitionSampler = null;
|
||||||
}
|
}
|
||||||
|
if (defaultSampler != null) defaultSampler.paused = true;
|
||||||
|
defaultSampler = null;
|
||||||
updateAnimation = null;
|
updateAnimation = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function update(delta: FastFloat) {
|
override public function update(delta: FastFloat) {
|
||||||
|
this.delta = delta;
|
||||||
if (!object.visible || object.culled) return;
|
if (!object.visible || object.culled) return;
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
@ -98,7 +135,6 @@ class ObjectAnimation extends Animation {
|
|||||||
transformMap = initTransformMap();
|
transformMap = initTransformMap();
|
||||||
|
|
||||||
super.update(delta);
|
super.update(delta);
|
||||||
if (defaultSampler != null) defaultSampler.paused = paused;
|
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
if (updateAnimation == null) return;
|
if (updateAnimation == null) return;
|
||||||
if (!isSkinned) updateObjectAnimation();
|
if (!isSkinned) updateObjectAnimation();
|
||||||
@ -160,11 +196,13 @@ class ObjectAnimation extends Animation {
|
|||||||
|
|
||||||
var ti = sampler.offset;
|
var ti = sampler.offset;
|
||||||
//ti = ti < 0 ? 1 : ti;
|
//ti = ti < 0 ? 1 : ti;
|
||||||
|
var tj = ti + sign;
|
||||||
|
if (tj < 0 || tj >= track.frames.length) tj = ti;
|
||||||
|
|
||||||
var t1 = track.frames[ti] * frameTime;
|
var t1 = track.frames[ti] * frameTime;
|
||||||
var t2 = track.frames[ti + sign] * frameTime;
|
var t2 = track.frames[tj] * frameTime;
|
||||||
var v1 = track.values[ti];
|
var v1 = track.values[ti];
|
||||||
var v2 = track.values[ti + sign];
|
var v2 = track.values[tj];
|
||||||
|
|
||||||
var value = interpolateLinear(t, t1, t2, v1, v2);
|
var value = interpolateLinear(t, t1, t2, v1, v2);
|
||||||
|
|
||||||
@ -202,6 +240,7 @@ class ObjectAnimation extends Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline function interpolateLinear(t: FastFloat, t1: FastFloat, t2: FastFloat, v1: FastFloat, v2: FastFloat): Null<FastFloat> {
|
inline function interpolateLinear(t: FastFloat, t1: FastFloat, t2: FastFloat, v1: FastFloat, v2: FastFloat): Null<FastFloat> {
|
||||||
|
if (t2 == t1) return v1;
|
||||||
var s = (t - t1) / (t2 - t1);
|
var s = (t - t1) / (t2 - t1);
|
||||||
return (1.0 - s) * v1 + s * v2;
|
return (1.0 - s) * v1 + s * v2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,13 +131,20 @@ class Transform {
|
|||||||
dscale = new Vec4().set(1, 1, 1);
|
dscale = new Vec4().set(1, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var _cdLoc = new Vec4();
|
||||||
|
static var _cdRot = new Quat();
|
||||||
|
static var _cdScale = new Vec4();
|
||||||
|
|
||||||
function composeDelta() {
|
function composeDelta() {
|
||||||
// Delta transform
|
// Delta transform: additive location, multiplicative rotation and scale
|
||||||
dloc.addvecs(loc, dloc);
|
_cdLoc.addvecs(loc, dloc);
|
||||||
dscale.addvecs(dscale, scale);
|
_cdScale.x = scale.x * dscale.x;
|
||||||
drot.fromEuler(_deulerX, _deulerY, _deulerZ);
|
_cdScale.y = scale.y * dscale.y;
|
||||||
drot.multquats(rot, drot);
|
_cdScale.z = scale.z * dscale.z;
|
||||||
local.compose(dloc, drot, dscale);
|
_cdRot.fromEuler(_deulerX, _deulerY, _deulerZ);
|
||||||
|
_cdRot.multquats(_cdRot, drot);
|
||||||
|
_cdRot.multquats(rot, _cdRot);
|
||||||
|
local.compose(_cdLoc, _cdRot, _cdScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -38,14 +38,14 @@ class Time {
|
|||||||
static var normalModeLogged: Bool = false;
|
static var normalModeLogged: Bool = false;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
static var lastTime = 0.0;
|
static var lastTime = -1.0;
|
||||||
static var _delta = 0.0;
|
static var _delta = 0.0;
|
||||||
public static var delta(get, never): Float;
|
public static var delta(get, never): Float;
|
||||||
static function get_delta(): Float {
|
static function get_delta(): Float {
|
||||||
return _delta;
|
return _delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
static var lastRenderTime = 0.0;
|
static var lastRenderTime = -1.0;
|
||||||
static var _renderDelta = 0.0;
|
static var _renderDelta = 0.0;
|
||||||
public static var renderDelta(get, never): Float;
|
public static var renderDelta(get, never): Float;
|
||||||
static function get_renderDelta(): Float {
|
static function get_renderDelta(): Float {
|
||||||
@ -79,12 +79,14 @@ class Time {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
_delta = realTime() - lastTime;
|
var now = realTime();
|
||||||
lastTime = realTime();
|
_delta = lastTime < 0.0 ? 0.0 : now - lastTime;
|
||||||
|
lastTime = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function render() {
|
public static function render() {
|
||||||
_renderDelta = realTime() - lastRenderTime;
|
var now = realTime();
|
||||||
lastRenderTime = realTime();
|
_renderDelta = lastRenderTime < 0.0 ? 0.0 : now - lastRenderTime;
|
||||||
|
lastRenderTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,13 +35,13 @@ class AnimActionNode extends LogicNode {
|
|||||||
if(object.animation == null) {
|
if(object.animation == null) {
|
||||||
#if lnx_skin
|
#if lnx_skin
|
||||||
animationBone = object.getBoneAnimation(object.uid);
|
animationBone = object.getBoneAnimation(object.uid);
|
||||||
animationBone.registerAction(property0, sampler);
|
animationBone.registerAction(sampler.action, sampler);
|
||||||
func = sampleBonaAction;
|
func = sampleBonaAction;
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
animationObject = cast(object.animation, ObjectAnimation);
|
animationObject = cast(object.animation, ObjectAnimation);
|
||||||
animationObject.registerAction(property0, sampler);
|
animationObject.registerAction(sampler.action, sampler);
|
||||||
func = sampleObjectAction;
|
func = sampleObjectAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,14 +61,16 @@ class BlendActionNode extends LogicNode {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(factor > 0.95) {
|
if(factor > 0.95) {
|
||||||
|
var action2 = inputs[2].get();
|
||||||
inputs[2].get()(animMats);
|
if (action2 != null) action2(animMats); else inputs[1].get()(animMats);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs[1].get()(animMats);
|
inputs[1].get()(animMats);
|
||||||
inputs[2].get()(tempMats);
|
var action2 = inputs[2].get();
|
||||||
|
if (action2 == null) return;
|
||||||
|
action2(tempMats);
|
||||||
animationBone.blendAction(animMats, tempMats, animMats, factor, boneLayer);
|
animationBone.blendAction(animMats, tempMats, animMats, factor, boneLayer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,9 @@ class OnActionMarkerNode extends LogicNode {
|
|||||||
public var property0: String;
|
public var property0: String;
|
||||||
public var property1: String;
|
public var property1: String;
|
||||||
|
|
||||||
|
var registeredSamplers: Array<iron.object.Animation.ActionSampler> = [];
|
||||||
|
var registeredCallbacks: Map<iron.object.Animation.ActionSampler, Void->Void> = new Map();
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
|
|
||||||
@ -21,9 +24,33 @@ class OnActionMarkerNode extends LogicNode {
|
|||||||
assert(Error, object != null, "Object input cannot be null");
|
assert(Error, object != null, "Object input cannot be null");
|
||||||
var animation = object.animation;
|
var animation = object.animation;
|
||||||
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
||||||
var action = animation.activeActions.get(actionID);
|
if (animation == null) return;
|
||||||
if(action == null) return;
|
var active = animation.activeActions;
|
||||||
animation.notifyOnMarker(action, marker, function() { runOutput(0); });
|
if (active == null) return;
|
||||||
tree.removeUpdate(init);
|
|
||||||
|
var i = registeredSamplers.length;
|
||||||
|
while (i-- > 0) {
|
||||||
|
var alive = false;
|
||||||
|
for (sampler in active) if (sampler == registeredSamplers[i]) {
|
||||||
|
alive = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!alive) {
|
||||||
|
var s = registeredSamplers[i];
|
||||||
|
animation.removeMarker(s, marker, registeredCallbacks.get(s));
|
||||||
|
registeredCallbacks.remove(s);
|
||||||
|
registeredSamplers.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in active.keys()) {
|
||||||
|
var sampler = active.get(key);
|
||||||
|
if (actionID != "" && key != actionID && sampler.action != actionID) continue;
|
||||||
|
if (registeredSamplers.indexOf(sampler) != -1) continue;
|
||||||
|
registeredSamplers.push(sampler);
|
||||||
|
var cb = function() { runOutput(0); };
|
||||||
|
registeredCallbacks.set(sampler, cb);
|
||||||
|
animation.notifyOnMarker(sampler, marker, cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class PlayAnimationTreeNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function playAnim() {
|
function playAnim() {
|
||||||
if (init = false) return;
|
if (init == false) return;
|
||||||
|
|
||||||
init = false;
|
init = false;
|
||||||
tree.removeUpdate(playAnim);
|
tree.removeUpdate(playAnim);
|
||||||
|
|||||||
@ -16,8 +16,10 @@ class RestartActionNode extends LogicNode {
|
|||||||
var animation = object.animation;
|
var animation = object.animation;
|
||||||
|
|
||||||
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
||||||
if(animation.activeActions == null) return;
|
if (animation == null || animation.activeActions == null) return;
|
||||||
animation.activeActions.get(action).restartAction();
|
var sampler = animation.activeActions.get(action);
|
||||||
|
if (sampler == null) return;
|
||||||
|
sampler.restartAction();
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,10 @@ class SetActionPausedNode extends LogicNode {
|
|||||||
var animation = object.animation;
|
var animation = object.animation;
|
||||||
|
|
||||||
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
if (animation == null) animation = object.getBoneAnimation(object.uid);
|
||||||
if(animation.activeActions == null) return;
|
if (animation == null || animation.activeActions == null) return;
|
||||||
animation.activeActions.get(action).paused = paused;
|
var sampler = animation.activeActions.get(action);
|
||||||
|
if (sampler == null) return;
|
||||||
|
sampler.paused = paused;
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Reference: https://github.com/leenkx3d/leenkx_docs/blob/master/dev/renderpath.md
|
// Reference: https://dev.leenkx.com/LeenkxTeam/LNXSDK/src/branch/main/api/src/manual/renderpath.md
|
||||||
package leenkx.renderpath;
|
package leenkx.renderpath;
|
||||||
|
|
||||||
import iron.RenderPath;
|
import iron.RenderPath;
|
||||||
|
|||||||
@ -222,7 +222,7 @@ class RenderPathForward {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || (rp_ssr && !rp_ssr_half) || (rp_water) || (rp_depth_texture))
|
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || (rp_fsr1 && rp_supersampling != 4) || (rp_ssr && !rp_ssr_half) || (rp_water) || (rp_depth_texture))
|
||||||
{
|
{
|
||||||
var t = new RenderTargetRaw();
|
var t = new RenderTargetRaw();
|
||||||
t.name = "bufa";
|
t.name = "bufa";
|
||||||
|
|||||||
@ -139,10 +139,10 @@ class NavAgent extends Trait {
|
|||||||
if (forward.length() > 0.0001) {
|
if (forward.length() > 0.0001) {
|
||||||
forward.normalize();
|
forward.normalize();
|
||||||
|
|
||||||
var xAxis = new Vec4(1, 0, 0, 0);
|
var yAxis = new Vec4(0, -1, 0, 0);
|
||||||
xAxis.applyQuat(targetRot);
|
yAxis.applyQuat(targetRot);
|
||||||
|
|
||||||
var currForward = new Vec4().setFrom(xAxis);
|
var currForward = new Vec4().setFrom(yAxis);
|
||||||
|
|
||||||
var twist = new Quat().fromTo(currForward, forward);
|
var twist = new Quat().fromTo(currForward, forward);
|
||||||
|
|
||||||
|
|||||||
@ -263,8 +263,6 @@ class VehicleWheel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#end
|
|
||||||
|
|
||||||
#elseif lnx_jolt
|
#elseif lnx_jolt
|
||||||
|
|
||||||
typedef VehicleBody = leenkx.trait.physics.jolt.VehicleBody;
|
typedef VehicleBody = leenkx.trait.physics.jolt.VehicleBody;
|
||||||
|
|||||||
@ -4,30 +4,72 @@ package leenkx.trait.internal;
|
|||||||
|
|
||||||
class LoadingScreen {
|
class LoadingScreen {
|
||||||
|
|
||||||
|
public static var bgImageName = 'splash.png';
|
||||||
|
public static var barColor: kha.Color = 0xff2ae0e0;
|
||||||
|
public static var bgColor: kha.Color = 0xff002a35;
|
||||||
|
public static var imageFit = 'Center';
|
||||||
|
public static var showPercentage = true;
|
||||||
|
|
||||||
static var font: kha.Font = null;
|
static var font: kha.Font = null;
|
||||||
static var fontRequested: Bool = false;
|
static var fontRequested: Bool = false;
|
||||||
static var randomColorR: Float = 0;
|
static var bgImage: kha.Image = null;
|
||||||
static var randomColorG: Float = 0;
|
static var bgRequested: Bool = false;
|
||||||
static var randomColorB: Float = 0;
|
|
||||||
static var isInitialized = false;
|
|
||||||
|
|
||||||
public static function render(g: kha.graphics2.Graphics, assetsLoaded: Int, assetsTotal: Int) {
|
public static function render(g: kha.graphics2.Graphics, assetsLoaded: Int, assetsTotal: Int) {
|
||||||
|
|
||||||
if (!isInitialized) {
|
var progress = assetsLoaded / assetsTotal;
|
||||||
randomColorR = Math.random();
|
final barHeight = 18;
|
||||||
randomColorG = Math.random();
|
var w = iron.App.w();
|
||||||
randomColorB = Math.random();
|
var h = iron.App.h();
|
||||||
isInitialized = true;
|
|
||||||
|
g.color = bgColor;
|
||||||
|
g.fillRect(0, 0, w, h);
|
||||||
|
|
||||||
|
if (bgImageName != '' && bgImage == null && !bgRequested) {
|
||||||
|
bgRequested = true;
|
||||||
|
iron.data.Data.getImage(bgImageName, (i: kha.Image) -> {
|
||||||
|
bgImage = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (bgImage != null) {
|
||||||
|
g.color = 0xffffffff;
|
||||||
|
if (imageFit == 'Stretch') {
|
||||||
|
g.drawScaledImage(bgImage, 0, 0, w, h);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var anchor = imageFit;
|
||||||
|
var dw: Float = bgImage.width;
|
||||||
|
var dh: Float = bgImage.height;
|
||||||
|
if (anchor.indexOf('Contain') == 0) {
|
||||||
|
var scale = Math.min(w / dw, h / dh);
|
||||||
|
dw *= scale;
|
||||||
|
dh *= scale;
|
||||||
|
anchor = anchor.length > 7 ? anchor.substr(7) : 'Center';
|
||||||
|
}
|
||||||
|
var x = 0.0;
|
||||||
|
var y = 0.0;
|
||||||
|
switch (anchor) {
|
||||||
|
case 'TopCenter' | 'Center' | 'BottomCenter':
|
||||||
|
x = (w - dw) / 2;
|
||||||
|
case 'TopRight' | 'CenterRight' | 'BottomRight':
|
||||||
|
x = w - dw;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
switch (anchor) {
|
||||||
|
case 'CenterLeft' | 'Center' | 'CenterRight':
|
||||||
|
y = (h - dh) / 2;
|
||||||
|
case 'BottomLeft' | 'BottomCenter' | 'BottomRight':
|
||||||
|
y = h - dh;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
g.drawScaledImage(bgImage, x, y, dw, dh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var progress = assetsLoaded / assetsTotal;
|
g.color = barColor;
|
||||||
var r = progress * randomColorR;
|
g.fillRect(0, h - barHeight, w / assetsTotal * assetsLoaded, barHeight);
|
||||||
var gg = progress * randomColorG;
|
|
||||||
var b = progress * randomColorB;
|
|
||||||
|
|
||||||
g.color = kha.Color.fromFloats(r, gg, b, 1);
|
|
||||||
g.fillRect(0, iron.App.h() - 50, iron.App.w() / assetsTotal * assetsLoaded, 50);
|
|
||||||
|
|
||||||
|
if (showPercentage) {
|
||||||
if (font == null && !fontRequested) {
|
if (font == null && !fontRequested) {
|
||||||
fontRequested = true;
|
fontRequested = true;
|
||||||
iron.data.Data.getFont('font_default.ttf', (f: kha.Font) -> {
|
iron.data.Data.getFont('font_default.ttf', (f: kha.Font) -> {
|
||||||
@ -36,9 +78,10 @@ class LoadingScreen {
|
|||||||
}
|
}
|
||||||
else if (font != null) {
|
else if (font != null) {
|
||||||
g.font = font;
|
g.font = font;
|
||||||
g.fontSize = 50;
|
g.fontSize = 30;
|
||||||
g.color = 0xffffffff;
|
g.color = barColor;
|
||||||
g.drawString(Math.floor(progress * 100) + '%', iron.App.w() / assetsTotal * assetsLoaded - 80, iron.App.h() - 50);
|
g.drawString(Math.floor(progress * 100) + '%', w / assetsTotal * assetsLoaded - 60, h - barHeight - 34);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2724,6 +2724,8 @@ class LeenkxExporter:
|
|||||||
self.extract_projection(o, proj)
|
self.extract_projection(o, proj)
|
||||||
else:
|
else:
|
||||||
self.extract_ortho(o, proj)
|
self.extract_ortho(o, proj)
|
||||||
|
if par_x == par_y:
|
||||||
|
o.pop('aspect', None)
|
||||||
|
|
||||||
o['frustum_culling'] = objref.lnx_frustum_culling
|
o['frustum_culling'] = objref.lnx_frustum_culling
|
||||||
o['clear_color'] = self.get_camera_clear_color()
|
o['clear_color'] = self.get_camera_clear_color()
|
||||||
@ -3124,7 +3126,8 @@ class LeenkxExporter:
|
|||||||
|
|
||||||
def export_world(self):
|
def export_world(self):
|
||||||
"""Exports the world of the current scene."""
|
"""Exports the world of the current scene."""
|
||||||
for world in bpy.data.worlds:
|
world = self.scene.world
|
||||||
|
|
||||||
if world is not None:
|
if world is not None:
|
||||||
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
|
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
|
||||||
|
|
||||||
@ -3397,6 +3400,7 @@ class LeenkxExporter:
|
|||||||
else:
|
else:
|
||||||
self.extract_ortho(out_camera, proj)
|
self.extract_ortho(out_camera, proj)
|
||||||
|
|
||||||
|
if par_x != par_y:
|
||||||
out_camera['aspect'] = effective_aspect
|
out_camera['aspect'] = effective_aspect
|
||||||
|
|
||||||
self.output['camera_datas'].append(out_camera)
|
self.output['camera_datas'].append(out_camera)
|
||||||
|
|||||||
@ -79,7 +79,7 @@ class MergeNode(LnxLogicTreeNode):
|
|||||||
# Recreate all original inputs
|
# Recreate all original inputs
|
||||||
array_nodes[str(id(newnode))] = newnode
|
array_nodes[str(id(newnode))] = newnode
|
||||||
for idx, input in enumerate(self.inputs):
|
for idx, input in enumerate(self.inputs):
|
||||||
bpy.ops.lnx.node_add_input('EXEC_DEFAULT', node_index=str(id(newnode)), socket_type='LnxNodeSocketAction')
|
newnode.add_input('LnxNodeSocketAction', f'Input {idx}')
|
||||||
|
|
||||||
for link in input.links:
|
for link in input.links:
|
||||||
node_tree.links.new(link.from_socket, newnode.inputs[idx])
|
node_tree.links.new(link.from_socket, newnode.inputs[idx])
|
||||||
|
|||||||
@ -278,6 +278,7 @@ def replace_all():
|
|||||||
|
|
||||||
log.error(f'There were errors in the node update procedure, a detailed report has been written to {reportfile}')
|
log.error(f'There were errors in the node update procedure, a detailed report has been written to {reportfile}')
|
||||||
|
|
||||||
|
if not bpy.app.background:
|
||||||
bpy.ops.lnx.show_node_update_errors()
|
bpy.ops.lnx.show_node_update_errors()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -232,7 +232,6 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
|
|||||||
wrd.world_defs += '_EnvClouds'
|
wrd.world_defs += '_EnvClouds'
|
||||||
frag_write_clouds(world, frag)
|
frag_write_clouds(world, frag)
|
||||||
|
|
||||||
if '_EnvSky' in world.world_defs or '_EnvTex' in world.world_defs or '_EnvImg' in world.world_defs or '_EnvCol' in world.world_defs or '_EnvClouds' in world.world_defs:
|
|
||||||
frag.add_uniform('float envmapStrength', link='_envmapStrength')
|
frag.add_uniform('float envmapStrength', link='_envmapStrength')
|
||||||
|
|
||||||
# Clear background color
|
# Clear background color
|
||||||
@ -242,7 +241,6 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
|
|||||||
else:
|
else:
|
||||||
if '_EnvTex' in world.world_defs and '_EnvLDR' in world.world_defs:
|
if '_EnvTex' in world.world_defs and '_EnvLDR' in world.world_defs:
|
||||||
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(2.2));')
|
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(2.2));')
|
||||||
frag.write('fragColor.rgb *= envmapStrength;')
|
|
||||||
|
|
||||||
if '_EnvClouds' in world.world_defs:
|
if '_EnvClouds' in world.world_defs:
|
||||||
frag.write('if (pos.z > 0.0) fragColor.rgb = mix(fragColor.rgb, traceClouds(fragColor.rgb, pos), clamp(pos.z * 5.0, 0, 1));')
|
frag.write('if (pos.z > 0.0) fragColor.rgb = mix(fragColor.rgb, traceClouds(fragColor.rgb, pos), clamp(pos.z * 5.0, 0, 1));')
|
||||||
|
|||||||
@ -3,12 +3,14 @@ import bpy
|
|||||||
import lnx.material.cycles as cycles
|
import lnx.material.cycles as cycles
|
||||||
import lnx.material.mat_state as mat_state
|
import lnx.material.mat_state as mat_state
|
||||||
import lnx.material.make_finalize as make_finalize
|
import lnx.material.make_finalize as make_finalize
|
||||||
|
import lnx.material.make_mesh as make_mesh
|
||||||
import lnx.utils
|
import lnx.utils
|
||||||
|
|
||||||
if lnx.is_reload(__name__):
|
if lnx.is_reload(__name__):
|
||||||
cycles = lnx.reload_module(cycles)
|
cycles = lnx.reload_module(cycles)
|
||||||
mat_state = lnx.reload_module(mat_state)
|
mat_state = lnx.reload_module(mat_state)
|
||||||
make_finalize = lnx.reload_module(make_finalize)
|
make_finalize = lnx.reload_module(make_finalize)
|
||||||
|
make_mesh = lnx.reload_module(make_mesh)
|
||||||
lnx.utils = lnx.reload_module(lnx.utils)
|
lnx.utils = lnx.reload_module(lnx.utils)
|
||||||
else:
|
else:
|
||||||
lnx.enable_reload(__name__)
|
lnx.enable_reload(__name__)
|
||||||
@ -64,14 +66,8 @@ def make(context_id):
|
|||||||
frag.write_attrib(' if (abs(mpos.z) > 1.0) discard;')
|
frag.write_attrib(' if (abs(mpos.z) > 1.0) discard;')
|
||||||
frag.write_attrib(' vec2 texCoord = mpos.xy * 0.5 + 0.5;')
|
frag.write_attrib(' vec2 texCoord = mpos.xy * 0.5 + 0.5;')
|
||||||
|
|
||||||
frag.write('vec3 basecol;')
|
# Declared to prevent compiler errors, but decals currently don't output any emission
|
||||||
frag.write('float roughness;')
|
make_mesh._write_material_attribs_default(frag, parse_opacity=True)
|
||||||
frag.write('float metallic;')
|
|
||||||
frag.write('float occlusion;')
|
|
||||||
frag.write('float specular;')
|
|
||||||
frag.write('float opacity;')
|
|
||||||
frag.write('vec3 emissionCol;') # Declared to prevent compiler errors, but decals currently don't output any emission
|
|
||||||
frag.write('float ior;')
|
|
||||||
cycles.parse(mat_state.nodes, con_decal, vert, frag, geom, tesc, tese)
|
cycles.parse(mat_state.nodes, con_decal, vert, frag, geom, tesc, tese)
|
||||||
|
|
||||||
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));')
|
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));')
|
||||||
|
|||||||
@ -241,8 +241,7 @@ def make(context_id, rpasses, shadowmap=False, shadowmap_transparent=False):
|
|||||||
vert.add_out('vec4 wvpposition')
|
vert.add_out('vec4 wvpposition')
|
||||||
vert.write('wvpposition = gl_Position;')
|
vert.write('wvpposition = gl_Position;')
|
||||||
frag.write('float depth = (wvpposition.z / wvpposition.w) * 0.5 + 0.5;')
|
frag.write('float depth = (wvpposition.z / wvpposition.w) * 0.5 + 0.5;')
|
||||||
frag.write('vec3 color = basecol;')
|
frag.write('vec3 color = mix(vec3(1.0), basecol, opacity);')
|
||||||
frag.write('color *= 1.0 - opacity;')
|
|
||||||
frag.write('fragColor = vec4(color, depth);')
|
frag.write('fragColor = vec4(color, depth);')
|
||||||
|
|
||||||
if parse_opacity and not shadowmap_transparent:
|
if parse_opacity and not shadowmap_transparent:
|
||||||
|
|||||||
@ -398,11 +398,14 @@ def make_forward_mobile(con_mesh):
|
|||||||
return
|
return
|
||||||
|
|
||||||
is_shadows = '_ShadowMap' in wrd.world_defs
|
is_shadows = '_ShadowMap' in wrd.world_defs
|
||||||
|
is_transparent_shadows = '_ShadowMapTransparent' in wrd.world_defs
|
||||||
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
||||||
shadowmap_sun = 'shadowMap'
|
shadowmap_sun = 'shadowMap'
|
||||||
|
shadowmap_sun_tr = 'shadowMapTransparent'
|
||||||
if is_shadows_atlas:
|
if is_shadows_atlas:
|
||||||
is_single_atlas = '_SingleAtlas' in wrd.world_defs
|
is_single_atlas = '_SingleAtlas' in wrd.world_defs
|
||||||
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
||||||
|
shadowmap_sun_tr = 'shadowMapAtlasSunTransparent' if not is_single_atlas else 'shadowMapAtlasTransparent'
|
||||||
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
||||||
frag.write('vec3 direct = vec3(0.0);')
|
frag.write('vec3 direct = vec3(0.0);')
|
||||||
|
|
||||||
@ -417,6 +420,8 @@ def make_forward_mobile(con_mesh):
|
|||||||
vert.write('lightPosition = LWVP * spos;')
|
vert.write('lightPosition = LWVP * spos;')
|
||||||
frag.add_uniform('bool receiveShadow')
|
frag.add_uniform('bool receiveShadow')
|
||||||
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
|
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
|
||||||
|
if is_transparent_shadows:
|
||||||
|
frag.add_uniform(f'sampler2D {shadowmap_sun_tr}')
|
||||||
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
||||||
|
|
||||||
frag.write('if (receiveShadow) {')
|
frag.write('if (receiveShadow) {')
|
||||||
@ -427,14 +432,20 @@ def make_forward_mobile(con_mesh):
|
|||||||
if is_shadows_atlas:
|
if is_shadows_atlas:
|
||||||
frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True)
|
frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True)
|
||||||
frag.write('tileBounds = tileBoundsSunArray[0];')
|
frag.write('tileBounds = tileBoundsSunArray[0];')
|
||||||
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias, opacity != 1.0);')
|
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
|
||||||
|
if is_transparent_shadows:
|
||||||
|
frag.write(f'{shadowmap_sun_tr},')
|
||||||
|
frag.write('eye, wposition + n * shadowsBias * 10, shadowsBias')
|
||||||
|
if is_transparent_shadows:
|
||||||
|
frag.write(', opacity != 1.0')
|
||||||
|
frag.write(');')
|
||||||
else:
|
else:
|
||||||
frag.write('if (lightPosition.w > 0.0) {')
|
frag.write('if (lightPosition.w > 0.0) {')
|
||||||
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
||||||
if '_Legacy' in wrd.world_defs:
|
if '_Legacy' in wrd.world_defs:
|
||||||
frag.write(f' svisibility = float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias, opacity != 1.0);')
|
frag.write(f' svisibility = vec3(float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias));')
|
||||||
else:
|
else:
|
||||||
frag.write(f' svisibility = texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias), opacity != 1.0).r;')
|
frag.write(f' svisibility = vec3(texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias)).r);')
|
||||||
frag.write('}')
|
frag.write('}')
|
||||||
frag.write('}') # receiveShadow
|
frag.write('}') # receiveShadow
|
||||||
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
|
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
|
||||||
|
|||||||
@ -80,9 +80,6 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
|
|
||||||
vert.add_const('float', 'P_NORMAL_FACTOR', str(normal_factor))
|
vert.add_const('float', 'P_NORMAL_FACTOR', str(normal_factor))
|
||||||
|
|
||||||
if (shadowmap and normal_factor != 0.0):
|
|
||||||
vert.add_out('vec3 N')
|
|
||||||
|
|
||||||
|
|
||||||
if ramp_el_len != 0:
|
if ramp_el_len != 0:
|
||||||
vert.add_const('float', 'P_SIZE_OVER_TIME_FACTOR', str(size_over_time_factor))
|
vert.add_const('float', 'P_SIZE_OVER_TIME_FACTOR', str(size_over_time_factor))
|
||||||
@ -156,6 +153,7 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
vert.write('p_velocity.z += (fhash(gl_InstanceID + pd_random + 2 * pd[0][3]) * 2.0 / pd_size - 1.0 / pd_size) * pd[1][3];')
|
vert.write('p_velocity.z += (fhash(gl_InstanceID + pd_random + 2 * pd[0][3]) * 2.0 / pd_size - 1.0 / pd_size) * pd[1][3];')
|
||||||
|
|
||||||
if normal_factor != 0.0:
|
if normal_factor != 0.0:
|
||||||
|
vert.add_uniform('mat3 N', '_normalMatrix')
|
||||||
vert.write('if (P_NORMAL_FACTOR != 0.0) {')
|
vert.write('if (P_NORMAL_FACTOR != 0.0) {')
|
||||||
vert.write(' vec3 local_normal_dir = normalize(ipos);')
|
vert.write(' vec3 local_normal_dir = normalize(ipos);')
|
||||||
vert.write(' vec3 world_emitter_normal = normalize(N * local_normal_dir);')
|
vert.write(' vec3 world_emitter_normal = normalize(N * local_normal_dir);')
|
||||||
|
|||||||
@ -123,6 +123,8 @@ def set_socket_default(socket: NodeSocket, value: Any):
|
|||||||
"""Set the socket's default value if it exists."""
|
"""Set the socket's default value if it exists."""
|
||||||
if isinstance(socket, lnx.logicnode.lnx_sockets.LnxCustomSocket):
|
if isinstance(socket, lnx.logicnode.lnx_sockets.LnxCustomSocket):
|
||||||
if socket.lnx_socket_type != 'NONE':
|
if socket.lnx_socket_type != 'NONE':
|
||||||
|
if socket.lnx_socket_type == 'INT' and isinstance(value, float):
|
||||||
|
value = int(value)
|
||||||
socket.default_value_raw = value
|
socket.default_value_raw = value
|
||||||
|
|
||||||
# Shader-type sockets don't have a default value
|
# Shader-type sockets don't have a default value
|
||||||
|
|||||||
@ -31,7 +31,7 @@ else:
|
|||||||
lnx.enable_reload(__name__)
|
lnx.enable_reload(__name__)
|
||||||
|
|
||||||
# Leenkx version
|
# Leenkx version
|
||||||
lnx_version = '2026.9'
|
lnx_version = '2026.10'
|
||||||
lnx_commit = '$Id: 6b2644d47db169cedd95593497cc283207d23a74 $'
|
lnx_commit = '$Id: 6b2644d47db169cedd95593497cc283207d23a74 $'
|
||||||
|
|
||||||
LNX_TORRENT_DEFAULT_TRACKERS = [
|
LNX_TORRENT_DEFAULT_TRACKERS = [
|
||||||
@ -382,6 +382,27 @@ def init_properties():
|
|||||||
('Hashlink', 'Hashlink', 'Hashlink')],
|
('Hashlink', 'Hashlink', 'Hashlink')],
|
||||||
name="Runtime", description="Runtime to use when launching the game", default='Krom', update=assets.invalidate_shader_cache)
|
name="Runtime", description="Runtime to use when launching the game", default='Krom', update=assets.invalidate_shader_cache)
|
||||||
bpy.types.World.lnx_loadscreen = BoolProperty(name="Loading Screen", description="Show asset loading progress on published builds", default=True)
|
bpy.types.World.lnx_loadscreen = BoolProperty(name="Loading Screen", description="Show asset loading progress on published builds", default=True)
|
||||||
|
bpy.types.World.lnx_loadscreen_mode = EnumProperty(
|
||||||
|
items=[('Image', 'Image', 'Background image with a progress bar'),
|
||||||
|
('Simple', 'Simple', 'Background color with a progress bar')],
|
||||||
|
name="Loading Screen Mode", description="What the loading screen displays behind the progress bar", default='Image', update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_loadscreen_bgcolor = FloatVectorProperty(name="Loading Background Color", description="Background color shown behind the loading bar and image", default=(0.0, 0.165, 0.208, 1.0), min=0.0, max=1.0, subtype='COLOR', size=4, update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_loadscreen_image = StringProperty(name="Loading Screen Image", description="Background image shown behind the loading bar, leave as 'Default' to use the Leenkx logo wallpaper", default="Default", subtype="FILE_PATH", update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_loadscreen_fit = EnumProperty(
|
||||||
|
items=[('TopLeft', 'Top Left', 'Draw the image at its native size anchored to the top left'),
|
||||||
|
('TopCenter', 'Top Center', 'Draw the image at its native size anchored to the top center'),
|
||||||
|
('TopRight', 'Top Right', 'Draw the image at its native size anchored to the top right'),
|
||||||
|
('CenterLeft', 'Center Left', 'Draw the image at its native size anchored to the center left'),
|
||||||
|
('Center', 'Center', 'Draw the image at its native size centered on screen'),
|
||||||
|
('CenterRight', 'Center Right', 'Draw the image at its native size anchored to the center right'),
|
||||||
|
('BottomLeft', 'Bottom Left', 'Draw the image at its native size anchored to the bottom left'),
|
||||||
|
('BottomCenter', 'Bottom Center', 'Draw the image at its native size anchored to the bottom center'),
|
||||||
|
('BottomRight', 'Bottom Right', 'Draw the image at its native size anchored to the bottom right'),
|
||||||
|
('Contain', 'Contain', 'Scale the image to fit inside the screen, preserving aspect ratio'),
|
||||||
|
('Stretch', 'Stretch to Fit', 'Stretch the image to fill the screen, ignoring aspect ratio')],
|
||||||
|
name="Image Fit", description="How the loading screen image is positioned on screen", default='Center', update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_loadscreen_show_percentage = BoolProperty(name="Loading Screen Percentage", description="Draw the loading progress percentage above the progress bar", default=False, update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_loadscreen_color = FloatVectorProperty(name="Loading Bar Color", description="Color of the loading progress bar and percentage text", default=(0.1647, 0.8784, 0.8784, 1.0), min=0.0, max=1.0, subtype='COLOR', size=4)
|
||||||
bpy.types.World.lnx_vsync = BoolProperty(name="VSync", description="Vertical Synchronization", default=True, update=assets.invalidate_compiler_cache)
|
bpy.types.World.lnx_vsync = BoolProperty(name="VSync", description="Vertical Synchronization", default=True, update=assets.invalidate_compiler_cache)
|
||||||
bpy.types.World.lnx_dce = BoolProperty(name="DCE", description="Enable dead code elimination", default=False, update=assets.invalidate_compiler_cache)
|
bpy.types.World.lnx_dce = BoolProperty(name="DCE", description="Enable dead code elimination", default=False, update=assets.invalidate_compiler_cache)
|
||||||
bpy.types.World.lnx_asset_compression = BoolProperty(name="Asset Compression", description="Enable scene data compression with LZ4. Warning: This will slow down export!", default=False, update=assets.invalidate_compiler_cache)
|
bpy.types.World.lnx_asset_compression = BoolProperty(name="Asset Compression", description="Enable scene data compression with LZ4. Warning: This will slow down export!", default=False, update=assets.invalidate_compiler_cache)
|
||||||
@ -763,8 +784,8 @@ def update_leenkx_world():
|
|||||||
# This allows for seamless migration from earlier versions of Leenkx
|
# This allows for seamless migration from earlier versions of Leenkx
|
||||||
for rp in wrd.lnx_rplist: # TODO: deprecated
|
for rp in wrd.lnx_rplist: # TODO: deprecated
|
||||||
if hasattr(rp, 'rp_gi') and rp.rp_gi != 'Off':
|
if hasattr(rp, 'rp_gi') and rp.rp_gi != 'Off':
|
||||||
rp.rp_gi = 'Off'
|
|
||||||
rp.rp_voxels = rp.rp_gi
|
rp.rp_voxels = rp.rp_gi
|
||||||
|
rp.rp_gi = 'Off'
|
||||||
|
|
||||||
# For some breaking changes we need to use a special update
|
# For some breaking changes we need to use a special update
|
||||||
# routine first before regularly replacing nodes
|
# routine first before regularly replacing nodes
|
||||||
|
|||||||
@ -168,7 +168,7 @@ def update_preset(self, context):
|
|||||||
rpdat.rp_compositornodes = True
|
rpdat.rp_compositornodes = True
|
||||||
rpdat.rp_volumetriclight = False
|
rpdat.rp_volumetriclight = False
|
||||||
rpdat.rp_ssao = True
|
rpdat.rp_ssao = True
|
||||||
rpdat.rp_ssgi = True
|
rpdat.rp_ssgi = False
|
||||||
rpdat.lnx_ssrs = True
|
rpdat.lnx_ssrs = True
|
||||||
rpdat.lnx_micro_shadowing = True
|
rpdat.lnx_micro_shadowing = True
|
||||||
rpdat.rp_ssr = True
|
rpdat.rp_ssr = True
|
||||||
|
|||||||
@ -1263,7 +1263,6 @@ class LNX_PT_ProjectFlagsPanel(bpy.types.Panel):
|
|||||||
col = column_with_heading(layout, 'Runtime', align=True)
|
col = column_with_heading(layout, 'Runtime', align=True)
|
||||||
col.prop(wrd, 'lnx_live_patch')
|
col.prop(wrd, 'lnx_live_patch')
|
||||||
col.prop(wrd, 'lnx_stream_scene')
|
col.prop(wrd, 'lnx_stream_scene')
|
||||||
col.prop(wrd, 'lnx_loadscreen')
|
|
||||||
col.prop(wrd, 'lnx_webview')
|
col.prop(wrd, 'lnx_webview')
|
||||||
col.prop(wrd, 'lnx_write_config')
|
col.prop(wrd, 'lnx_write_config')
|
||||||
|
|
||||||
@ -1289,6 +1288,33 @@ class LNX_PT_ProjectFlagsPanel(bpy.types.Panel):
|
|||||||
col.prop(wrd, 'lnx_copy_override')
|
col.prop(wrd, 'lnx_copy_override')
|
||||||
col.operator('lnx.copy_to_bundled', icon='IMAGE_DATA')
|
col.operator('lnx.copy_to_bundled', icon='IMAGE_DATA')
|
||||||
|
|
||||||
|
class LNX_PT_ProjectFlagsLoadscreenPanel(bpy.types.Panel):
|
||||||
|
bl_label = "Loading Screen"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "render"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
bl_parent_id = "LNX_PT_ProjectFlagsPanel"
|
||||||
|
|
||||||
|
def draw_header(self, context):
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
self.layout.prop(wrd, 'lnx_loadscreen', text='')
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
col = layout.column()
|
||||||
|
col.enabled = wrd.lnx_loadscreen
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_mode')
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_bgcolor')
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_color')
|
||||||
|
if wrd.lnx_loadscreen_mode == 'Image':
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_image')
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_fit')
|
||||||
|
col.prop(wrd, 'lnx_loadscreen_show_percentage')
|
||||||
|
|
||||||
class LNX_PT_ProjectFlagsDebugConsolePanel(bpy.types.Panel):
|
class LNX_PT_ProjectFlagsDebugConsolePanel(bpy.types.Panel):
|
||||||
bl_label = "Debug Console"
|
bl_label = "Debug Console"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
@ -3017,9 +3043,9 @@ class LNX_OT_UpdateFileSDK(bpy.types.Operator):
|
|||||||
wrd = bpy.data.worlds['Lnx']
|
wrd = bpy.data.worlds['Lnx']
|
||||||
# This allows for seamless migration from earlier versions of Leenkx
|
# This allows for seamless migration from earlier versions of Leenkx
|
||||||
for rp in wrd.lnx_rplist: # TODO: deprecated
|
for rp in wrd.lnx_rplist: # TODO: deprecated
|
||||||
if rp.rp_gi != 'Off':
|
if hasattr(rp, 'rp_gi') and rp.rp_gi != 'Off':
|
||||||
|
rp.rp_voxels = rp.rp_gi
|
||||||
rp.rp_gi = 'Off'
|
rp.rp_gi = 'Off'
|
||||||
rp.rp_voxelao = True
|
|
||||||
|
|
||||||
# Replace deprecated nodes
|
# Replace deprecated nodes
|
||||||
lnx.logicnode.replacement.replace_all()
|
lnx.logicnode.replacement.replace_all()
|
||||||
@ -3265,6 +3291,7 @@ __REG_CLASSES = (
|
|||||||
LNX_PT_LeenkxExporterWindowsSettingsPanel,
|
LNX_PT_LeenkxExporterWindowsSettingsPanel,
|
||||||
LNX_PT_LeenkxProjectPanel,
|
LNX_PT_LeenkxProjectPanel,
|
||||||
LNX_PT_ProjectFlagsPanel,
|
LNX_PT_ProjectFlagsPanel,
|
||||||
|
LNX_PT_ProjectFlagsLoadscreenPanel,
|
||||||
LNX_PT_ProjectFlagsDebugConsolePanel,
|
LNX_PT_ProjectFlagsDebugConsolePanel,
|
||||||
LNX_PT_ProjectWindowPanel,
|
LNX_PT_ProjectWindowPanel,
|
||||||
LNX_PT_ProjectModulesPanel,
|
LNX_PT_ProjectModulesPanel,
|
||||||
|
|||||||
@ -539,6 +539,12 @@ let project = new Project('""" + lnx.utils.safesrc(wrd.lnx_project_name + '-' +
|
|||||||
|
|
||||||
if wrd.lnx_loadscreen:
|
if wrd.lnx_loadscreen:
|
||||||
assets.add_khafile_def('lnx_loadscreen')
|
assets.add_khafile_def('lnx_loadscreen')
|
||||||
|
custom_loadscreen = os.path.isfile(lnx.utils.get_fp() + '/Sources/' + wrd.lnx_project_package + '/LoadingScreen.hx')
|
||||||
|
if wrd.lnx_loadscreen_mode == 'Image' or custom_loadscreen:
|
||||||
|
loadscreen_image = bpy.path.abspath(wrd.lnx_loadscreen_image) if wrd.lnx_loadscreen_image else ''
|
||||||
|
if loadscreen_image == '' or not os.path.isfile(loadscreen_image):
|
||||||
|
loadscreen_image = os.path.join(sdk_path, 'leenkx', 'blender', 'theme', 'splash.png')
|
||||||
|
khafile.write(add_assets(loadscreen_image.replace('\\', '/'), use_data_dir=use_data_dir, rel_path=do_relpath_sdk))
|
||||||
|
|
||||||
if wrd.lnx_winresize or state.target == 'html5':
|
if wrd.lnx_winresize or state.target == 'html5':
|
||||||
assets.add_khafile_def('lnx_resizable')
|
assets.add_khafile_def('lnx_resizable')
|
||||||
@ -742,6 +748,20 @@ class Main {
|
|||||||
f.write("\n" + content_indent + "leenkx.system.Starter.numAssets = " + str(len(asset_references)) + ";")
|
f.write("\n" + content_indent + "leenkx.system.Starter.numAssets = " + str(len(asset_references)) + ";")
|
||||||
f.write("\n" + content_indent + "leenkx.system.Starter.drawLoading = " + loadscreen_class + ".render;")
|
f.write("\n" + content_indent + "leenkx.system.Starter.drawLoading = " + loadscreen_class + ".render;")
|
||||||
f.write("\n" + content_indent + "leenkx.system.Starter.assets = " + str([os.path.basename(path) for path in asset_references if 'Bundled' in path]) + ";")
|
f.write("\n" + content_indent + "leenkx.system.Starter.assets = " + str([os.path.basename(path) for path in asset_references if 'Bundled' in path]) + ";")
|
||||||
|
if loadscreen_class == 'leenkx.trait.internal.LoadingScreen':
|
||||||
|
if wrd.lnx_loadscreen_mode == 'Image':
|
||||||
|
li = bpy.path.abspath(wrd.lnx_loadscreen_image) if wrd.lnx_loadscreen_image else ''
|
||||||
|
if li == '' or not os.path.isfile(li):
|
||||||
|
li = os.path.join(lnx.utils.get_sdk_path(), 'leenkx', 'blender', 'theme', 'splash.png')
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".bgImageName = '" + os.path.basename(li) + "';")
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".imageFit = '" + wrd.lnx_loadscreen_fit + "';")
|
||||||
|
else:
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".bgImageName = '';")
|
||||||
|
c = wrd.lnx_loadscreen_color
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".barColor = " + hex(0xff000000 + (int(c[0] * 255) << 16) + (int(c[1] * 255) << 8) + int(c[2] * 255)) + ";")
|
||||||
|
c = wrd.lnx_loadscreen_bgcolor
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".bgColor = " + hex(0xff000000 + (int(c[0] * 255) << 16) + (int(c[1] * 255) << 8) + int(c[2] * 255)) + ";")
|
||||||
|
f.write("\n" + content_indent + loadscreen_class + ".showPercentage = " + ('true' if wrd.lnx_loadscreen_show_percentage else 'false') + ";")
|
||||||
|
|
||||||
scenes_list = [f"'{scene.name}'" for scene in bpy.data.scenes if getattr(scene, 'lnx_export', True)]
|
scenes_list = [f"'{scene.name}'" for scene in bpy.data.scenes if getattr(scene, 'lnx_export', True)]
|
||||||
scenes_array = "[" + ", ".join(scenes_list) + "]"
|
scenes_array = "[" + ", ".join(scenes_list) + "]"
|
||||||
@ -819,7 +839,7 @@ def write_indexhtml(w, h, is_publish):
|
|||||||
<title>"""+html.escape( wrd.lnx_project_name)+"""</title>
|
<title>"""+html.escape( wrd.lnx_project_name)+"""</title>
|
||||||
"""+icon+"""
|
"""+icon+"""
|
||||||
</head>
|
</head>
|
||||||
<body style='margin: 0; padding: 0'>
|
<body style='margin: 0; padding: 0; background: #001A22'>
|
||||||
""")
|
""")
|
||||||
if rpdat.rp_stereo or wrd.lnx_winmode == 'Fullscreen':
|
if rpdat.rp_stereo or wrd.lnx_winmode == 'Fullscreen':
|
||||||
f.write("""
|
f.write("""
|
||||||
|
|||||||
BIN
leenkx/blender/theme/Leenkx_Logo.png
Normal file
BIN
leenkx/blender/theme/Leenkx_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
Reference in New Issue
Block a user