Merge branch 'master' into live-patch
This commit is contained in:
commit
cca82a69bf
|
@ -21,7 +21,9 @@
|
||||||
uniform sampler2D gbufferD;
|
uniform sampler2D gbufferD;
|
||||||
uniform sampler2D gbuffer0;
|
uniform sampler2D gbuffer0;
|
||||||
uniform sampler2D gbuffer1;
|
uniform sampler2D gbuffer1;
|
||||||
|
#ifdef _gbuffer2
|
||||||
uniform sampler2D gbuffer2;
|
uniform sampler2D gbuffer2;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _VoxelAOvar
|
#ifdef _VoxelAOvar
|
||||||
uniform sampler3D voxels;
|
uniform sampler3D voxels;
|
||||||
|
@ -206,7 +208,9 @@ void main() {
|
||||||
vec3 v = normalize(eye - p);
|
vec3 v = normalize(eye - p);
|
||||||
float dotNV = max(dot(n, v), 0.0);
|
float dotNV = max(dot(n, v), 0.0);
|
||||||
|
|
||||||
|
#ifdef _gbuffer2
|
||||||
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MicroShadowing
|
#ifdef _MicroShadowing
|
||||||
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
|
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
|
||||||
|
@ -221,14 +225,16 @@ void main() {
|
||||||
|
|
||||||
vec3 envl = shIrradiance(n, shirr);
|
vec3 envl = shIrradiance(n, shirr);
|
||||||
|
|
||||||
if (g2.b < 0.5) {
|
#ifdef _gbuffer2
|
||||||
envl = envl;
|
if (g2.b < 0.5) {
|
||||||
} else {
|
envl = envl;
|
||||||
envl = vec3(1.0);
|
} else {
|
||||||
}
|
envl = vec3(1.0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _EnvTex
|
#ifdef _EnvTex
|
||||||
envl /= PI;
|
envl /= PI;
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
vec3 envl = vec3(1.0);
|
vec3 envl = vec3(1.0);
|
||||||
|
|
|
@ -60,7 +60,7 @@ float random(vec2 coords) {
|
||||||
|
|
||||||
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
||||||
vec2 coords = vec2(
|
vec2 coords = vec2(
|
||||||
sqrt(height * (1 / nishita_atmo_radius)),
|
sqrt(height * (1 / nishita_atmo_radius)),
|
||||||
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
||||||
);
|
);
|
||||||
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
||||||
|
@ -125,7 +125,7 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||||
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
||||||
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
||||||
float sunTheta = acos(dot(normalize(iPos), normalize(pSun)));
|
float sunTheta = acos(dot(normalize(iPos), normalize(pSun)));
|
||||||
vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta);// * vec3(14000000 / 255, 14000000 / 255, 2000000 / 255);
|
vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta);
|
||||||
|
|
||||||
// Apply dithering to reduce visible banding
|
// Apply dithering to reduce visible banding
|
||||||
jODepth += mix(-1000, 1000, random(r.xy));
|
jODepth += mix(-1000, 1000, random(r.xy));
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package armory.logicnode;
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.math.Quat;
|
||||||
|
import iron.math.Vec4;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
import iron.object.BoneAnimation;
|
import iron.object.BoneAnimation;
|
||||||
import iron.math.Mat4;
|
import iron.math.Mat4;
|
||||||
|
@ -26,19 +28,28 @@ class BoneFKNode extends LogicNode {
|
||||||
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
||||||
if (anim == null) anim = object.getParentArmature(object.name);
|
if (anim == null) anim = object.getParentArmature(object.name);
|
||||||
|
|
||||||
// Manipulating bone in world space
|
// Get bone in armature
|
||||||
var bone = anim.getBone(boneName);
|
var bone = anim.getBone(boneName);
|
||||||
m = anim.getBoneMat(bone);
|
|
||||||
w = anim.getAbsMat(bone);
|
|
||||||
|
|
||||||
function moveBone() {
|
function moveBone() {
|
||||||
m.setFrom(w);
|
|
||||||
m.multmat(transform);
|
var t2 = Mat4.identity();
|
||||||
iw.getInverse(w);
|
var loc= new Vec4();
|
||||||
m.multmat(iw);
|
var rot = new Quat();
|
||||||
|
var scl = new Vec4();
|
||||||
|
|
||||||
// anim.removeUpdate(moveBone);
|
//Set scale to Armature scale. Bone scaling not yet implemented
|
||||||
// notified = false;
|
t2.setFrom(transform);
|
||||||
|
t2.decompose(loc, rot, scl);
|
||||||
|
scl = object.transform.world.getScale();
|
||||||
|
t2.compose(loc, rot, scl);
|
||||||
|
|
||||||
|
//Set the bone local transform from world transform
|
||||||
|
anim.setBoneMatFromWorldMat(t2, bone);
|
||||||
|
|
||||||
|
//Remove this method from animation loop after FK
|
||||||
|
anim.removeUpdate(moveBone);
|
||||||
|
notified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notified) {
|
if (!notified) {
|
||||||
|
|
|
@ -7,6 +7,13 @@ import iron.math.Vec4;
|
||||||
class BoneIKNode extends LogicNode {
|
class BoneIKNode extends LogicNode {
|
||||||
|
|
||||||
var goal: Vec4;
|
var goal: Vec4;
|
||||||
|
var pole: Vec4;
|
||||||
|
var poleEnabled: Bool;
|
||||||
|
var chainLength: Int;
|
||||||
|
var maxIterartions: Int;
|
||||||
|
var precision: Float;
|
||||||
|
var rollAngle: Float;
|
||||||
|
|
||||||
var notified = false;
|
var notified = false;
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
|
@ -19,6 +26,12 @@ class BoneIKNode extends LogicNode {
|
||||||
var object: Object = inputs[1].get();
|
var object: Object = inputs[1].get();
|
||||||
var boneName: String = inputs[2].get();
|
var boneName: String = inputs[2].get();
|
||||||
goal = inputs[3].get();
|
goal = inputs[3].get();
|
||||||
|
poleEnabled = inputs[4].get();
|
||||||
|
pole = inputs[5].get();
|
||||||
|
chainLength = inputs[6].get();
|
||||||
|
maxIterartions = inputs[7].get();
|
||||||
|
precision = inputs[8].get();
|
||||||
|
rollAngle = inputs[9].get();
|
||||||
|
|
||||||
if (object == null || goal == null) return;
|
if (object == null || goal == null) return;
|
||||||
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
||||||
|
@ -26,11 +39,15 @@ class BoneIKNode extends LogicNode {
|
||||||
|
|
||||||
var bone = anim.getBone(boneName);
|
var bone = anim.getBone(boneName);
|
||||||
|
|
||||||
function solveBone() {
|
if(! poleEnabled) pole = null;
|
||||||
anim.solveIK(bone, goal);
|
|
||||||
|
|
||||||
// anim.removeUpdate(solveBone);
|
function solveBone() {
|
||||||
// notified = false;
|
//Solve IK
|
||||||
|
anim.solveIK(bone, goal, precision, maxIterartions, chainLength, pole, rollAngle);
|
||||||
|
|
||||||
|
//Remove this method from animation loop after IK
|
||||||
|
anim.removeUpdate(solveBone);
|
||||||
|
notified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notified) {
|
if (!notified) {
|
||||||
|
|
|
@ -19,9 +19,9 @@ class CompareNode extends LogicNode {
|
||||||
|
|
||||||
switch (property0) {
|
switch (property0) {
|
||||||
case "Equal":
|
case "Equal":
|
||||||
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2;
|
cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
|
||||||
case "Almost Equal":
|
case "Almost Equal":
|
||||||
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
|
cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
|
||||||
case "Greater":
|
case "Greater":
|
||||||
cond = v1 > v2;
|
cond = v1 > v2;
|
||||||
case "Greater Equal":
|
case "Greater Equal":
|
||||||
|
|
|
@ -18,9 +18,9 @@ class GateNode extends LogicNode {
|
||||||
|
|
||||||
switch (property0) {
|
switch (property0) {
|
||||||
case "Equal":
|
case "Equal":
|
||||||
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2;
|
cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
|
||||||
case "Almost Equal":
|
case "Almost Equal":
|
||||||
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
|
cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
|
||||||
case "Greater":
|
case "Greater":
|
||||||
cond = v1 > v2;
|
cond = v1 > v2;
|
||||||
case "Greater Equal":
|
case "Greater Equal":
|
||||||
|
|
31
Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx
Normal file
31
Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.object.Object;
|
||||||
|
import iron.object.BoneAnimation;
|
||||||
|
|
||||||
|
class GetBoneFkIkOnlyNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Bool {
|
||||||
|
#if arm_skin
|
||||||
|
|
||||||
|
var object: Object = inputs[0].get();
|
||||||
|
var boneName: String = inputs[1].get();
|
||||||
|
|
||||||
|
if (object == null) return null;
|
||||||
|
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
||||||
|
if (anim == null) anim = object.getParentArmature(object.name);
|
||||||
|
|
||||||
|
// Get bone in armature
|
||||||
|
var bone = anim.getBone(boneName);
|
||||||
|
|
||||||
|
//Get bone transform in world coordinates
|
||||||
|
return bone.is_ik_fk_only;
|
||||||
|
|
||||||
|
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
30
Sources/armory/logicnode/GetBoneTransformNode.hx
Normal file
30
Sources/armory/logicnode/GetBoneTransformNode.hx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.object.Object;
|
||||||
|
import iron.object.BoneAnimation;
|
||||||
|
import iron.math.Mat4;
|
||||||
|
|
||||||
|
class GetBoneTransformNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Mat4 {
|
||||||
|
#if arm_skin
|
||||||
|
|
||||||
|
var object: Object = inputs[0].get();
|
||||||
|
var boneName: String = inputs[1].get();
|
||||||
|
|
||||||
|
if (object == null) return null;
|
||||||
|
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
||||||
|
if (anim == null) anim = object.getParentArmature(object.name);
|
||||||
|
|
||||||
|
// Get bone in armature
|
||||||
|
var bone = anim.getBone(boneName);
|
||||||
|
|
||||||
|
return anim.getAbsWorldMat(bone);
|
||||||
|
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
33
Sources/armory/logicnode/GetGamepadStartedNode.hx
Normal file
33
Sources/armory/logicnode/GetGamepadStartedNode.hx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.system.Input;
|
||||||
|
|
||||||
|
class GetGamepadStartedNode extends LogicNode {
|
||||||
|
|
||||||
|
var buttonStarted: Null<String>;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var g = Input.getGamepad(inputs[0].get());
|
||||||
|
|
||||||
|
buttonStarted = null;
|
||||||
|
|
||||||
|
for (b in Gamepad.buttons) {
|
||||||
|
if (g.started(b)) {
|
||||||
|
buttonStarted = b;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonStarted != null) {
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int) {
|
||||||
|
return buttonStarted;
|
||||||
|
}
|
||||||
|
}
|
24
Sources/armory/logicnode/GetInputMapKeyNode.hx
Normal file
24
Sources/armory/logicnode/GetInputMapKeyNode.hx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import armory.system.InputMap;
|
||||||
|
|
||||||
|
class GetInputMapKeyNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
var inputMap = inputs[0].get();
|
||||||
|
var key = inputs[1].get();
|
||||||
|
|
||||||
|
var k = InputMap.getInputMapKey(inputMap, key);
|
||||||
|
|
||||||
|
if (k != null) {
|
||||||
|
if (from == 0) return k.scale;
|
||||||
|
else if (from == 1) return k.deadzone;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
32
Sources/armory/logicnode/GetKeyboardStartedNode.hx
Normal file
32
Sources/armory/logicnode/GetKeyboardStartedNode.hx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.system.Input;
|
||||||
|
|
||||||
|
class GetKeyboardStartedNode extends LogicNode {
|
||||||
|
|
||||||
|
var kb = Input.getKeyboard();
|
||||||
|
var keyStarted: Null<String>;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
keyStarted = null;
|
||||||
|
|
||||||
|
for (k in Keyboard.keys) {
|
||||||
|
if (kb.started(k)) {
|
||||||
|
keyStarted = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyStarted != null) {
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int) {
|
||||||
|
return keyStarted;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,22 @@ class GetLocationNode extends LogicNode {
|
||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
var object: Object = inputs[0].get();
|
var object: Object = inputs[0].get();
|
||||||
|
var relative: Bool = inputs[1].get();
|
||||||
|
|
||||||
if (object == null) return null;
|
if (object == null) return null;
|
||||||
|
|
||||||
return object.transform.world.getLoc();
|
var loc = object.transform.world.getLoc();
|
||||||
|
|
||||||
|
if (relative && object.parent != null) {
|
||||||
|
loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence
|
||||||
|
|
||||||
|
// Convert loc to parent local space
|
||||||
|
var dotX = loc.dot(object.parent.transform.right());
|
||||||
|
var dotY = loc.dot(object.parent.transform.look());
|
||||||
|
var dotZ = loc.dot(object.parent.transform.up());
|
||||||
|
loc.set(dotX, dotY, dotZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
Sources/armory/logicnode/GetMouseStartedNode.hx
Normal file
32
Sources/armory/logicnode/GetMouseStartedNode.hx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.system.Input;
|
||||||
|
|
||||||
|
class GetMouseStartedNode extends LogicNode {
|
||||||
|
|
||||||
|
var m = Input.getMouse();
|
||||||
|
var buttonStarted: Null<String>;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
buttonStarted = null;
|
||||||
|
|
||||||
|
for (b in Mouse.buttons) {
|
||||||
|
if (m.started(b)) {
|
||||||
|
buttonStarted = b;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonStarted != null) {
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int) {
|
||||||
|
return buttonStarted;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,12 +16,12 @@ class GetTraitNameNode extends LogicNode {
|
||||||
case 0: {
|
case 0: {
|
||||||
// Check CanvasScript
|
// Check CanvasScript
|
||||||
var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript");
|
var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript");
|
||||||
if (Std.is(trait, cname)) {
|
if (Std.isOfType(trait, cname)) {
|
||||||
return trait.cnvName;
|
return trait.cnvName;
|
||||||
}
|
}
|
||||||
// Check WasmScript
|
// Check WasmScript
|
||||||
var cname = cast Type.resolveClass("armory.trait.internal.WasmScript");
|
var cname = cast Type.resolveClass("armory.trait.internal.WasmScript");
|
||||||
if (Std.is(trait, cname)) {
|
if (Std.isOfType(trait, cname)) {
|
||||||
return trait.wasmName;
|
return trait.wasmName;
|
||||||
}
|
}
|
||||||
// Other
|
// Other
|
||||||
|
|
|
@ -2,11 +2,31 @@ package armory.logicnode;
|
||||||
|
|
||||||
class MergeNode extends LogicNode {
|
class MergeNode extends LogicNode {
|
||||||
|
|
||||||
|
/** Execution mode. **/
|
||||||
|
public var property0: String;
|
||||||
|
|
||||||
|
var lastInputIndex = -1;
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
|
tree.notifyOnLateUpdate(lateUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
|
// Check if there already were executions on the same frame
|
||||||
|
if (lastInputIndex != -1 && property0 == "once_per_frame") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastInputIndex = from;
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
return lastInputIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lateUpdate() {
|
||||||
|
lastInputIndex = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import armory.trait.internal.CanvasScript;
|
||||||
import iron.Scene;
|
import iron.Scene;
|
||||||
|
|
||||||
#if arm_ui
|
#if arm_ui
|
||||||
import zui.Canvas.Anchor;
|
import armory.ui.Canvas.Anchor;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
class OnCanvasElementNode extends LogicNode {
|
class OnCanvasElementNode extends LogicNode {
|
||||||
|
|
35
Sources/armory/logicnode/OnInputMapNode.hx
Normal file
35
Sources/armory/logicnode/OnInputMapNode.hx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import armory.system.InputMap;
|
||||||
|
|
||||||
|
class OnInputMapNode extends LogicNode {
|
||||||
|
|
||||||
|
var inputMap: Null<InputMap>;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
|
||||||
|
tree.notifyOnUpdate(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var i = inputs[0].get();
|
||||||
|
|
||||||
|
inputMap = InputMap.getInputMap(i);
|
||||||
|
|
||||||
|
if (inputMap != null) {
|
||||||
|
if (inputMap.started()) {
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputMap.released()) {
|
||||||
|
runOutput(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
if (from == 2) return inputMap.value();
|
||||||
|
else return inputMap.lastKeyPressed;
|
||||||
|
}
|
||||||
|
}
|
22
Sources/armory/logicnode/OncePerFrameNode.hx
Normal file
22
Sources/armory/logicnode/OncePerFrameNode.hx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
class OncePerFrameNode extends LogicNode {
|
||||||
|
|
||||||
|
var c = false;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
tree.notifyOnUpdate(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
if(c) {
|
||||||
|
c = false;
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
c = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ class PauseTraitNode extends LogicNode {
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var trait: Dynamic = inputs[1].get();
|
var trait: Dynamic = inputs[1].get();
|
||||||
if (trait == null || !Std.is(trait, LogicTree)) return;
|
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
|
||||||
|
|
||||||
cast(trait, LogicTree).pause();
|
cast(trait, LogicTree).pause();
|
||||||
|
|
||||||
|
|
19
Sources/armory/logicnode/RemoveInputMapKeyNode.hx
Normal file
19
Sources/armory/logicnode/RemoveInputMapKeyNode.hx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import armory.system.InputMap;
|
||||||
|
|
||||||
|
class RemoveInputMapKeyNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var inputMap = inputs[1].get();
|
||||||
|
var key = inputs[2].get();
|
||||||
|
|
||||||
|
if (InputMap.removeInputMapKey(inputMap, key)) {
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ class ResumeTraitNode extends LogicNode {
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var trait: Dynamic = inputs[1].get();
|
var trait: Dynamic = inputs[1].get();
|
||||||
if (trait == null || !Std.is(trait, LogicTree)) return;
|
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
|
||||||
|
|
||||||
cast(trait, LogicTree).resume();
|
cast(trait, LogicTree).resume();
|
||||||
|
|
||||||
|
|
37
Sources/armory/logicnode/SelectNode.hx
Normal file
37
Sources/armory/logicnode/SelectNode.hx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
class SelectNode extends LogicNode {
|
||||||
|
|
||||||
|
/** Execution mode. **/
|
||||||
|
public var property0: String;
|
||||||
|
|
||||||
|
var value: Dynamic = null;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
// Get value according to the activated input (run() can only be called
|
||||||
|
// if the execution mode is from_input).
|
||||||
|
value = inputs[from + Std.int(inputs.length / 2)].get();
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function get(from: Int): Dynamic {
|
||||||
|
if (property0 == "from_index") {
|
||||||
|
var index = inputs[0].get() + 2;
|
||||||
|
|
||||||
|
// Return default value for invalid index
|
||||||
|
if (index < 2 || index >= inputs.length) {
|
||||||
|
return inputs[1].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputs[index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from_input
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
35
Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx
Normal file
35
Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.math.Quat;
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.object.Object;
|
||||||
|
import iron.object.BoneAnimation;
|
||||||
|
|
||||||
|
class SetBoneFkIkOnlyNode extends LogicNode {
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
#if arm_skin
|
||||||
|
|
||||||
|
var object: Object = inputs[1].get();
|
||||||
|
var boneName: String = inputs[2].get();
|
||||||
|
var fk_ik_only: Bool = inputs[3].get();
|
||||||
|
|
||||||
|
if (object == null) return;
|
||||||
|
var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null;
|
||||||
|
if (anim == null) anim = object.getParentArmature(object.name);
|
||||||
|
|
||||||
|
// Get bone in armature
|
||||||
|
var bone = anim.getBone(boneName);
|
||||||
|
|
||||||
|
//Set bone animated by FK or IK only
|
||||||
|
bone.is_ik_fk_only = fk_ik_only;
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
46
Sources/armory/logicnode/SetInputMapKeyNode.hx
Normal file
46
Sources/armory/logicnode/SetInputMapKeyNode.hx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import armory.system.InputMap;
|
||||||
|
|
||||||
|
class SetInputMapKeyNode extends LogicNode {
|
||||||
|
|
||||||
|
public var property0: String;
|
||||||
|
|
||||||
|
public function new(tree: LogicTree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run(from: Int) {
|
||||||
|
var inputMap = inputs[1].get();
|
||||||
|
var key = inputs[2].get();
|
||||||
|
var scale = inputs[3].get();
|
||||||
|
var deadzone = inputs[4].get();
|
||||||
|
var index = inputs[5].get();
|
||||||
|
|
||||||
|
var i = InputMap.getInputMap(inputMap);
|
||||||
|
|
||||||
|
if (i == null) {
|
||||||
|
i = InputMap.addInputMap(inputMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var k = InputMap.getInputMapKey(inputMap, key);
|
||||||
|
|
||||||
|
if (k == null) {
|
||||||
|
switch(property0) {
|
||||||
|
case "keyboard": k = i.addKeyboard(key, scale);
|
||||||
|
case "mouse": k = i.addMouse(key, scale, deadzone);
|
||||||
|
case "gamepad": {
|
||||||
|
k = i.addGamepad(key, scale, deadzone);
|
||||||
|
k.setIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
k.scale = scale;
|
||||||
|
k.deadzone = deadzone;
|
||||||
|
k.setIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
runOutput(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,9 +13,21 @@ class SetLocationNode extends LogicNode {
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var object: Object = inputs[1].get();
|
var object: Object = inputs[1].get();
|
||||||
var vec: Vec4 = inputs[2].get();
|
var vec: Vec4 = inputs[2].get();
|
||||||
|
var relative: Bool = inputs[3].get();
|
||||||
|
|
||||||
if (object == null || vec == null) return;
|
if (object == null || vec == null) return;
|
||||||
|
|
||||||
|
if (!relative && object.parent != null) {
|
||||||
|
var loc = vec.clone();
|
||||||
|
loc.sub(object.parent.transform.world.getLoc()); // Remove parent location influence
|
||||||
|
|
||||||
|
// Convert vec to parent local space
|
||||||
|
var dotX = loc.dot(object.parent.transform.right());
|
||||||
|
var dotY = loc.dot(object.parent.transform.look());
|
||||||
|
var dotZ = loc.dot(object.parent.transform.up());
|
||||||
|
vec.set(dotX, dotY, dotZ);
|
||||||
|
}
|
||||||
|
|
||||||
object.transform.loc.setFrom(vec);
|
object.transform.loc.setFrom(vec);
|
||||||
object.transform.buildMatrix();
|
object.transform.buildMatrix();
|
||||||
|
|
||||||
|
@ -26,4 +38,4 @@ class SetLocationNode extends LogicNode {
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,40 +1,40 @@
|
||||||
package armory.logicnode;
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.Scene;
|
||||||
import iron.data.MaterialData;
|
import iron.data.MaterialData;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
import armory.trait.internal.UniformsManager;
|
||||||
|
|
||||||
class SetMaterialImageParamNode extends LogicNode {
|
class SetMaterialImageParamNode extends LogicNode {
|
||||||
|
|
||||||
static var registered = false;
|
|
||||||
static var map = new Map<MaterialData, Map<String, kha.Image>>();
|
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
if (!registered) {
|
|
||||||
registered = true;
|
|
||||||
iron.object.Uniforms.externalTextureLinks.push(textureLink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var mat = inputs[1].get();
|
var perObject: Null<Bool>;
|
||||||
if (mat == null) return;
|
|
||||||
var entry = map.get(mat);
|
var object = inputs[1].get();
|
||||||
if (entry == null) {
|
if(object == null) return;
|
||||||
entry = new Map();
|
|
||||||
map.set(mat, entry);
|
perObject = inputs[2].get();
|
||||||
|
if(perObject == null) perObject = false;
|
||||||
|
|
||||||
|
var mat = inputs[3].get();
|
||||||
|
if(mat == null) return;
|
||||||
|
|
||||||
|
if(! perObject){
|
||||||
|
UniformsManager.removeObjectFromMap(object, Texture);
|
||||||
|
object = Scene.active.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
iron.data.Data.getImage(inputs[3].get(), function(image: kha.Image) {
|
var img = inputs[5].get();
|
||||||
entry.set(inputs[2].get(), image); // Node name, value
|
if(img == null) return;
|
||||||
|
iron.data.Data.getImage(img, function(image: kha.Image) {
|
||||||
|
UniformsManager.setTextureValue(mat, object, inputs[4].get(), image);
|
||||||
});
|
});
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
|
|
||||||
if (mat == null) return null;
|
|
||||||
var entry = map.get(mat);
|
|
||||||
if (entry == null) return null;
|
|
||||||
return entry.get(link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,35 @@
|
||||||
package armory.logicnode;
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.Scene;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
import iron.data.MaterialData;
|
import iron.data.MaterialData;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
import armory.trait.internal.UniformsManager;
|
||||||
|
|
||||||
class SetMaterialRgbParamNode extends LogicNode {
|
class SetMaterialRgbParamNode extends LogicNode {
|
||||||
|
|
||||||
static var registered = false;
|
|
||||||
static var map = new Map<MaterialData, Map<String, Vec4>>();
|
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
if (!registered) {
|
|
||||||
registered = true;
|
|
||||||
iron.object.Uniforms.externalVec3Links.push(vec3Link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var mat = inputs[1].get();
|
var perObject: Null<Bool>;
|
||||||
if (mat == null) return;
|
|
||||||
var entry = map.get(mat);
|
var object = inputs[1].get();
|
||||||
if (entry == null) {
|
if(object == null) return;
|
||||||
entry = new Map();
|
|
||||||
map.set(mat, entry);
|
perObject = inputs[2].get();
|
||||||
|
if(perObject == null) perObject = false;
|
||||||
|
|
||||||
|
var mat = inputs[3].get();
|
||||||
|
if(mat == null) return;
|
||||||
|
|
||||||
|
if(! perObject){
|
||||||
|
UniformsManager.removeObjectFromMap(object, Vector);
|
||||||
|
object = Scene.active.root;
|
||||||
}
|
}
|
||||||
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
|
|
||||||
|
UniformsManager.setVec3Value(mat, object, inputs[4].get(), inputs[5].get());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
|
|
||||||
if (mat == null) return null;
|
|
||||||
var entry = map.get(mat);
|
|
||||||
if (entry == null) return null;
|
|
||||||
return entry.get(link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,35 @@
|
||||||
package armory.logicnode;
|
package armory.logicnode;
|
||||||
|
|
||||||
|
import iron.Scene;
|
||||||
import iron.data.MaterialData;
|
import iron.data.MaterialData;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
import armory.trait.internal.UniformsManager;
|
||||||
|
|
||||||
class SetMaterialValueParamNode extends LogicNode {
|
class SetMaterialValueParamNode extends LogicNode {
|
||||||
|
|
||||||
static var registered = false;
|
|
||||||
static var map = new Map<MaterialData, Map<String, Null<kha.FastFloat>>>();
|
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
if (!registered) {
|
|
||||||
registered = true;
|
|
||||||
iron.object.Uniforms.externalFloatLinks.push(floatLink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override function run(from: Int) {
|
override function run(from: Int) {
|
||||||
var mat = inputs[1].get();
|
var perObject: Null<Bool>;
|
||||||
if (mat == null) return;
|
|
||||||
var entry = map.get(mat);
|
var object = inputs[1].get();
|
||||||
if (entry == null) {
|
if(object == null) return;
|
||||||
entry = new Map();
|
|
||||||
map.set(mat, entry);
|
perObject = inputs[2].get();
|
||||||
|
if(perObject == null) perObject = false;
|
||||||
|
|
||||||
|
var mat = inputs[3].get();
|
||||||
|
if(mat == null) return;
|
||||||
|
|
||||||
|
if(! perObject){
|
||||||
|
UniformsManager.removeObjectFromMap(object, Float);
|
||||||
|
object = Scene.active.root;
|
||||||
}
|
}
|
||||||
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
|
|
||||||
|
UniformsManager.setFloatValue(mat, object, inputs[4].get(), inputs[5].get());
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
|
|
||||||
if (mat == null) return null;
|
|
||||||
var entry = map.get(mat);
|
|
||||||
if (entry == null) return null;
|
|
||||||
return entry.get(link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ class SetParentNode extends LogicNode {
|
||||||
|
|
||||||
var parent: Object;
|
var parent: Object;
|
||||||
var isUnparent = false;
|
var isUnparent = false;
|
||||||
if (Std.is(inputs[2].fromNode, ObjectNode)) {
|
|
||||||
|
if (Std.isOfType(inputs[2].fromNode, ObjectNode)) {
|
||||||
var parentNode = cast(inputs[2].fromNode, ObjectNode);
|
var parentNode = cast(inputs[2].fromNode, ObjectNode);
|
||||||
isUnparent = parentNode.objectName == "";
|
isUnparent = parentNode.objectName == "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ class SetTraitPausedNode extends LogicNode {
|
||||||
var trait: Dynamic = inputs[1].get();
|
var trait: Dynamic = inputs[1].get();
|
||||||
var paused: Bool = inputs[2].get();
|
var paused: Bool = inputs[2].get();
|
||||||
|
|
||||||
if (trait == null || !Std.is(trait, LogicTree)) return;
|
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
|
||||||
|
|
||||||
paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume();
|
paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume();
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ package armory.logicnode;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
|
|
||||||
using armory.object.TransformExtension;
|
|
||||||
|
|
||||||
class VectorToObjectOrientationNode extends LogicNode {
|
class VectorToObjectOrientationNode extends LogicNode {
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
|
@ -18,7 +16,7 @@ class VectorToObjectOrientationNode extends LogicNode {
|
||||||
|
|
||||||
if (object == null || vec == null) return null;
|
if (object == null || vec == null) return null;
|
||||||
|
|
||||||
return object.transform.worldVecToOrientation(vec);
|
return vec.applyQuat(object.transform.rot);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@ package armory.logicnode;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
import iron.object.Object;
|
import iron.object.Object;
|
||||||
|
|
||||||
using armory.object.TransformExtension;
|
|
||||||
|
|
||||||
class WorldVectorToLocalSpaceNode extends LogicNode {
|
class WorldVectorToLocalSpaceNode extends LogicNode {
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
|
@ -17,7 +15,8 @@ class WorldVectorToLocalSpaceNode extends LogicNode {
|
||||||
|
|
||||||
if (object == null || worldVec == null) return null;
|
if (object == null || worldVec == null) return null;
|
||||||
|
|
||||||
var localVec: Vec4 = new Vec4();
|
var localVec = new Vec4();
|
||||||
|
localVec.sub(object.transform.world.getLoc());
|
||||||
|
|
||||||
localVec.x = worldVec.dot(object.transform.right());
|
localVec.x = worldVec.dot(object.transform.right());
|
||||||
localVec.y = worldVec.dot(object.transform.look());
|
localVec.y = worldVec.dot(object.transform.look());
|
||||||
|
|
|
@ -715,7 +715,9 @@ class ShadowMapAtlas {
|
||||||
|
|
||||||
public static inline function getMaxAtlasSize(type: String): Int {
|
public static inline function getMaxAtlasSize(type: String): Int {
|
||||||
#if arm_shadowmap_atlas_single_map
|
#if arm_shadowmap_atlas_single_map
|
||||||
#if (rp_shadowmap_atlas_max_size == 1024)
|
#if (rp_shadowmap_atlas_max_size == 512)
|
||||||
|
return 512;
|
||||||
|
#elseif (rp_shadowmap_atlas_max_size == 1024)
|
||||||
return 1024;
|
return 1024;
|
||||||
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
||||||
return 2048;
|
return 2048;
|
||||||
|
@ -742,7 +744,9 @@ class ShadowMapAtlas {
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
case "spot": {
|
case "spot": {
|
||||||
#if (rp_shadowmap_atlas_max_size_spot == 1024)
|
#if (rp_shadowmap_atlas_max_size_spot == 512)
|
||||||
|
return 512;
|
||||||
|
#elseif (rp_shadowmap_atlas_max_size_spot == 1024)
|
||||||
return 1024;
|
return 1024;
|
||||||
#elseif (rp_shadowmap_atlas_max_size_spot == 2048)
|
#elseif (rp_shadowmap_atlas_max_size_spot == 2048)
|
||||||
return 2048;
|
return 2048;
|
||||||
|
@ -755,7 +759,9 @@ class ShadowMapAtlas {
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
case "sun": {
|
case "sun": {
|
||||||
#if (rp_shadowmap_atlas_max_size_sun == 1024)
|
#if (rp_shadowmap_atlas_max_size_sun == 512)
|
||||||
|
return 512;
|
||||||
|
#elseif (rp_shadowmap_atlas_max_size_sun == 1024)
|
||||||
return 1024;
|
return 1024;
|
||||||
#elseif (rp_shadowmap_atlas_max_size_sun == 2048)
|
#elseif (rp_shadowmap_atlas_max_size_sun == 2048)
|
||||||
return 2048;
|
return 2048;
|
||||||
|
@ -768,7 +774,9 @@ class ShadowMapAtlas {
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
#if (rp_shadowmap_atlas_max_size == 1024)
|
#if (rp_shadowmap_atlas_max_size == 512)
|
||||||
|
return 512;
|
||||||
|
#elseif (rp_shadowmap_atlas_max_size == 1024)
|
||||||
return 1024;
|
return 1024;
|
||||||
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
#elseif (rp_shadowmap_atlas_max_size == 2048)
|
||||||
return 2048;
|
return 2048;
|
||||||
|
|
|
@ -4,347 +4,223 @@ import kha.FastFloat;
|
||||||
import iron.system.Input;
|
import iron.system.Input;
|
||||||
|
|
||||||
class InputMap {
|
class InputMap {
|
||||||
var commands = new Map<String, Null<Array<InputCommand>>>();
|
|
||||||
|
static var inputMaps = new Map<String, InputMap>();
|
||||||
|
|
||||||
|
public var keys(default, null) = new Array<InputMapKey>();
|
||||||
|
public var lastKeyPressed(default, null) = "";
|
||||||
|
|
||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
||||||
public function addKeyboard(config: String) {
|
public static function getInputMap(inputMap: String): Null<InputMap> {
|
||||||
var command = new KeyboardCommand();
|
if (inputMaps.exists(inputMap)) {
|
||||||
return addCustomCommand(command, config);
|
return inputMaps[inputMap];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addGamepad(config: String) {
|
public static function addInputMap(inputMap: String): InputMap {
|
||||||
var command = new GamepadCommand();
|
return inputMaps[inputMap] = new InputMap();
|
||||||
return addCustomCommand(command, config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addCustomCommand(command: InputCommand, config: String) {
|
public static function getInputMapKey(inputMap: String, key: String): Null<InputMapKey> {
|
||||||
if (commands[config] == null) commands[config] = new Array<InputCommand>();
|
if (inputMaps.exists(inputMap)) {
|
||||||
commands[config].push(command);
|
for (k in inputMaps[inputMap].keys) {
|
||||||
return command;
|
if (k.key == key) {
|
||||||
}
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActionMap extends InputMap {
|
|
||||||
|
|
||||||
public inline function started(config: String) {
|
|
||||||
var started = false;
|
|
||||||
|
|
||||||
for (c in commands[config]) {
|
|
||||||
if (c.started()) {
|
|
||||||
started = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return started;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function released(config: String) {
|
public static function removeInputMapKey(inputMap: String, key: String): Bool {
|
||||||
var released = false;
|
if (inputMaps.exists(inputMap)) {
|
||||||
|
var i = inputMaps[inputMap];
|
||||||
|
|
||||||
for (c in commands[config]) {
|
for (k in i.keys) {
|
||||||
if (c.released()) {
|
if (k.key == key) {
|
||||||
released = true;
|
return i.removeKey(k);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return released;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class AxisMap extends InputMap {
|
public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey {
|
||||||
var scale: FastFloat = 1.0;
|
return addKey(new KeyboardKey(key, scale));
|
||||||
|
}
|
||||||
|
|
||||||
public inline function getAxis(config: String) {
|
public function addMouse(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
|
||||||
var axis = 0.0;
|
return addKey(new MouseKey(key, scale, deadzone));
|
||||||
|
}
|
||||||
|
|
||||||
for (c in commands[config]) {
|
public function addGamepad(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
|
||||||
var tempAxis = c.getAxis();
|
return addKey(new GamepadKey(key, scale, deadzone));
|
||||||
|
}
|
||||||
|
|
||||||
if (tempAxis != 0.0 && tempAxis != axis) {
|
public function addKey(key: InputMapKey): InputMapKey {
|
||||||
axis += tempAxis;
|
keys.push(key);
|
||||||
scale = c.getScale();
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeKey(key: InputMapKey): Bool {
|
||||||
|
return keys.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function value(): FastFloat {
|
||||||
|
var v = 0.0;
|
||||||
|
|
||||||
|
for (k in keys) {
|
||||||
|
v += k.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return axis;
|
return v;
|
||||||
}
|
|
||||||
|
|
||||||
public inline function getScale() {
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InputCommand {
|
|
||||||
var keys = new Array<String>();
|
|
||||||
var modifiers = new Array<String>();
|
|
||||||
var displacementKeys = new Array<String>();
|
|
||||||
var displacementModifiers = new Array<String>();
|
|
||||||
var deadzone: FastFloat = 0.0;
|
|
||||||
var scale: FastFloat = 1.0;
|
|
||||||
|
|
||||||
public function new() {}
|
|
||||||
|
|
||||||
public function setKeys(keys: Array<String>) {
|
|
||||||
return this.keys = keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMods(modifiers: Array<String>) {
|
|
||||||
return this.modifiers = modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDisplacementKeys(keys: Array<String>) {
|
|
||||||
return displacementKeys = keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDisplacementMods(modifiers: Array<String>) {
|
|
||||||
return displacementModifiers = modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDeadzone(deadzone: FastFloat) {
|
|
||||||
return this.deadzone = deadzone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setScale(scale: FastFloat) {
|
|
||||||
return this.scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getScale() {
|
|
||||||
return scale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function started() {
|
public function started() {
|
||||||
|
for (k in keys) {
|
||||||
|
if (k.started()) {
|
||||||
|
lastKeyPressed = k.key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function released() {
|
public function released() {
|
||||||
|
for (k in keys) {
|
||||||
|
if (k.released()) {
|
||||||
|
lastKeyPressed = k.key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputMapKey {
|
||||||
|
|
||||||
|
public var key: String;
|
||||||
|
public var scale: FastFloat;
|
||||||
|
public var deadzone: FastFloat;
|
||||||
|
|
||||||
|
public function new(key: String, scale = 1.0, deadzone = 0.0) {
|
||||||
|
this.key = key.toLowerCase();
|
||||||
|
this.scale = scale;
|
||||||
|
this.deadzone = deadzone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function started(): Bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAxis(): FastFloat {
|
public function released(): Bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function value(): FastFloat {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class KeyboardCommand extends InputCommand {
|
public function setIndex(index: Int) {}
|
||||||
var keyboard = Input.getKeyboard();
|
|
||||||
var mouse = Input.getMouse();
|
|
||||||
|
|
||||||
public inline override function started() {
|
function evalDeadzone(value: FastFloat): FastFloat {
|
||||||
for (k in keys) {
|
var v = 0.0;
|
||||||
if (keyboard.started(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (!keyboard.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
if (value > deadzone) {
|
||||||
if (!mouse.down(m)) return false;
|
v = value - deadzone;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
} else if (value < -deadzone) {
|
||||||
}
|
v = value + deadzone;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (k in displacementKeys) {
|
return v * scale;
|
||||||
if (mouse.started(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (!keyboard.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
|
||||||
if (!mouse.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline override function released() {
|
function evalPressure(value: FastFloat): FastFloat {
|
||||||
for (k in keys) {
|
var v = value - deadzone;
|
||||||
if (keyboard.released(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (!keyboard.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
if (v > 0.0) {
|
||||||
if (!mouse.down(m)) return false;
|
v /= (1.0 - deadzone);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
} else {
|
||||||
}
|
v = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (k in displacementKeys) {
|
return v;
|
||||||
if (mouse.released(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (!keyboard.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
|
||||||
if (!mouse.down(m)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public inline override function getAxis() {
|
|
||||||
var axis = 0.0;
|
|
||||||
var movementX = mouse.movementX;
|
|
||||||
var movementY = mouse.movementY;
|
|
||||||
var wheelDelta = mouse.wheelDelta;
|
|
||||||
|
|
||||||
for (k in keys) {
|
|
||||||
if (keyboard.down(k)) {
|
|
||||||
axis++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (keyboard.down(m)) {
|
|
||||||
axis --;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k in displacementKeys) {
|
|
||||||
switch (k) {
|
|
||||||
case "moved x": if (movementX > deadzone) axis++;
|
|
||||||
case "moved y": if (movementY > deadzone) axis--;
|
|
||||||
case "wheel": if (wheelDelta < -deadzone) axis++;
|
|
||||||
case "movement x": if (movementX > deadzone) return movementX - deadzone;
|
|
||||||
case "movement y": if (movementY > deadzone) return movementY - deadzone;
|
|
||||||
default: {
|
|
||||||
if (mouse.down(k)) {
|
|
||||||
axis ++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
|
||||||
switch (m) {
|
|
||||||
case "moved x": if (movementX < -deadzone) axis--;
|
|
||||||
case "moved y": if (movementY < -deadzone) axis++;
|
|
||||||
case "wheel": if (wheelDelta > deadzone) axis--;
|
|
||||||
case "movement x": if (movementX < -deadzone) return movementX + deadzone;
|
|
||||||
case "movement y": if (movementY < -deadzone) return movementY + deadzone;
|
|
||||||
default: {
|
|
||||||
if (mouse.down(m)) {
|
|
||||||
axis --;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GamepadCommand extends InputCommand {
|
class KeyboardKey extends InputMapKey {
|
||||||
var gamepad = Input.getGamepad(0);
|
|
||||||
|
var kb = Input.getKeyboard();
|
||||||
|
|
||||||
public inline override function started() {
|
public inline override function started() {
|
||||||
for (k in keys) {
|
return kb.started(key);
|
||||||
if (gamepad.started(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (gamepad.down(m) < deadzone) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline override function released() {
|
public inline override function released() {
|
||||||
for (k in keys) {
|
return kb.released(key);
|
||||||
if (gamepad.released(k)) {
|
|
||||||
for (m in modifiers) {
|
|
||||||
if (gamepad.down(m) < deadzone) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline override function getAxis() {
|
public inline override function value(): FastFloat {
|
||||||
var axis = 0.0;
|
return kb.down(key) ? scale : 0.0;
|
||||||
var rsMovementX = gamepad.rightStick.movementX;
|
|
||||||
var rsMovementY = gamepad.rightStick.movementY;
|
|
||||||
var lsMovementX = gamepad.leftStick.movementX;
|
|
||||||
var lsMovementY = gamepad.leftStick.movementY;
|
|
||||||
var rtPressure = gamepad.down("r2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
|
|
||||||
var ltPressure = gamepad.down("l2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
|
|
||||||
|
|
||||||
for (k in keys) {
|
|
||||||
switch(k) {
|
|
||||||
case "rtPressure": axis += rtPressure;
|
|
||||||
case "ltPressure": axis += ltPressure;
|
|
||||||
default: {
|
|
||||||
if (gamepad.down(k) > deadzone) {
|
|
||||||
axis++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in modifiers) {
|
|
||||||
switch (m) {
|
|
||||||
case "rtPressure": axis -= rtPressure;
|
|
||||||
case "ltPressure": axis -= ltPressure;
|
|
||||||
default: {
|
|
||||||
if (gamepad.down(m) > deadzone) {
|
|
||||||
axis--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k in displacementKeys) {
|
|
||||||
switch(k) {
|
|
||||||
case "rs moved x": if (rsMovementX > deadzone) axis++;
|
|
||||||
case "rs moved y": if (rsMovementY > deadzone) axis++;
|
|
||||||
case "ls moved x": if (lsMovementX > deadzone) axis++;
|
|
||||||
case "ls moved y": if (lsMovementY > deadzone) axis++;
|
|
||||||
case "rs movement x": if (rsMovementX > deadzone) return rsMovementX - deadzone;
|
|
||||||
case "rs movement y": if (rsMovementY > deadzone) return rsMovementY - deadzone;
|
|
||||||
case "ls movement x": if (lsMovementX > deadzone) return lsMovementX - deadzone;
|
|
||||||
case "ls movement y": if (lsMovementY > deadzone) return lsMovementY - deadzone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (m in displacementModifiers) {
|
|
||||||
switch (m) {
|
|
||||||
case "rs moved x": if (rsMovementX < -deadzone) axis--;
|
|
||||||
case "rs moved y": if (rsMovementY < -deadzone) axis--;
|
|
||||||
case "ls moved x": if (lsMovementX < -deadzone) axis--;
|
|
||||||
case "ls moved y": if (lsMovementY < -deadzone) axis--;
|
|
||||||
case "rs movement x": if (rsMovementX < -deadzone) return rsMovementX + deadzone;
|
|
||||||
case "rs movement y": if (rsMovementY < -deadzone) return rsMovementY + deadzone;
|
|
||||||
case "ls movement x": if (lsMovementX < -deadzone) return lsMovementX + deadzone;
|
|
||||||
case "ls movement y": if (lsMovementY < -deadzone) return lsMovementY + deadzone;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MouseKey extends InputMapKey {
|
||||||
|
|
||||||
|
var m = Input.getMouse();
|
||||||
|
|
||||||
|
public inline override function started() {
|
||||||
|
return m.started(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline override function released() {
|
||||||
|
return m.released(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function value(): FastFloat {
|
||||||
|
return switch (key) {
|
||||||
|
case "movement x": evalDeadzone(m.movementX);
|
||||||
|
case "movement y": evalDeadzone(m.movementY);
|
||||||
|
case "wheel": evalDeadzone(m.wheelDelta);
|
||||||
|
default: m.down(key) ? scale : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GamepadKey extends InputMapKey {
|
||||||
|
|
||||||
|
var g = Input.getGamepad();
|
||||||
|
|
||||||
|
public inline override function started() {
|
||||||
|
return g.started(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline override function released() {
|
||||||
|
return g.released(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function value(): FastFloat {
|
||||||
|
return switch(key) {
|
||||||
|
case "ls movement x": evalDeadzone(g.leftStick.movementX);
|
||||||
|
case "ls movement y": evalDeadzone(g.leftStick.movementY);
|
||||||
|
case "rs movement x": evalDeadzone(g.rightStick.movementX);
|
||||||
|
case "rs movement y": evalDeadzone(g.rightStick.movementY);
|
||||||
|
case "lt pressure": evalDeadzone(evalPressure(g.down("l2")));
|
||||||
|
case "rt pressure": evalDeadzone(evalPressure(g.down("r2")));
|
||||||
|
default: evalDeadzone(g.down(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function setIndex(index: Int) {
|
||||||
|
g = Input.getGamepad(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class FollowCamera extends iron.Trait {
|
||||||
trace("FollowCamera error, unable to set target object");
|
trace("FollowCamera error, unable to set target object");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Std.is(object, iron.object.CameraObject)) {
|
if (Std.isOfType(object, iron.object.CameraObject)) {
|
||||||
disabled = true;
|
disabled = true;
|
||||||
trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom.");
|
trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -594,7 +594,7 @@ class DebugConsole extends Trait {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) {
|
function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) {
|
||||||
var color = kha.Color.fromFloats(0.1, 0.1, 0.1);
|
var color: Null<kha.Color> = kha.Color.fromFloats(0.1, 0.1, 0.1);
|
||||||
var borderColor = color;
|
var borderColor = color;
|
||||||
var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95;
|
var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95;
|
||||||
var x = (tile.coordsX / atlas.sizew) * atlasVisualSize;
|
var x = (tile.coordsX / atlas.sizew) * atlasVisualSize;
|
||||||
|
|
|
@ -2,14 +2,29 @@ package armory.trait.internal;
|
||||||
|
|
||||||
import kha.Image;
|
import kha.Image;
|
||||||
import kha.Video;
|
import kha.Video;
|
||||||
|
|
||||||
import iron.Trait;
|
import iron.Trait;
|
||||||
import iron.object.MeshObject;
|
import iron.object.MeshObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Replaces the diffuse texture of the first material of the trait's object
|
||||||
|
with a video texture.
|
||||||
|
|
||||||
|
@see https://github.com/armory3d/armory_examples/tree/master/material_movie
|
||||||
|
**/
|
||||||
class MovieTexture extends Trait {
|
class MovieTexture extends Trait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Caches all render targets used by this trait for re-use when having
|
||||||
|
multiple videos of the same size. The lookup only takes place on trait
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
Map layout: `[width => [height => image]]`
|
||||||
|
**/
|
||||||
|
static var imageCache: Map<Int, Map<Int, Image>> = new Map();
|
||||||
|
|
||||||
var video: Video;
|
var video: Video;
|
||||||
public static var image: Image;
|
var image: Image;
|
||||||
public static var created = false;
|
|
||||||
|
|
||||||
var videoName: String;
|
var videoName: String;
|
||||||
|
|
||||||
|
@ -33,10 +48,7 @@ class MovieTexture extends Trait {
|
||||||
|
|
||||||
this.videoName = videoName;
|
this.videoName = videoName;
|
||||||
|
|
||||||
if (!created) {
|
notifyOnInit(init);
|
||||||
created = true;
|
|
||||||
notifyOnInit(init);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
@ -44,9 +56,21 @@ class MovieTexture extends Trait {
|
||||||
video = vid;
|
video = vid;
|
||||||
video.play(true);
|
video.play(true);
|
||||||
|
|
||||||
image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height()));
|
var w = getPower2(video.width());
|
||||||
|
var h = getPower2(video.height());
|
||||||
|
|
||||||
var o = cast(object, iron.object.MeshObject);
|
// Lazily fill the outer map
|
||||||
|
var hMap: Map<Int, Image> = imageCache[w];
|
||||||
|
if (hMap == null) {
|
||||||
|
imageCache[w] = new Map<Int, Image>();
|
||||||
|
}
|
||||||
|
|
||||||
|
image = imageCache[w][h];
|
||||||
|
if (image == null) {
|
||||||
|
imageCache[w][h] = image = Image.createRenderTarget(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
var o = cast(object, MeshObject);
|
||||||
o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture
|
o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture
|
||||||
notifyOnRender2D(render);
|
notifyOnRender2D(render);
|
||||||
});
|
});
|
||||||
|
|
313
Sources/armory/trait/internal/UniformsManager.hx
Normal file
313
Sources/armory/trait/internal/UniformsManager.hx
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
package armory.trait.internal;
|
||||||
|
|
||||||
|
import iron.object.MeshObject;
|
||||||
|
import iron.Trait;
|
||||||
|
import kha.Image;
|
||||||
|
import iron.math.Vec4;
|
||||||
|
import iron.data.MaterialData;
|
||||||
|
import iron.Scene;
|
||||||
|
import iron.object.Object;
|
||||||
|
import iron.object.Uniforms;
|
||||||
|
|
||||||
|
|
||||||
|
class UniformsManager extends Trait{
|
||||||
|
|
||||||
|
static var floatsRegistered = false;
|
||||||
|
static var floatsMap = new Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>();
|
||||||
|
|
||||||
|
static var vectorsRegistered = false;
|
||||||
|
static var vectorsMap = new Map<Object, Map<MaterialData, Map<String, Vec4>>>();
|
||||||
|
|
||||||
|
static var texturesRegistered = false;
|
||||||
|
static var texturesMap = new Map<Object, Map<MaterialData, Map<String, kha.Image>>>();
|
||||||
|
|
||||||
|
static var sceneRemoveInitalized = false;
|
||||||
|
|
||||||
|
public var uniformExists = false;
|
||||||
|
|
||||||
|
public function new(){
|
||||||
|
super();
|
||||||
|
|
||||||
|
notifyOnInit(init);
|
||||||
|
|
||||||
|
notifyOnRemove(removeObject);
|
||||||
|
|
||||||
|
if(! sceneRemoveInitalized){
|
||||||
|
|
||||||
|
Scene.active.notifyOnRemove(removeScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var materials = cast(object, MeshObject).materials;
|
||||||
|
|
||||||
|
for (material in materials){
|
||||||
|
|
||||||
|
var exists = registerShaderUniforms(material);
|
||||||
|
if(exists) {
|
||||||
|
uniformExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function removeScene() {
|
||||||
|
|
||||||
|
removeObjectFromAllMaps(Scene.active.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeObject() {
|
||||||
|
|
||||||
|
removeObjectFromAllMaps(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to register float, vec3 and texture getter functions
|
||||||
|
static function register(type: UniformType){
|
||||||
|
|
||||||
|
switch (type){
|
||||||
|
case Float:{
|
||||||
|
if(! floatsRegistered){
|
||||||
|
floatsRegistered = true;
|
||||||
|
Uniforms.externalFloatLinks.push(floatLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case Vector:{
|
||||||
|
if(! vectorsRegistered){
|
||||||
|
vectorsRegistered = true;
|
||||||
|
Uniforms.externalVec3Links.push(vec3Link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case Texture:{
|
||||||
|
if(! texturesRegistered){
|
||||||
|
texturesRegistered = true;
|
||||||
|
Uniforms.externalTextureLinks.push(textureLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register and map shader uniforms if it is an armory shader parameter
|
||||||
|
public static function registerShaderUniforms(material: MaterialData) : Bool {
|
||||||
|
|
||||||
|
var uniformExist = false;
|
||||||
|
|
||||||
|
if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null);
|
||||||
|
if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null);
|
||||||
|
if(! texturesMap.exists(Scene.active.root)) texturesMap.set(Scene.active.root, null);
|
||||||
|
|
||||||
|
for(context in material.shader.raw.contexts){ // For each context in shader
|
||||||
|
for (constant in context.constants){ // For each constant in the context
|
||||||
|
if(constant.is_arm_parameter){ // Check if armory parameter
|
||||||
|
|
||||||
|
uniformExist = true;
|
||||||
|
var object = Scene.active.root; // Map default uniforms to scene root
|
||||||
|
|
||||||
|
switch (constant.type){
|
||||||
|
case "float":{
|
||||||
|
var link = constant.link;
|
||||||
|
var value = constant.float;
|
||||||
|
setFloatValue(material, object, link, value);
|
||||||
|
register(Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "vec3":{
|
||||||
|
|
||||||
|
var vec = new Vec4();
|
||||||
|
vec.x = constant.vec3.get(0);
|
||||||
|
vec.y = constant.vec3.get(1);
|
||||||
|
vec.z = constant.vec3.get(2);
|
||||||
|
|
||||||
|
setVec3Value(material, object, constant.link, vec);
|
||||||
|
register(Vector);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (texture in context.texture_units){
|
||||||
|
if(texture.is_arm_parameter){ // Check if armory parameter
|
||||||
|
|
||||||
|
uniformExist = true;
|
||||||
|
var object = Scene.active.root; // Map default texture to scene root
|
||||||
|
|
||||||
|
iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) {
|
||||||
|
setTextureValue(material, object, texture.link, image);
|
||||||
|
|
||||||
|
});
|
||||||
|
register(Texture);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniformExist;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to set map Object -> Material -> Link -> FLoat
|
||||||
|
public static function setFloatValue(material: MaterialData, object: Object, link: String, value: Null<kha.FastFloat>){
|
||||||
|
|
||||||
|
if(object == null || material == null || link == null) return;
|
||||||
|
|
||||||
|
var map = floatsMap;
|
||||||
|
|
||||||
|
var matMap = map.get(object);
|
||||||
|
if (matMap == null) {
|
||||||
|
matMap = new Map();
|
||||||
|
map.set(object, matMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = matMap.get(material);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Map();
|
||||||
|
matMap.set(material, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.set(link, value); // parameter name, value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to set map Object -> Material -> Link -> Vec3
|
||||||
|
public static function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){
|
||||||
|
|
||||||
|
if(object == null || material == null || link == null) return;
|
||||||
|
|
||||||
|
var map = vectorsMap;
|
||||||
|
|
||||||
|
var matMap = map.get(object);
|
||||||
|
if (matMap == null) {
|
||||||
|
matMap = new Map();
|
||||||
|
map.set(object, matMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = matMap.get(material);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Map();
|
||||||
|
matMap.set(material, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.set(link, value); // parameter name, value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to set map Object -> Material -> Link -> Texture
|
||||||
|
public static function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){
|
||||||
|
|
||||||
|
if(object == null || material == null || link == null) return;
|
||||||
|
|
||||||
|
var map = texturesMap;
|
||||||
|
|
||||||
|
var matMap = map.get(object);
|
||||||
|
if (matMap == null) {
|
||||||
|
matMap = new Map();
|
||||||
|
map.set(object, matMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = matMap.get(material);
|
||||||
|
if (entry == null) {
|
||||||
|
entry = new Map();
|
||||||
|
matMap.set(material, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.set(link, value); // parameter name, value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mehtod to get object specific material parameter float value
|
||||||
|
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
|
||||||
|
|
||||||
|
if(object == null || mat == null) return null;
|
||||||
|
|
||||||
|
if(! floatsMap.exists(object)){
|
||||||
|
object = Scene.active.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
var material = floatsMap.get(object);
|
||||||
|
if (material == null) return null;
|
||||||
|
|
||||||
|
var entry = material.get(mat);
|
||||||
|
if (entry == null) return null;
|
||||||
|
|
||||||
|
return entry.get(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mehtod to get object specific material parameter vec3 value
|
||||||
|
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
|
||||||
|
|
||||||
|
if(object == null || mat == null) return null;
|
||||||
|
|
||||||
|
if(! vectorsMap.exists(object)){
|
||||||
|
object = Scene.active.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
var material = vectorsMap.get(object);
|
||||||
|
if (material == null) return null;
|
||||||
|
|
||||||
|
var entry = material.get(mat);
|
||||||
|
if (entry == null) return null;
|
||||||
|
|
||||||
|
return entry.get(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mehtod to get object specific material parameter texture value
|
||||||
|
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
|
||||||
|
|
||||||
|
if(object == null || mat == null) return null;
|
||||||
|
|
||||||
|
if(! texturesMap.exists(object)){
|
||||||
|
object = Scene.active.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
var material = texturesMap.get(object);
|
||||||
|
if (material == null) return null;
|
||||||
|
|
||||||
|
var entry = material.get(mat);
|
||||||
|
if (entry == null) return null;
|
||||||
|
|
||||||
|
return entry.get(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns complete map of float value material paramets
|
||||||
|
public static function getFloatsMap():Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>{
|
||||||
|
|
||||||
|
return floatsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns complete map of vec3 value material paramets
|
||||||
|
public static function getVectorsMap():Map<Object, Map<MaterialData, Map<String, Vec4>>>{
|
||||||
|
|
||||||
|
return vectorsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns complete map of texture value material paramets
|
||||||
|
public static function getTexturesMap():Map<Object, Map<MaterialData, Map<String, kha.Image>>>{
|
||||||
|
|
||||||
|
return texturesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all object specific material paramenter keys
|
||||||
|
public static function removeObjectFromAllMaps(object: Object) {
|
||||||
|
|
||||||
|
floatsMap.remove(object);
|
||||||
|
vectorsMap.remove(object);
|
||||||
|
texturesMap.remove(object);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove object specific material paramenter keys
|
||||||
|
public static function removeObjectFromMap(object: Object, type: UniformType) {
|
||||||
|
|
||||||
|
switch (type){
|
||||||
|
case Float: floatsMap.remove(object);
|
||||||
|
|
||||||
|
case Vector: vectorsMap.remove(object);
|
||||||
|
|
||||||
|
case Texture: texturesMap.remove(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:enum abstract UniformType(Int) from Int to Int {
|
||||||
|
var Float = 0;
|
||||||
|
var Vector = 1;
|
||||||
|
var Texture = 2;
|
||||||
|
}
|
|
@ -18,8 +18,9 @@ class PhysicsConstraintExportHelper extends iron.Trait {
|
||||||
var breakingThreshold: Float;
|
var breakingThreshold: Float;
|
||||||
var limits: Array<Float>;
|
var limits: Array<Float>;
|
||||||
var constraintAdded: Bool = false;
|
var constraintAdded: Bool = false;
|
||||||
|
var relativeConstraint: Bool = false;
|
||||||
|
|
||||||
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) {
|
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, relatieConstraint: Bool = false, limits: Array<Float> = null) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.body1 = body1;
|
this.body1 = body1;
|
||||||
|
@ -27,14 +28,26 @@ class PhysicsConstraintExportHelper extends iron.Trait {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.disableCollisions = disableCollisions;
|
this.disableCollisions = disableCollisions;
|
||||||
this.breakingThreshold = breakingThreshold;
|
this.breakingThreshold = breakingThreshold;
|
||||||
|
this.relativeConstraint = relatieConstraint;
|
||||||
this.limits = limits;
|
this.limits = limits;
|
||||||
notifyOnInit(init);
|
notifyOnInit(init);
|
||||||
notifyOnUpdate(update);
|
notifyOnUpdate(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
var target1 = Scene.active.getChild(body1);
|
var target1;
|
||||||
var target2 = Scene.active.getChild(body2);
|
var target2;
|
||||||
|
|
||||||
|
if(relativeConstraint) {
|
||||||
|
|
||||||
|
target1 = object.parent.getChild(body1);
|
||||||
|
target2 = object.parent.getChild(body2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
target1 = Scene.active.getChild(body1);
|
||||||
|
target2 = Scene.active.getChild(body2);
|
||||||
|
}
|
||||||
object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits));
|
object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits));
|
||||||
constraintAdded = true;
|
constraintAdded = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ class RigidBody extends iron.Trait {
|
||||||
if (ready) return;
|
if (ready) return;
|
||||||
ready = true;
|
ready = true;
|
||||||
|
|
||||||
if (!Std.is(object, MeshObject)) return; // No mesh data
|
if (!Std.isOfType(object, MeshObject)) return; // No mesh data
|
||||||
|
|
||||||
transform = object.transform;
|
transform = object.transform;
|
||||||
physics = armory.trait.physics.PhysicsWorld.active;
|
physics = armory.trait.physics.PhysicsWorld.active;
|
||||||
|
|
|
@ -58,18 +58,18 @@ class Canvas {
|
||||||
var rotated = element.rotation != null && element.rotation != 0;
|
var rotated = element.rotation != null && element.rotation != 0;
|
||||||
if (rotated) ui.g.pushRotation(element.rotation, ui._x + scaled(element.width) / 2, ui._y + scaled(element.height) / 2);
|
if (rotated) ui.g.pushRotation(element.rotation, ui._x + scaled(element.width) / 2, ui._y + scaled(element.height) / 2);
|
||||||
|
|
||||||
|
var font = ui.ops.font;
|
||||||
|
var fontAsset = isFontAsset(element.asset);
|
||||||
|
if (fontAsset) ui.ops.font = getAsset(canvas, element.asset);
|
||||||
|
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
case Text:
|
case Text:
|
||||||
var font = ui.ops.font;
|
|
||||||
var size = ui.fontSize;
|
var size = ui.fontSize;
|
||||||
|
|
||||||
var fontAsset = element.asset != null && StringTools.endsWith(element.asset, ".ttf");
|
|
||||||
if (fontAsset) ui.ops.font = getAsset(canvas, element.asset);
|
|
||||||
ui.fontSize = scaled(element.height);
|
ui.fontSize = scaled(element.height);
|
||||||
ui.t.TEXT_COL = getColor(element.color_text, getTheme(canvas.theme).TEXT_COL);
|
ui.t.TEXT_COL = getColor(element.color_text, getTheme(canvas.theme).TEXT_COL);
|
||||||
ui.text(getText(canvas, element), element.alignment);
|
ui.text(getText(canvas, element), element.alignment);
|
||||||
|
|
||||||
ui.ops.font = font;
|
|
||||||
ui.fontSize = size;
|
ui.fontSize = size;
|
||||||
|
|
||||||
case Button:
|
case Button:
|
||||||
|
@ -90,7 +90,6 @@ class Canvas {
|
||||||
|
|
||||||
case Image:
|
case Image:
|
||||||
var image = getAsset(canvas, element.asset);
|
var image = getAsset(canvas, element.asset);
|
||||||
var fontAsset = element.asset != null && StringTools.endsWith(element.asset, ".ttf");
|
|
||||||
if (image != null && !fontAsset) {
|
if (image != null && !fontAsset) {
|
||||||
ui.imageScrollAlign = false;
|
ui.imageScrollAlign = false;
|
||||||
var tint = element.color != null ? element.color : 0xffffffff;
|
var tint = element.color != null ? element.color : 0xffffffff;
|
||||||
|
@ -218,6 +217,8 @@ class Canvas {
|
||||||
case Empty:
|
case Empty:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.ops.font = font;
|
||||||
|
|
||||||
if (element.children != null) {
|
if (element.children != null) {
|
||||||
for (id in element.children) {
|
for (id in element.children) {
|
||||||
drawElement(ui, canvas, elemById(canvas, id), scaled(element.x) + px, scaled(element.y) + py);
|
drawElement(ui, canvas, elemById(canvas, id), scaled(element.x) + px, scaled(element.y) + py);
|
||||||
|
@ -257,6 +258,10 @@ class Canvas {
|
||||||
return Std.int(f * _ui.SCALE());
|
return Std.int(f * _ui.SCALE());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline function isFontAsset(assetName: Null<String>): Bool {
|
||||||
|
return assetName != null && StringTools.endsWith(assetName.toLowerCase(), ".ttf");
|
||||||
|
}
|
||||||
|
|
||||||
public static inline function getColor(color: Null<Int>, defaultColor: Int): Int {
|
public static inline function getColor(color: Null<Int>, defaultColor: Int): Int {
|
||||||
return color != null ? color : defaultColor;
|
return color != null ? color : defaultColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,160 +172,160 @@ class Ext {
|
||||||
Keycodes can be found here: http://api.kha.tech/kha/input/KeyCode.html
|
Keycodes can be found here: http://api.kha.tech/kha/input/KeyCode.html
|
||||||
**/
|
**/
|
||||||
static function keycodeToString(keycode: Int): String {
|
static function keycodeToString(keycode: Int): String {
|
||||||
switch (keycode) {
|
return switch (keycode) {
|
||||||
case -1: return "None";
|
default: String.fromCharCode(keycode);
|
||||||
case KeyCode.Unknown: return "Unknown";
|
case -1: "None";
|
||||||
case KeyCode.Back: return "Back";
|
case KeyCode.Unknown: "Unknown";
|
||||||
case KeyCode.Cancel: return "Cancel";
|
case KeyCode.Back: "Back";
|
||||||
case KeyCode.Help: return "Help";
|
case KeyCode.Cancel: "Cancel";
|
||||||
case KeyCode.Backspace: return "Backspace";
|
case KeyCode.Help: "Help";
|
||||||
case KeyCode.Tab: return "Tab";
|
case KeyCode.Backspace: "Backspace";
|
||||||
case KeyCode.Clear: return "Clear";
|
case KeyCode.Tab: "Tab";
|
||||||
case KeyCode.Return: return "Return";
|
case KeyCode.Clear: "Clear";
|
||||||
case KeyCode.Shift: return "Shift";
|
case KeyCode.Return: "Return";
|
||||||
case KeyCode.Control: return "Ctrl";
|
case KeyCode.Shift: "Shift";
|
||||||
case KeyCode.Alt: return "Alt";
|
case KeyCode.Control: "Ctrl";
|
||||||
case KeyCode.Pause: return "Pause";
|
case KeyCode.Alt: "Alt";
|
||||||
case KeyCode.CapsLock: return "CapsLock";
|
case KeyCode.Pause: "Pause";
|
||||||
case KeyCode.Kana: return "Kana";
|
case KeyCode.CapsLock: "CapsLock";
|
||||||
// case KeyCode.Hangul: return "Hangul"; // Hangul == Kana
|
case KeyCode.Kana: "Kana";
|
||||||
case KeyCode.Eisu: return "Eisu";
|
// case KeyCode.Hangul: "Hangul"; // Hangul == Kana
|
||||||
case KeyCode.Junja: return "Junja";
|
case KeyCode.Eisu: "Eisu";
|
||||||
case KeyCode.Final: return "Final";
|
case KeyCode.Junja: "Junja";
|
||||||
case KeyCode.Hanja: return "Hanja";
|
case KeyCode.Final: "Final";
|
||||||
// case KeyCode.Kanji: return "Kanji"; // Kanji == Hanja
|
case KeyCode.Hanja: "Hanja";
|
||||||
case KeyCode.Escape: return "Esc";
|
// case KeyCode.Kanji: "Kanji"; // Kanji == Hanja
|
||||||
case KeyCode.Convert: return "Convert";
|
case KeyCode.Escape: "Esc";
|
||||||
case KeyCode.NonConvert: return "NonConvert";
|
case KeyCode.Convert: "Convert";
|
||||||
case KeyCode.Accept: return "Accept";
|
case KeyCode.NonConvert: "NonConvert";
|
||||||
case KeyCode.ModeChange: return "ModeChange";
|
case KeyCode.Accept: "Accept";
|
||||||
case KeyCode.Space: return "Space";
|
case KeyCode.ModeChange: "ModeChange";
|
||||||
case KeyCode.PageUp: return "PageUp";
|
case KeyCode.Space: "Space";
|
||||||
case KeyCode.PageDown: return "PageDown";
|
case KeyCode.PageUp: "PageUp";
|
||||||
case KeyCode.End: return "End";
|
case KeyCode.PageDown: "PageDown";
|
||||||
case KeyCode.Home: return "Home";
|
case KeyCode.End: "End";
|
||||||
case KeyCode.Left: return "Left";
|
case KeyCode.Home: "Home";
|
||||||
case KeyCode.Up: return "Up";
|
case KeyCode.Left: "Left";
|
||||||
case KeyCode.Right: return "Right";
|
case KeyCode.Up: "Up";
|
||||||
case KeyCode.Down: return "Down";
|
case KeyCode.Right: "Right";
|
||||||
case KeyCode.Select: return "Select";
|
case KeyCode.Down: "Down";
|
||||||
case KeyCode.Print: return "Print";
|
case KeyCode.Select: "Select";
|
||||||
case KeyCode.Execute: return "Execute";
|
case KeyCode.Print: "Print";
|
||||||
case KeyCode.PrintScreen: return "PrintScreen";
|
case KeyCode.Execute: "Execute";
|
||||||
case KeyCode.Insert: return "Insert";
|
case KeyCode.PrintScreen: "PrintScreen";
|
||||||
case KeyCode.Delete: return "Delete";
|
case KeyCode.Insert: "Insert";
|
||||||
case KeyCode.Colon: return "Colon";
|
case KeyCode.Delete: "Delete";
|
||||||
case KeyCode.Semicolon: return "Semicolon";
|
case KeyCode.Colon: "Colon";
|
||||||
case KeyCode.LessThan: return "LessThan";
|
case KeyCode.Semicolon: "Semicolon";
|
||||||
case KeyCode.Equals: return "Equals";
|
case KeyCode.LessThan: "LessThan";
|
||||||
case KeyCode.GreaterThan: return "GreaterThan";
|
case KeyCode.Equals: "Equals";
|
||||||
case KeyCode.QuestionMark: return "QuestionMark";
|
case KeyCode.GreaterThan: "GreaterThan";
|
||||||
case KeyCode.At: return "At";
|
case KeyCode.QuestionMark: "QuestionMark";
|
||||||
case KeyCode.Win: return "Win";
|
case KeyCode.At: "At";
|
||||||
case KeyCode.ContextMenu: return "ContextMenu";
|
case KeyCode.Win: "Win";
|
||||||
case KeyCode.Sleep: return "Sleep";
|
case KeyCode.ContextMenu: "ContextMenu";
|
||||||
case KeyCode.Numpad0: return "Numpad0";
|
case KeyCode.Sleep: "Sleep";
|
||||||
case KeyCode.Numpad1: return "Numpad1";
|
case KeyCode.Numpad0: "Numpad0";
|
||||||
case KeyCode.Numpad2: return "Numpad2";
|
case KeyCode.Numpad1: "Numpad1";
|
||||||
case KeyCode.Numpad3: return "Numpad3";
|
case KeyCode.Numpad2: "Numpad2";
|
||||||
case KeyCode.Numpad4: return "Numpad4";
|
case KeyCode.Numpad3: "Numpad3";
|
||||||
case KeyCode.Numpad5: return "Numpad5";
|
case KeyCode.Numpad4: "Numpad4";
|
||||||
case KeyCode.Numpad6: return "Numpad6";
|
case KeyCode.Numpad5: "Numpad5";
|
||||||
case KeyCode.Numpad7: return "Numpad7";
|
case KeyCode.Numpad6: "Numpad6";
|
||||||
case KeyCode.Numpad8: return "Numpad8";
|
case KeyCode.Numpad7: "Numpad7";
|
||||||
case KeyCode.Numpad9: return "Numpad9";
|
case KeyCode.Numpad8: "Numpad8";
|
||||||
case KeyCode.Multiply: return "Multiply";
|
case KeyCode.Numpad9: "Numpad9";
|
||||||
case KeyCode.Add: return "Add";
|
case KeyCode.Multiply: "Multiply";
|
||||||
case KeyCode.Separator: return "Separator";
|
case KeyCode.Add: "Add";
|
||||||
case KeyCode.Subtract: return "Subtract";
|
case KeyCode.Separator: "Separator";
|
||||||
case KeyCode.Decimal: return "Decimal";
|
case KeyCode.Subtract: "Subtract";
|
||||||
case KeyCode.Divide: return "Divide";
|
case KeyCode.Decimal: "Decimal";
|
||||||
case KeyCode.F1: return "F1";
|
case KeyCode.Divide: "Divide";
|
||||||
case KeyCode.F2: return "F2";
|
case KeyCode.F1: "F1";
|
||||||
case KeyCode.F3: return "F3";
|
case KeyCode.F2: "F2";
|
||||||
case KeyCode.F4: return "F4";
|
case KeyCode.F3: "F3";
|
||||||
case KeyCode.F5: return "F5";
|
case KeyCode.F4: "F4";
|
||||||
case KeyCode.F6: return "F6";
|
case KeyCode.F5: "F5";
|
||||||
case KeyCode.F7: return "F7";
|
case KeyCode.F6: "F6";
|
||||||
case KeyCode.F8: return "F8";
|
case KeyCode.F7: "F7";
|
||||||
case KeyCode.F9: return "F9";
|
case KeyCode.F8: "F8";
|
||||||
case KeyCode.F10: return "F10";
|
case KeyCode.F9: "F9";
|
||||||
case KeyCode.F11: return "F11";
|
case KeyCode.F10: "F10";
|
||||||
case KeyCode.F12: return "F12";
|
case KeyCode.F11: "F11";
|
||||||
case KeyCode.F13: return "F13";
|
case KeyCode.F12: "F12";
|
||||||
case KeyCode.F14: return "F14";
|
case KeyCode.F13: "F13";
|
||||||
case KeyCode.F15: return "F15";
|
case KeyCode.F14: "F14";
|
||||||
case KeyCode.F16: return "F16";
|
case KeyCode.F15: "F15";
|
||||||
case KeyCode.F17: return "F17";
|
case KeyCode.F16: "F16";
|
||||||
case KeyCode.F18: return "F18";
|
case KeyCode.F17: "F17";
|
||||||
case KeyCode.F19: return "F19";
|
case KeyCode.F18: "F18";
|
||||||
case KeyCode.F20: return "F20";
|
case KeyCode.F19: "F19";
|
||||||
case KeyCode.F21: return "F21";
|
case KeyCode.F20: "F20";
|
||||||
case KeyCode.F22: return "F22";
|
case KeyCode.F21: "F21";
|
||||||
case KeyCode.F23: return "F23";
|
case KeyCode.F22: "F22";
|
||||||
case KeyCode.F24: return "F24";
|
case KeyCode.F23: "F23";
|
||||||
case KeyCode.NumLock: return "NumLock";
|
case KeyCode.F24: "F24";
|
||||||
case KeyCode.ScrollLock: return "ScrollLock";
|
case KeyCode.NumLock: "NumLock";
|
||||||
case KeyCode.WinOemFjJisho: return "WinOemFjJisho";
|
case KeyCode.ScrollLock: "ScrollLock";
|
||||||
case KeyCode.WinOemFjMasshou: return "WinOemFjMasshou";
|
case KeyCode.WinOemFjJisho: "WinOemFjJisho";
|
||||||
case KeyCode.WinOemFjTouroku: return "WinOemFjTouroku";
|
case KeyCode.WinOemFjMasshou: "WinOemFjMasshou";
|
||||||
case KeyCode.WinOemFjLoya: return "WinOemFjLoya";
|
case KeyCode.WinOemFjTouroku: "WinOemFjTouroku";
|
||||||
case KeyCode.WinOemFjRoya: return "WinOemFjRoya";
|
case KeyCode.WinOemFjLoya: "WinOemFjLoya";
|
||||||
case KeyCode.Circumflex: return "Circumflex";
|
case KeyCode.WinOemFjRoya: "WinOemFjRoya";
|
||||||
case KeyCode.Exclamation: return "Exclamation";
|
case KeyCode.Circumflex: "Circumflex";
|
||||||
case KeyCode.DoubleQuote: return "DoubleQuote";
|
case KeyCode.Exclamation: "Exclamation";
|
||||||
case KeyCode.Hash: return "Hash";
|
case KeyCode.DoubleQuote: "DoubleQuote";
|
||||||
case KeyCode.Dollar: return "Dollar";
|
case KeyCode.Hash: "Hash";
|
||||||
case KeyCode.Percent: return "Percent";
|
case KeyCode.Dollar: "Dollar";
|
||||||
case KeyCode.Ampersand: return "Ampersand";
|
case KeyCode.Percent: "Percent";
|
||||||
case KeyCode.Underscore: return "Underscore";
|
case KeyCode.Ampersand: "Ampersand";
|
||||||
case KeyCode.OpenParen: return "OpenParen";
|
case KeyCode.Underscore: "Underscore";
|
||||||
case KeyCode.CloseParen: return "CloseParen";
|
case KeyCode.OpenParen: "OpenParen";
|
||||||
case KeyCode.Asterisk: return "Asterisk";
|
case KeyCode.CloseParen: "CloseParen";
|
||||||
case KeyCode.Plus: return "Plus";
|
case KeyCode.Asterisk: "Asterisk";
|
||||||
case KeyCode.Pipe: return "Pipe";
|
case KeyCode.Plus: "Plus";
|
||||||
case KeyCode.HyphenMinus: return "HyphenMinus";
|
case KeyCode.Pipe: "Pipe";
|
||||||
case KeyCode.OpenCurlyBracket: return "OpenCurlyBracket";
|
case KeyCode.HyphenMinus: "HyphenMinus";
|
||||||
case KeyCode.CloseCurlyBracket: return "CloseCurlyBracket";
|
case KeyCode.OpenCurlyBracket: "OpenCurlyBracket";
|
||||||
case KeyCode.Tilde: return "Tilde";
|
case KeyCode.CloseCurlyBracket: "CloseCurlyBracket";
|
||||||
case KeyCode.VolumeMute: return "VolumeMute";
|
case KeyCode.Tilde: "Tilde";
|
||||||
case KeyCode.VolumeDown: return "VolumeDown";
|
case KeyCode.VolumeMute: "VolumeMute";
|
||||||
case KeyCode.VolumeUp: return "VolumeUp";
|
case KeyCode.VolumeDown: "VolumeDown";
|
||||||
case KeyCode.Comma: return "Comma";
|
case KeyCode.VolumeUp: "VolumeUp";
|
||||||
case KeyCode.Period: return "Period";
|
case KeyCode.Comma: "Comma";
|
||||||
case KeyCode.Slash: return "Slash";
|
case KeyCode.Period: "Period";
|
||||||
case KeyCode.BackQuote: return "BackQuote";
|
case KeyCode.Slash: "Slash";
|
||||||
case KeyCode.OpenBracket: return "OpenBracket";
|
case KeyCode.BackQuote: "BackQuote";
|
||||||
case KeyCode.BackSlash: return "BackSlash";
|
case KeyCode.OpenBracket: "OpenBracket";
|
||||||
case KeyCode.CloseBracket: return "CloseBracket";
|
case KeyCode.BackSlash: "BackSlash";
|
||||||
case KeyCode.Quote: return "Quote";
|
case KeyCode.CloseBracket: "CloseBracket";
|
||||||
case KeyCode.Meta: return "Meta";
|
case KeyCode.Quote: "Quote";
|
||||||
case KeyCode.AltGr: return "AltGr";
|
case KeyCode.Meta: "Meta";
|
||||||
case KeyCode.WinIcoHelp: return "WinIcoHelp";
|
case KeyCode.AltGr: "AltGr";
|
||||||
case KeyCode.WinIco00: return "WinIco00";
|
case KeyCode.WinIcoHelp: "WinIcoHelp";
|
||||||
case KeyCode.WinIcoClear: return "WinIcoClear";
|
case KeyCode.WinIco00: "WinIco00";
|
||||||
case KeyCode.WinOemReset: return "WinOemReset";
|
case KeyCode.WinIcoClear: "WinIcoClear";
|
||||||
case KeyCode.WinOemJump: return "WinOemJump";
|
case KeyCode.WinOemReset: "WinOemReset";
|
||||||
case KeyCode.WinOemPA1: return "WinOemPA1";
|
case KeyCode.WinOemJump: "WinOemJump";
|
||||||
case KeyCode.WinOemPA2: return "WinOemPA2";
|
case KeyCode.WinOemPA1: "WinOemPA1";
|
||||||
case KeyCode.WinOemPA3: return "WinOemPA3";
|
case KeyCode.WinOemPA2: "WinOemPA2";
|
||||||
case KeyCode.WinOemWSCTRL: return "WinOemWSCTRL";
|
case KeyCode.WinOemPA3: "WinOemPA3";
|
||||||
case KeyCode.WinOemCUSEL: return "WinOemCUSEL";
|
case KeyCode.WinOemWSCTRL: "WinOemWSCTRL";
|
||||||
case KeyCode.WinOemATTN: return "WinOemATTN";
|
case KeyCode.WinOemCUSEL: "WinOemCUSEL";
|
||||||
case KeyCode.WinOemFinish: return "WinOemFinish";
|
case KeyCode.WinOemATTN: "WinOemATTN";
|
||||||
case KeyCode.WinOemCopy: return "WinOemCopy";
|
case KeyCode.WinOemFinish: "WinOemFinish";
|
||||||
case KeyCode.WinOemAuto: return "WinOemAuto";
|
case KeyCode.WinOemCopy: "WinOemCopy";
|
||||||
case KeyCode.WinOemENLW: return "WinOemENLW";
|
case KeyCode.WinOemAuto: "WinOemAuto";
|
||||||
case KeyCode.WinOemBackTab: return "WinOemBackTab";
|
case KeyCode.WinOemENLW: "WinOemENLW";
|
||||||
case KeyCode.ATTN: return "ATTN";
|
case KeyCode.WinOemBackTab: "WinOemBackTab";
|
||||||
case KeyCode.CRSEL: return "CRSEL";
|
case KeyCode.ATTN: "ATTN";
|
||||||
case KeyCode.EXSEL: return "EXSEL";
|
case KeyCode.CRSEL: "CRSEL";
|
||||||
case KeyCode.EREOF: return "EREOF";
|
case KeyCode.EXSEL: "EXSEL";
|
||||||
case KeyCode.Play: return "Play";
|
case KeyCode.EREOF: "EREOF";
|
||||||
case KeyCode.Zoom: return "Zoom";
|
case KeyCode.Play: "Play";
|
||||||
case KeyCode.PA1: return "PA1";
|
case KeyCode.Zoom: "Zoom";
|
||||||
case KeyCode.WinOemClear: return "WinOemClear";
|
case KeyCode.PA1: "PA1";
|
||||||
|
case KeyCode.WinOemClear: "WinOemClear";
|
||||||
}
|
}
|
||||||
return String.fromCharCode(keycode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2434,7 +2434,7 @@ Make sure the mesh only has tris/quads.""")
|
||||||
# Rigid body constraint
|
# Rigid body constraint
|
||||||
rbc = bobject.rigid_body_constraint
|
rbc = bobject.rigid_body_constraint
|
||||||
if rbc is not None and rbc.enabled:
|
if rbc is not None and rbc.enabled:
|
||||||
self.add_rigidbody_constraint(o, rbc)
|
self.add_rigidbody_constraint(o, bobject, rbc)
|
||||||
|
|
||||||
# Camera traits
|
# Camera traits
|
||||||
if type is NodeType.CAMERA:
|
if type is NodeType.CAMERA:
|
||||||
|
@ -2454,6 +2454,13 @@ Make sure the mesh only has tris/quads.""")
|
||||||
else:
|
else:
|
||||||
self.material_to_object_dict[mat] = [bobject]
|
self.material_to_object_dict[mat] = [bobject]
|
||||||
self.material_to_arm_object_dict[mat] = [o]
|
self.material_to_arm_object_dict[mat] = [o]
|
||||||
|
|
||||||
|
# Add UniformsManager trait
|
||||||
|
if type is NodeType.MESH:
|
||||||
|
uniformManager = {}
|
||||||
|
uniformManager['type'] = 'Script'
|
||||||
|
uniformManager['class_name'] = 'armory.trait.internal.UniformsManager'
|
||||||
|
o['traits'].append(uniformManager)
|
||||||
|
|
||||||
# Export constraints
|
# Export constraints
|
||||||
if len(bobject.constraints) > 0:
|
if len(bobject.constraints) > 0:
|
||||||
|
@ -2741,7 +2748,7 @@ Make sure the mesh only has tris/quads.""")
|
||||||
o['traits'].append(out_trait)
|
o['traits'].append(out_trait)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_rigidbody_constraint(o, rbc):
|
def add_rigidbody_constraint(o, bobject, rbc):
|
||||||
rb1 = rbc.object1
|
rb1 = rbc.object1
|
||||||
rb2 = rbc.object2
|
rb2 = rbc.object2
|
||||||
if rb1 is None or rb2 is None:
|
if rb1 is None or rb2 is None:
|
||||||
|
@ -2760,7 +2767,8 @@ Make sure the mesh only has tris/quads.""")
|
||||||
"'" + rb1.name + "'",
|
"'" + rb1.name + "'",
|
||||||
"'" + rb2.name + "'",
|
"'" + rb2.name + "'",
|
||||||
str(rbc.disable_collisions).lower(),
|
str(rbc.disable_collisions).lower(),
|
||||||
str(breaking_threshold)
|
str(breaking_threshold),
|
||||||
|
str(bobject.arm_relative_physics_constraint).lower()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
if rbc.type == "FIXED":
|
if rbc.type == "FIXED":
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
import queue
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
@ -70,20 +71,56 @@ def on_operator_post(operator_id: str) -> None:
|
||||||
target_obj.arm_rb_collision_filter_mask = source_obj.arm_rb_collision_filter_mask
|
target_obj.arm_rb_collision_filter_mask = source_obj.arm_rb_collision_filter_mask
|
||||||
|
|
||||||
|
|
||||||
def always():
|
def send_operator(op):
|
||||||
|
if hasattr(bpy.context, 'object') and bpy.context.object != None:
|
||||||
|
obj = bpy.context.object.name
|
||||||
|
if op.name == 'Move':
|
||||||
|
vec = bpy.context.object.location
|
||||||
|
js = 'var o = iron.Scene.active.getChild("' + obj + '"); o.transform.loc.set(' + str(vec[0]) + ', ' + str(vec[1]) + ', ' + str(vec[2]) + '); o.transform.dirty = true;'
|
||||||
|
make.write_patch(js)
|
||||||
|
elif op.name == 'Resize':
|
||||||
|
vec = bpy.context.object.scale
|
||||||
|
js = 'var o = iron.Scene.active.getChild("' + obj + '"); o.transform.scale.set(' + str(vec[0]) + ', ' + str(vec[1]) + ', ' + str(vec[2]) + '); o.transform.dirty = true;'
|
||||||
|
make.write_patch(js)
|
||||||
|
elif op.name == 'Rotate':
|
||||||
|
vec = bpy.context.object.rotation_euler.to_quaternion()
|
||||||
|
js = 'var o = iron.Scene.active.getChild("' + obj + '"); o.transform.rot.set(' + str(vec[1]) + ', ' + str(vec[2]) + ', ' + str(vec[3]) + ' ,' + str(vec[0]) + '); o.transform.dirty = true;'
|
||||||
|
make.write_patch(js)
|
||||||
|
else: # Rebuild
|
||||||
|
make.patch()
|
||||||
|
|
||||||
|
|
||||||
|
def always() -> float:
|
||||||
# Force ui redraw
|
# Force ui redraw
|
||||||
if state.redraw_ui and context_screen != None:
|
if state.redraw_ui and context_screen is not None:
|
||||||
for area in context_screen.areas:
|
for area in context_screen.areas:
|
||||||
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
|
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
|
||||||
area.tag_redraw()
|
area.tag_redraw()
|
||||||
state.redraw_ui = False
|
state.redraw_ui = False
|
||||||
# TODO: depsgraph.updates only triggers material trees
|
# TODO: depsgraph.updates only triggers material trees
|
||||||
space = arm.utils.logic_editor_space(context_screen)
|
space = arm.utils.logic_editor_space(context_screen)
|
||||||
if space != None:
|
if space is not None:
|
||||||
space.node_tree.arm_cached = False
|
space.node_tree.arm_cached = False
|
||||||
return 0.5
|
return 0.5
|
||||||
|
|
||||||
|
|
||||||
|
def poll_threads() -> float:
|
||||||
|
"""Polls the thread callback queue and if a thread has finished, it
|
||||||
|
is joined with the main thread and the corresponding callback is
|
||||||
|
executed in the main thread.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
thread, callback = make.thread_callback_queue.get(block=False)
|
||||||
|
except queue.Empty:
|
||||||
|
return 0.25
|
||||||
|
|
||||||
|
thread.join()
|
||||||
|
callback()
|
||||||
|
|
||||||
|
# Quickly check if another thread has finished
|
||||||
|
return 0.01
|
||||||
|
|
||||||
|
|
||||||
appended_py_paths = []
|
appended_py_paths = []
|
||||||
context_screen = None
|
context_screen = None
|
||||||
|
|
||||||
|
@ -164,7 +201,9 @@ def register():
|
||||||
bpy.app.handlers.load_post.append(on_load_post)
|
bpy.app.handlers.load_post.append(on_load_post)
|
||||||
bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update_post)
|
bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update_post)
|
||||||
# bpy.app.handlers.undo_post.append(on_undo_post)
|
# bpy.app.handlers.undo_post.append(on_undo_post)
|
||||||
|
|
||||||
bpy.app.timers.register(always, persistent=True)
|
bpy.app.timers.register(always, persistent=True)
|
||||||
|
bpy.app.timers.register(poll_threads, persistent=True)
|
||||||
|
|
||||||
if arm.utils.get_fp() != '':
|
if arm.utils.get_fp() != '':
|
||||||
appended_py_paths = []
|
appended_py_paths = []
|
||||||
|
@ -181,6 +220,9 @@ def register():
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
bpy.app.timers.unregister(poll_threads)
|
||||||
|
bpy.app.timers.unregister(always)
|
||||||
|
|
||||||
bpy.app.handlers.load_post.remove(on_load_post)
|
bpy.app.handlers.load_post.remove(on_load_post)
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update_post)
|
bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update_post)
|
||||||
# bpy.app.handlers.undo_post.remove(on_undo_post)
|
# bpy.app.handlers.undo_post.remove(on_undo_post)
|
||||||
|
|
|
@ -1,16 +1,50 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class BoneIKNode(ArmLogicTreeNode):
|
class BoneIKNode(ArmLogicTreeNode):
|
||||||
"""Applies inverse kinematics in the given object bone."""
|
"""Performs inverse kinematics on the selected armature with specified bone.
|
||||||
|
|
||||||
|
@input Object: Armature on which IK should be performed.
|
||||||
|
|
||||||
|
@input Bone: Effector or tip bone for the inverse kinematics
|
||||||
|
|
||||||
|
@input Goal Position: Position in world coordinates the effector bone will track to
|
||||||
|
|
||||||
|
@input Enable Pole: Bend IK solution towards pole location
|
||||||
|
|
||||||
|
@input Pole Position: Location of the pole in world coordinates
|
||||||
|
|
||||||
|
@input Chain Length: Number of bones to include in the IK solver including the effector. If set to 0, all bones from effector to the root bone of the armature will be considered.
|
||||||
|
|
||||||
|
@input Max Iterations: Maximum allowed FABRIK iterations to solve for IK. For longer chains, more iterations are needed.
|
||||||
|
|
||||||
|
@input Precision: Presition of IK to stop at. It is described as a tolerence in length. Typically 0.01 is a good value.
|
||||||
|
|
||||||
|
@input Roll Angle: Roll the bones along their local axis with specified radians. set 0 for no extra roll.
|
||||||
|
"""
|
||||||
bl_idname = 'LNBoneIKNode'
|
bl_idname = 'LNBoneIKNode'
|
||||||
bl_label = 'Bone IK'
|
bl_label = 'Bone IK'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
arm_section = 'armature'
|
arm_section = 'armature'
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
self.add_input('ArmNodeSocketObject', 'Object')
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
self.add_input('ArmStringSocket', 'Bone')
|
self.add_input('ArmStringSocket', 'Bone')
|
||||||
self.add_input('ArmVectorSocket', 'Goal')
|
self.add_input('ArmVectorSocket', 'Goal Position')
|
||||||
|
self.add_input('ArmBoolSocket', 'Enable Pole')
|
||||||
|
self.add_input('ArmVectorSocket', 'Pole Position')
|
||||||
|
self.add_input('ArmIntSocket', 'Chain Length')
|
||||||
|
self.add_input('ArmIntSocket', 'Max Iterations', 10)
|
||||||
|
self.add_input('ArmFloatSocket', 'Precision', 0.01)
|
||||||
|
self.add_input('ArmFloatSocket', 'Roll Angle')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
|
||||||
|
return NodeReplacement(
|
||||||
|
'LNBoneIKNode', self.arm_version, 'LNBoneIKNode', 2,
|
||||||
|
in_socket_mapping={0:0, 1:1, 2:2, 3:3}, out_socket_mapping={0:0}
|
||||||
|
)
|
||||||
|
|
14
blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py
Normal file
14
blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetBoneFkIkOnlyNode(ArmLogicTreeNode):
|
||||||
|
"""Get if a particular bone is animated by Forward kinematics or Inverse kinematics only."""
|
||||||
|
bl_idname = 'LNGetBoneFkIkOnlyNode'
|
||||||
|
bl_label = 'Get Bone FK IK Only'
|
||||||
|
arm_version = 1
|
||||||
|
arm_section = 'armature'
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetBoneFkIkOnlyNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('NodeSocketString', 'Bone')
|
||||||
|
self.add_output('NodeSocketBool', 'FK or IK only')
|
14
blender/arm/logicnode/animation/LN_get_bone_transform.py
Normal file
14
blender/arm/logicnode/animation/LN_get_bone_transform.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetBoneTransformNode(ArmLogicTreeNode):
|
||||||
|
"""Returns bone transform in world space."""
|
||||||
|
bl_idname = 'LNGetBoneTransformNode'
|
||||||
|
bl_label = 'Get Bone Transform'
|
||||||
|
arm_version = 1
|
||||||
|
arm_section = 'armature'
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetBoneTransformNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('NodeSocketString', 'Bone')
|
||||||
|
self.add_output('NodeSocketShader', 'Transform')
|
17
blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py
Normal file
17
blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class SetBoneFkIkOnlyNode(ArmLogicTreeNode):
|
||||||
|
"""Set particular bone to be animated by Forward kinematics or Inverse kinematics only. All other animations will be ignored"""
|
||||||
|
bl_idname = 'LNSetBoneFkIkOnlyNode'
|
||||||
|
bl_label = 'Set Bone FK IK Only'
|
||||||
|
arm_version = 1
|
||||||
|
arm_section = 'armature'
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(SetBoneFkIkOnlyNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('NodeSocketString', 'Bone')
|
||||||
|
self.add_input('NodeSocketBool', 'FK or IK only')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
|
@ -347,6 +347,30 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator):
|
||||||
return{'FINISHED'}
|
return{'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class ArmNodeCallFuncButton(bpy.types.Operator):
|
||||||
|
"""Operator that calls a function on a specified
|
||||||
|
node (used for dynamic callbacks)."""
|
||||||
|
bl_idname = 'arm.node_call_func'
|
||||||
|
bl_label = 'Execute'
|
||||||
|
bl_options = {'UNDO', 'INTERNAL'}
|
||||||
|
|
||||||
|
node_index: StringProperty(name='Node Index', default='')
|
||||||
|
callback_name: StringProperty(name='Callback Name', default='')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
node = array_nodes[self.node_index]
|
||||||
|
if hasattr(node, self.callback_name):
|
||||||
|
getattr(node, self.callback_name)()
|
||||||
|
else:
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
# Reset to default again for subsequent calls of this operator
|
||||||
|
self.node_index = ''
|
||||||
|
self.callback_name = ''
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class ArmNodeSearch(bpy.types.Operator):
|
class ArmNodeSearch(bpy.types.Operator):
|
||||||
bl_idname = "arm.node_search"
|
bl_idname = "arm.node_search"
|
||||||
bl_label = "Search..."
|
bl_label = "Search..."
|
||||||
|
@ -537,12 +561,16 @@ def reset_globals():
|
||||||
category_items = OrderedDict()
|
category_items = OrderedDict()
|
||||||
|
|
||||||
|
|
||||||
bpy.utils.register_class(ArmNodeSearch)
|
REG_CLASSES = (
|
||||||
bpy.utils.register_class(ArmNodeAddInputButton)
|
ArmNodeSearch,
|
||||||
bpy.utils.register_class(ArmNodeAddInputValueButton)
|
ArmNodeAddInputButton,
|
||||||
bpy.utils.register_class(ArmNodeRemoveInputButton)
|
ArmNodeAddInputValueButton,
|
||||||
bpy.utils.register_class(ArmNodeRemoveInputValueButton)
|
ArmNodeRemoveInputButton,
|
||||||
bpy.utils.register_class(ArmNodeAddOutputButton)
|
ArmNodeRemoveInputValueButton,
|
||||||
bpy.utils.register_class(ArmNodeRemoveOutputButton)
|
ArmNodeAddOutputButton,
|
||||||
bpy.utils.register_class(ArmNodeAddInputOutputButton)
|
ArmNodeRemoveOutputButton,
|
||||||
bpy.utils.register_class(ArmNodeRemoveInputOutputButton)
|
ArmNodeAddInputOutputButton,
|
||||||
|
ArmNodeRemoveInputOutputButton,
|
||||||
|
ArmNodeCallFuncButton
|
||||||
|
)
|
||||||
|
register, unregister = bpy.utils.register_classes_factory(REG_CLASSES)
|
||||||
|
|
15
blender/arm/logicnode/input/LN_get_gamepad_started.py
Normal file
15
blender/arm/logicnode/input/LN_get_gamepad_started.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetGamepadStartedNode(ArmLogicTreeNode):
|
||||||
|
"""."""
|
||||||
|
bl_idname = 'LNGetGamepadStartedNode'
|
||||||
|
bl_label = 'Get Gamepad Started'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetGamepadStartedNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('NodeSocketInt', 'Index')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
self.add_output('NodeSocketString', 'Button')
|
15
blender/arm/logicnode/input/LN_get_input_map_key.py
Normal file
15
blender/arm/logicnode/input/LN_get_input_map_key.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetInputMapKeyNode(ArmLogicTreeNode):
|
||||||
|
"""Get key data if it exists in the input map."""
|
||||||
|
bl_idname = 'LNGetInputMapKeyNode'
|
||||||
|
bl_label = 'Get Input Map Key'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetInputMapKeyNode, self).init(context)
|
||||||
|
self.add_input('NodeSocketString', 'Input Map')
|
||||||
|
self.add_input('NodeSocketString', 'Key')
|
||||||
|
|
||||||
|
self.add_output('NodeSocketFloat', 'Scale', default_value = 1.0)
|
||||||
|
self.add_output('NodeSocketFloat', 'Deadzone')
|
14
blender/arm/logicnode/input/LN_get_keyboard_started.py
Normal file
14
blender/arm/logicnode/input/LN_get_keyboard_started.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetKeyboardStartedNode(ArmLogicTreeNode):
|
||||||
|
"""."""
|
||||||
|
bl_idname = 'LNGetKeyboardStartedNode'
|
||||||
|
bl_label = 'Get Keyboard Started'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetKeyboardStartedNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
self.add_output('NodeSocketString', 'Key')
|
14
blender/arm/logicnode/input/LN_get_mouse_started.py
Normal file
14
blender/arm/logicnode/input/LN_get_mouse_started.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class GetMouseStartedNode(ArmLogicTreeNode):
|
||||||
|
"""."""
|
||||||
|
bl_idname = 'LNGetMouseStartedNode'
|
||||||
|
bl_label = 'Get Mouse Started'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(GetMouseStartedNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
self.add_output('NodeSocketString', 'Button')
|
16
blender/arm/logicnode/input/LN_on_input_map.py
Normal file
16
blender/arm/logicnode/input/LN_on_input_map.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class OnInputMapNode(ArmLogicTreeNode):
|
||||||
|
"""Send a signal if any input map key is started or released."""
|
||||||
|
bl_idname = 'LNOnInputMapNode'
|
||||||
|
bl_label = 'On Input Map'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(OnInputMapNode, self).init(context)
|
||||||
|
self.add_input('NodeSocketString', 'Input Map')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Started')
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Released')
|
||||||
|
self.add_output('NodeSocketFloat', 'Value')
|
||||||
|
self.add_output('NodeSocketString', 'Key Pressed')
|
15
blender/arm/logicnode/input/LN_remove_input_map_key.py
Normal file
15
blender/arm/logicnode/input/LN_remove_input_map_key.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class RemoveInputMapKeyNode(ArmLogicTreeNode):
|
||||||
|
"""Remove input map key."""
|
||||||
|
bl_idname = 'LNRemoveInputMapKeyNode'
|
||||||
|
bl_label = 'Remove Input Map Key'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(RemoveInputMapKeyNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('NodeSocketString', 'Input Map')
|
||||||
|
self.add_input('NodeSocketString', 'Key')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
28
blender/arm/logicnode/input/LN_set_input_map_key.py
Normal file
28
blender/arm/logicnode/input/LN_set_input_map_key.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
class SetInputMapKeyNode(ArmLogicTreeNode):
|
||||||
|
"""Set input map key."""
|
||||||
|
bl_idname = 'LNSetInputMapKeyNode'
|
||||||
|
bl_label = 'Set Input Map Key'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
property0: EnumProperty(
|
||||||
|
items = [('keyboard', 'Keyboard', 'Keyboard input'),
|
||||||
|
('mouse', 'Mouse', 'Mouse input'),
|
||||||
|
('gamepad', 'Gamepad', 'Gamepad input')],
|
||||||
|
name='', default='keyboard')
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(SetInputMapKeyNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('NodeSocketString', 'Input Map')
|
||||||
|
self.add_input('NodeSocketString', 'Key')
|
||||||
|
self.add_input('NodeSocketFloat', 'Scale', default_value=1.0)
|
||||||
|
self.add_input('NodeSocketFloat', 'Deadzone')
|
||||||
|
self.add_input('NodeSocketInt', 'Index')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
|
||||||
|
def draw_buttons(self, context, layout):
|
||||||
|
layout.prop(self, 'property0')
|
|
@ -1,14 +1,45 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
|
||||||
class MergeNode(ArmLogicTreeNode):
|
class MergeNode(ArmLogicTreeNode):
|
||||||
"""Activates the output when any connected input is activated.
|
"""Activates the output when at least one connected input is activated.
|
||||||
|
If multiple inputs are active, the behaviour is specified by the
|
||||||
|
`Execution Mode` option.
|
||||||
|
|
||||||
|
@output Active Input Index: [*Available if Execution Mode is set to
|
||||||
|
Once Per Input*] The index of the last input that activated the output,
|
||||||
|
-1 if there was no execution yet on the current frame.
|
||||||
|
|
||||||
|
@option Execution Mode: The node's behaviour if multiple inputs are
|
||||||
|
active on the same frame.
|
||||||
|
|
||||||
|
- `Once Per Input`: If multiple inputs are active on one frame, activate
|
||||||
|
the output for each active input individually (simple forwarding).
|
||||||
|
|
||||||
|
- `Once Per Frame`: If multiple inputs are active on one frame,
|
||||||
|
trigger the output only once.
|
||||||
|
|
||||||
@option New: Add a new input socket.
|
@option New: Add a new input socket.
|
||||||
@option X Button: Remove the lowermost input socket."""
|
@option X Button: Remove the lowermost input socket."""
|
||||||
bl_idname = 'LNMergeNode'
|
bl_idname = 'LNMergeNode'
|
||||||
bl_label = 'Merge'
|
bl_label = 'Merge'
|
||||||
arm_section = 'flow'
|
arm_section = 'flow'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
|
def update_exec_mode(self, context):
|
||||||
|
self.outputs['Active Input Index'].hide = self.property0 == 'once_per_frame'
|
||||||
|
|
||||||
|
property0: EnumProperty(
|
||||||
|
name='Execution Mode',
|
||||||
|
description='The node\'s behaviour if multiple inputs are active on the same frame',
|
||||||
|
items=[('once_per_input', 'Once Per Input',
|
||||||
|
'If multiple inputs are active on one frame, activate the'
|
||||||
|
' output for each active input individually (simple forwarding)'),
|
||||||
|
('once_per_frame', 'Once Per Frame',
|
||||||
|
'If multiple inputs are active on one frame, trigger the output only once')],
|
||||||
|
default='once_per_input',
|
||||||
|
update=update_exec_mode,
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(MergeNode, self).__init__()
|
super(MergeNode, self).__init__()
|
||||||
|
@ -16,10 +47,12 @@ class MergeNode(ArmLogicTreeNode):
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
self.add_output('NodeSocketInt', 'Active Input Index')
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
row = layout.row(align=True)
|
layout.prop(self, 'property0', text='')
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
op = row.operator('arm.node_add_input', text='New', icon='PLUS', emboss=True)
|
op = row.operator('arm.node_add_input', text='New', icon='PLUS', emboss=True)
|
||||||
op.node_index = str(id(self))
|
op.node_index = str(id(self))
|
||||||
op.socket_type = 'ArmNodeSocketAction'
|
op.socket_type = 'ArmNodeSocketAction'
|
||||||
|
@ -31,3 +64,24 @@ class MergeNode(ArmLogicTreeNode):
|
||||||
return self.bl_label
|
return self.bl_label
|
||||||
|
|
||||||
return f'{self.bl_label}: [{len(self.inputs)}]'
|
return f'{self.bl_label}: [{len(self.inputs)}]'
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
|
||||||
|
newnode = node_tree.nodes.new('LNMergeNode')
|
||||||
|
newnode.property0 = self.property0
|
||||||
|
|
||||||
|
# Recreate all original inputs
|
||||||
|
array_nodes[str(id(newnode))] = newnode
|
||||||
|
for idx, input in enumerate(self.inputs):
|
||||||
|
bpy.ops.arm.node_add_input('EXEC_DEFAULT', node_index=str(id(newnode)), socket_type='ArmNodeSocketAction')
|
||||||
|
|
||||||
|
for link in input.links:
|
||||||
|
node_tree.links.new(link.from_socket, newnode.inputs[idx])
|
||||||
|
|
||||||
|
# Recreate outputs
|
||||||
|
for link in self.outputs[0].links:
|
||||||
|
node_tree.links.new(newnode.outputs[0], link.to_socket)
|
||||||
|
|
||||||
|
return newnode
|
||||||
|
|
16
blender/arm/logicnode/logic/LN_once_per_frame.py
Normal file
16
blender/arm/logicnode/logic/LN_once_per_frame.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
|
||||||
|
class OncePerFrameNode(ArmLogicTreeNode):
|
||||||
|
"""Activates the output only once per frame if receives one or more inputs in that frame
|
||||||
|
If there is no input, there will be no output"""
|
||||||
|
bl_idname = 'LNOncePerFrameNode'
|
||||||
|
bl_label = 'Once Per Frame'
|
||||||
|
arm_section = 'flow'
|
||||||
|
arm_version = 1
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super(OncePerFrameNode, self).init(context)
|
||||||
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
122
blender/arm/logicnode/logic/LN_select.py
Normal file
122
blender/arm/logicnode/logic/LN_select.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
from bpy.types import NodeSocketInterfaceInt
|
||||||
|
|
||||||
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
|
|
||||||
|
class SelectNode(ArmLogicTreeNode):
|
||||||
|
"""Selects one of multiple values (of arbitrary types) based on some
|
||||||
|
input state. The exact behaviour of this node is specified by the
|
||||||
|
`Execution Mode` option (see below).
|
||||||
|
|
||||||
|
@output Out: [*Available if Execution Mode is set to From Input*]
|
||||||
|
Activated after the node was executed.
|
||||||
|
|
||||||
|
@output Value: The last selected value. This value is not reset
|
||||||
|
until the next execution of this node.
|
||||||
|
|
||||||
|
@option Execution Mode: Specifies the condition that determines
|
||||||
|
what value to choose.
|
||||||
|
|
||||||
|
- `From Index`: Select the value at the given index. If there is
|
||||||
|
no value at that index, the value plugged in to the
|
||||||
|
`Default` input is used instead (`null` if unconnected).
|
||||||
|
|
||||||
|
- `From Input`: This mode uses input pairs of one action socket
|
||||||
|
and one value socket. Depending on which action socket is
|
||||||
|
activated, the associated value socket (the value with the
|
||||||
|
same index as the activated action input) is forwarded to
|
||||||
|
the `Value` output.
|
||||||
|
|
||||||
|
@option New: Add a new value to the list of values.
|
||||||
|
@option X Button: Remove the value with the highest index."""
|
||||||
|
bl_idname = 'LNSelectNode'
|
||||||
|
bl_label = 'Select'
|
||||||
|
arm_version = 1
|
||||||
|
min_inputs = 2
|
||||||
|
|
||||||
|
def update_exec_mode(self, context):
|
||||||
|
self.set_mode()
|
||||||
|
|
||||||
|
property0: EnumProperty(
|
||||||
|
name='Execution Mode',
|
||||||
|
description="The node's behaviour.",
|
||||||
|
items=[
|
||||||
|
('from_index', 'From Index', 'Choose the value from the given index'),
|
||||||
|
('from_input', 'From Input', 'Choose the value with the same position as the active input')],
|
||||||
|
default='from_index',
|
||||||
|
update=update_exec_mode,
|
||||||
|
)
|
||||||
|
|
||||||
|
# The number of choices, NOT of individual inputs. This needs to be
|
||||||
|
# a property in order to be saved with each individual node
|
||||||
|
num_choices: IntProperty(default=1, min=0)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
array_nodes[str(id(self))] = self
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
super().init(context)
|
||||||
|
|
||||||
|
self.set_mode()
|
||||||
|
|
||||||
|
def set_mode(self):
|
||||||
|
self.inputs.clear()
|
||||||
|
self.outputs.clear()
|
||||||
|
|
||||||
|
if self.property0 == 'from_index':
|
||||||
|
self.add_input('NodeSocketInt', 'Index')
|
||||||
|
self.add_input('NodeSocketShader', 'Default')
|
||||||
|
self.num_choices = 0
|
||||||
|
|
||||||
|
# from_input
|
||||||
|
else:
|
||||||
|
# We could also start with index 1 here, but we need to use
|
||||||
|
# 0 for the "from_index" mode and it makes the code simpler
|
||||||
|
# if we stick to the same convention for both exec modes
|
||||||
|
self.add_input('ArmNodeSocketAction', 'Input 0')
|
||||||
|
self.add_input('NodeSocketShader', 'Value 0')
|
||||||
|
self.num_choices = 1
|
||||||
|
|
||||||
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
self.add_output('NodeSocketShader', 'Value')
|
||||||
|
|
||||||
|
def draw_buttons(self, context, layout):
|
||||||
|
layout.prop(self, 'property0', text='')
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
|
||||||
|
op = row.operator('arm.node_call_func', text='New', icon='PLUS', emboss=True)
|
||||||
|
op.node_index = str(id(self))
|
||||||
|
op.callback_name = 'add_input_func'
|
||||||
|
|
||||||
|
op = row.operator('arm.node_call_func', text='', icon='X', emboss=True)
|
||||||
|
op.node_index = str(id(self))
|
||||||
|
op.callback_name = 'remove_input_func'
|
||||||
|
|
||||||
|
def add_input_func(self):
|
||||||
|
if self.property0 == 'from_input':
|
||||||
|
self.add_input('ArmNodeSocketAction', f'Input {self.num_choices}')
|
||||||
|
|
||||||
|
# Move new action input up to the end of all other action inputs
|
||||||
|
self.inputs.move(from_index=len(self.inputs) - 1, to_index=self.num_choices)
|
||||||
|
|
||||||
|
self.add_input('NodeSocketShader', f'Value {self.num_choices}')
|
||||||
|
|
||||||
|
self.num_choices += 1
|
||||||
|
|
||||||
|
def remove_input_func(self):
|
||||||
|
if self.property0 == 'from_input':
|
||||||
|
if len(self.inputs) > self.min_inputs:
|
||||||
|
self.inputs.remove(self.inputs[self.num_choices - 1])
|
||||||
|
|
||||||
|
if len(self.inputs) > self.min_inputs:
|
||||||
|
self.inputs.remove(self.inputs[-1])
|
||||||
|
self.num_choices -= 1
|
||||||
|
|
||||||
|
def draw_label(self) -> str:
|
||||||
|
if self.num_choices == 0:
|
||||||
|
return self.bl_label
|
||||||
|
|
||||||
|
return f'{self.bl_label}: [{self.num_choices}]'
|
|
@ -1,16 +1,42 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class SetMaterialImageParamNode(ArmLogicTreeNode):
|
class SetMaterialImageParamNode(ArmLogicTreeNode):
|
||||||
"""TO DO."""
|
"""Set an image value material parameter to the specified object.
|
||||||
|
|
||||||
|
@seeNode Get Scene Root
|
||||||
|
|
||||||
|
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
|
||||||
|
|
||||||
|
@input Per Object:
|
||||||
|
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
|
||||||
|
- `Disabled`: Set parameter globally, including this object.
|
||||||
|
|
||||||
|
@input Material: Material whose parameter to be set.
|
||||||
|
|
||||||
|
@input Node: Name of the parameter.
|
||||||
|
|
||||||
|
@input Image: Name of the image.
|
||||||
|
"""
|
||||||
bl_idname = 'LNSetMaterialImageParamNode'
|
bl_idname = 'LNSetMaterialImageParamNode'
|
||||||
bl_label = 'Set Material Image Param'
|
bl_label = 'Set Material Image Param'
|
||||||
arm_section = 'params'
|
arm_section = 'params'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('ArmBoolSocket', 'Per Object')
|
||||||
self.add_input('ArmDynamicSocket', 'Material')
|
self.add_input('ArmDynamicSocket', 'Material')
|
||||||
self.add_input('ArmStringSocket', 'Node')
|
self.add_input('ArmStringSocket', 'Node')
|
||||||
self.add_input('ArmStringSocket', 'Image')
|
self.add_input('ArmStringSocket', 'Image')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
|
||||||
|
return NodeReplacement(
|
||||||
|
'LNSetMaterialImageParamNode', self.arm_version, 'LNSetMaterialImageParamNode', 2,
|
||||||
|
in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0}
|
||||||
|
)
|
||||||
|
|
|
@ -1,16 +1,42 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class SetMaterialRgbParamNode(ArmLogicTreeNode):
|
class SetMaterialRgbParamNode(ArmLogicTreeNode):
|
||||||
"""TO DO."""
|
"""Set a color or vector value material parameter to the specified object.
|
||||||
|
|
||||||
|
@seeNode Get Scene Root
|
||||||
|
|
||||||
|
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
|
||||||
|
|
||||||
|
@input Per Object:
|
||||||
|
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
|
||||||
|
- `Disabled`: Set parameter globally, including this object.
|
||||||
|
|
||||||
|
@input Material: Material whose parameter to be set.
|
||||||
|
|
||||||
|
@input Node: Name of the parameter.
|
||||||
|
|
||||||
|
@input Color: Color or vector input.
|
||||||
|
"""
|
||||||
bl_idname = 'LNSetMaterialRgbParamNode'
|
bl_idname = 'LNSetMaterialRgbParamNode'
|
||||||
bl_label = 'Set Material RGB Param'
|
bl_label = 'Set Material RGB Param'
|
||||||
arm_section = 'params'
|
arm_section = 'params'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('ArmBoolSocket', 'Per Object')
|
||||||
self.add_input('ArmDynamicSocket', 'Material')
|
self.add_input('ArmDynamicSocket', 'Material')
|
||||||
self.add_input('ArmStringSocket', 'Node')
|
self.add_input('ArmStringSocket', 'Node')
|
||||||
self.add_input('ArmColorSocket', 'Color')
|
self.add_input('ArmColorSocket', 'Color')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
|
||||||
|
return NodeReplacement(
|
||||||
|
'LNSetMaterialRgbParamNode', self.arm_version, 'LNSetMaterialRgbParamNode', 2,
|
||||||
|
in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0}
|
||||||
|
)
|
||||||
|
|
|
@ -1,16 +1,43 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class SetMaterialValueParamNode(ArmLogicTreeNode):
|
class SetMaterialValueParamNode(ArmLogicTreeNode):
|
||||||
"""TO DO."""
|
"""Set a float value material parameter to the specified object.
|
||||||
|
|
||||||
|
@seeNode Get Scene Root
|
||||||
|
|
||||||
|
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
|
||||||
|
|
||||||
|
@input Per Object:
|
||||||
|
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
|
||||||
|
- `Disabled`: Set parameter globally, including this object.
|
||||||
|
|
||||||
|
@input Material: Material whose parameter to be set.
|
||||||
|
|
||||||
|
@input Node: Name of the parameter.
|
||||||
|
|
||||||
|
@input Float: float value.
|
||||||
|
"""
|
||||||
bl_idname = 'LNSetMaterialValueParamNode'
|
bl_idname = 'LNSetMaterialValueParamNode'
|
||||||
bl_label = 'Set Material Value Param'
|
bl_label = 'Set Material Value Param'
|
||||||
arm_section = 'params'
|
arm_section = 'params'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('ArmBoolSocket', 'Per Object')
|
||||||
self.add_input('ArmDynamicSocket', 'Material')
|
self.add_input('ArmDynamicSocket', 'Material')
|
||||||
self.add_input('ArmStringSocket', 'Node')
|
self.add_input('ArmStringSocket', 'Node')
|
||||||
self.add_input('ArmFloatSocket', 'Float')
|
self.add_input('ArmFloatSocket', 'Float')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
|
||||||
|
return NodeReplacement(
|
||||||
|
'LNSetMaterialValueParamNode', self.arm_version, 'LNSetMaterialValueParamNode', 2,
|
||||||
|
in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0}
|
||||||
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ class GoToLocationNode(ArmLogicTreeNode):
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
self.add_input('ArmNodeSocketObject', 'Object')
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
self.add_input('ArmDynamicSocket', 'Location')
|
self.add_input('ArmVectorSocket', 'Location')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,8 @@ def replace_all():
|
||||||
print(f"A node whose class doesn't exist was found in node tree \"{tree_name}\"", file=reportf)
|
print(f"A node whose class doesn't exist was found in node tree \"{tree_name}\"", file=reportf)
|
||||||
elif error_type == 'update failed':
|
elif error_type == 'update failed':
|
||||||
print(f"A node of type {node_class} in tree \"{tree_name}\" failed to be updated, "
|
print(f"A node of type {node_class} in tree \"{tree_name}\" failed to be updated, "
|
||||||
f"because there is no (longer?) an update routine for this version of the node.", file=reportf)
|
f"because there is no (longer?) an update routine for this version of the node. Original exception:"
|
||||||
|
"\n" + tb + "\n", file=reportf)
|
||||||
elif error_type == 'future version':
|
elif error_type == 'future version':
|
||||||
print(f"A node of type {node_class} in tree \"{tree_name}\" seemingly comes from a future version of armory. "
|
print(f"A node of type {node_class} in tree \"{tree_name}\" seemingly comes from a future version of armory. "
|
||||||
f"Please check whether your version of armory is up to date", file=reportf)
|
f"Please check whether your version of armory is up to date", file=reportf)
|
||||||
|
|
|
@ -1,13 +1,26 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class GetLocationNode(ArmLogicTreeNode):
|
class GetLocationNode(ArmLogicTreeNode):
|
||||||
"""Returns the current location of the given object in world coordinates."""
|
"""Get the location of the given object in world coordinates.
|
||||||
|
|
||||||
|
@input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates
|
||||||
|
|
||||||
|
@seeNode Set Object Location
|
||||||
|
@seeNode World Vector to Local Space
|
||||||
|
@seeNode Vector to Object Orientation
|
||||||
|
"""
|
||||||
bl_idname = 'LNGetLocationNode'
|
bl_idname = 'LNGetLocationNode'
|
||||||
bl_label = 'Get Object Location'
|
bl_label = 'Get Object Location'
|
||||||
arm_section = 'location'
|
arm_section = 'location'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketObject', 'Object')
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
|
self.add_input('NodeSocketBool', 'Parent Relative')
|
||||||
|
|
||||||
self.add_output('ArmVectorSocket', 'Location')
|
self.add_output('ArmVectorSocket', 'Location')
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
return NodeReplacement.Identity(self)
|
||||||
|
|
|
@ -1,15 +1,28 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class SetLocationNode(ArmLogicTreeNode):
|
class SetLocationNode(ArmLogicTreeNode):
|
||||||
"""Sets the location of the given object."""
|
"""Set the location of the given object in world coordinates.
|
||||||
|
|
||||||
|
@input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates
|
||||||
|
|
||||||
|
@seeNode Get Object Location
|
||||||
|
@seeNode World Vector to Local Space
|
||||||
|
@seeNode Vector to Object Orientation
|
||||||
|
"""
|
||||||
bl_idname = 'LNSetLocationNode'
|
bl_idname = 'LNSetLocationNode'
|
||||||
bl_label = 'Set Object Location'
|
bl_label = 'Set Object Location'
|
||||||
arm_section = 'location'
|
arm_section = 'location'
|
||||||
arm_version = 1
|
arm_version = 2
|
||||||
|
|
||||||
def arm_init(self, context):
|
def arm_init(self, context):
|
||||||
self.add_input('ArmNodeSocketAction', 'In')
|
self.add_input('ArmNodeSocketAction', 'In')
|
||||||
self.add_input('ArmNodeSocketObject', 'Object')
|
self.add_input('ArmNodeSocketObject', 'Object')
|
||||||
self.add_input('ArmVectorSocket', 'Location')
|
self.add_input('ArmVectorSocket', 'Location')
|
||||||
|
self.add_input('NodeSocketBool', 'Parent Relative')
|
||||||
|
|
||||||
self.add_output('ArmNodeSocketAction', 'Out')
|
self.add_output('ArmNodeSocketAction', 'Out')
|
||||||
|
|
||||||
|
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||||
|
if self.arm_version not in (0, 1):
|
||||||
|
raise LookupError()
|
||||||
|
return NodeReplacement.Identity(self)
|
|
@ -1,10 +1,9 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class VectorToObjectOrientationNode(ArmLogicTreeNode):
|
class VectorToObjectOrientationNode(ArmLogicTreeNode):
|
||||||
"""Converts the given world vector to a vector oriented by the given object.
|
"""Transform world coordinates into object oriented coordinates (in other words: apply object rotation to it).
|
||||||
The object scale is taken in count.
|
|
||||||
|
|
||||||
@seeNode World Vector To Object Space
|
@seeNode World Vector to Object Space
|
||||||
@seeNode Get World Orientation
|
@seeNode Get World Orientation
|
||||||
@seeNode Vector From Transform
|
@seeNode Vector From Transform
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from arm.logicnode.arm_nodes import *
|
from arm.logicnode.arm_nodes import *
|
||||||
|
|
||||||
class WorldVectorToLocalSpaceNode(ArmLogicTreeNode):
|
class WorldVectorToLocalSpaceNode(ArmLogicTreeNode):
|
||||||
"""Converts the given world vector to a object space vector.
|
"""Transform world coordinates into object local coordinates.
|
||||||
The object scale is taken in count.
|
|
||||||
|
|
||||||
@seeNode Vector To Object Orientation
|
@seeNode Vector to Object Orientation
|
||||||
@seeNode Get World Orientation
|
@seeNode Get World Orientation
|
||||||
@seeNode Vector From Transform
|
@seeNode Vector From Transform
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
|
import errno
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from queue import Queue
|
||||||
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
from typing import Callable
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import shlex
|
|
||||||
import errno
|
|
||||||
import math
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
@ -29,15 +30,42 @@ import arm.write_data as write_data
|
||||||
scripts_mtime = 0 # Monitor source changes
|
scripts_mtime = 0 # Monitor source changes
|
||||||
profile_time = 0
|
profile_time = 0
|
||||||
|
|
||||||
def run_proc(cmd, done):
|
# Queue of threads and their done callbacks. Item format: [thread, done]
|
||||||
def fn(p, done):
|
thread_callback_queue = Queue(maxsize=0)
|
||||||
p.wait()
|
|
||||||
if done != None:
|
|
||||||
|
def run_proc(cmd, done: Callable) -> subprocess.Popen:
|
||||||
|
"""Creates a subprocess with the given command and returns it.
|
||||||
|
|
||||||
|
If Blender is not running in background mode, a thread is spawned
|
||||||
|
that waits until the subprocess has finished executing to not freeze
|
||||||
|
the UI, otherwise (in background mode) execution is blocked until
|
||||||
|
the subprocess has finished.
|
||||||
|
|
||||||
|
If `done` is not `None`, it is called afterwards in the main thread.
|
||||||
|
"""
|
||||||
|
use_thread = not bpy.app.background
|
||||||
|
|
||||||
|
def wait_for_proc(proc: subprocess.Popen):
|
||||||
|
proc.wait()
|
||||||
|
|
||||||
|
if use_thread:
|
||||||
|
# Put the done callback into the callback queue so that it
|
||||||
|
# can be received by a polling function in the main thread
|
||||||
|
thread_callback_queue.put([threading.current_thread(), done], block=True)
|
||||||
|
else:
|
||||||
done()
|
done()
|
||||||
|
|
||||||
p = subprocess.Popen(cmd)
|
p = subprocess.Popen(cmd)
|
||||||
threading.Thread(target=fn, args=(p, done)).start()
|
|
||||||
|
if use_thread:
|
||||||
|
threading.Thread(target=wait_for_proc, args=(p,)).start()
|
||||||
|
else:
|
||||||
|
wait_for_proc(p)
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def compile_shader_pass(res, raw_shaders_path, shader_name, defs, make_variants):
|
def compile_shader_pass(res, raw_shaders_path, shader_name, defs, make_variants):
|
||||||
os.chdir(raw_shaders_path + '/' + shader_name)
|
os.chdir(raw_shaders_path + '/' + shader_name)
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,8 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st
|
||||||
'BSDF_VELVET': nodes_shader.parse_bsdfvelvet,
|
'BSDF_VELVET': nodes_shader.parse_bsdfvelvet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.reset_outs()
|
||||||
|
|
||||||
if node.type in node_parser_funcs:
|
if node.type in node_parser_funcs:
|
||||||
node_parser_funcs[node.type](node, socket, state)
|
node_parser_funcs[node.type](node, socket, state)
|
||||||
|
|
||||||
|
@ -558,7 +560,7 @@ def to_uniform(inp: bpy.types.NodeSocket):
|
||||||
def store_var_name(node: bpy.types.Node):
|
def store_var_name(node: bpy.types.Node):
|
||||||
return node_name(node.name) + '_store'
|
return node_name(node.name) + '_store'
|
||||||
|
|
||||||
def texture_store(node, tex, tex_name, to_linear=False, tex_link=None):
|
def texture_store(node, tex, tex_name, to_linear=False, tex_link=None, default_value=None, is_arm_mat_param=None):
|
||||||
curshader = state.curshader
|
curshader = state.curshader
|
||||||
|
|
||||||
tex_store = store_var_name(node)
|
tex_store = store_var_name(node)
|
||||||
|
@ -567,7 +569,7 @@ def texture_store(node, tex, tex_name, to_linear=False, tex_link=None):
|
||||||
state.parsed.add(tex_store)
|
state.parsed.add(tex_store)
|
||||||
mat_bind_texture(tex)
|
mat_bind_texture(tex)
|
||||||
state.con.add_elem('tex', 'short2norm')
|
state.con.add_elem('tex', 'short2norm')
|
||||||
curshader.add_uniform('sampler2D {0}'.format(tex_name), link=tex_link)
|
curshader.add_uniform('sampler2D {0}'.format(tex_name), link=tex_link, default_value=default_value, is_arm_mat_param=is_arm_mat_param)
|
||||||
triplanar = node.projection == 'BOX'
|
triplanar = node.projection == 'BOX'
|
||||||
if node.inputs[0].is_linked:
|
if node.inputs[0].is_linked:
|
||||||
uv_name = parse_vector_input(node.inputs[0])
|
uv_name = parse_vector_input(node.inputs[0])
|
||||||
|
|
|
@ -92,7 +92,13 @@ def parse_attribute(node: bpy.types.ShaderNodeAttribute, out_socket: bpy.types.N
|
||||||
def parse_rgb(node: bpy.types.ShaderNodeRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
def parse_rgb(node: bpy.types.ShaderNodeRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str:
|
||||||
if node.arm_material_param:
|
if node.arm_material_param:
|
||||||
nn = 'param_' + c.node_name(node.name)
|
nn = 'param_' + c.node_name(node.name)
|
||||||
state.curshader.add_uniform(f'vec3 {nn}', link=f'{node.name}')
|
v = out_socket.default_value
|
||||||
|
value = []
|
||||||
|
value.append(float(v[0]))
|
||||||
|
value.append(float(v[1]))
|
||||||
|
value.append(float(v[2]))
|
||||||
|
is_arm_mat_param = True
|
||||||
|
state.curshader.add_uniform(f'vec3 {nn}', link=f'{node.name}', default_value = value, is_arm_mat_param = is_arm_mat_param)
|
||||||
return nn
|
return nn
|
||||||
else:
|
else:
|
||||||
return c.to_vec3(out_socket.default_value)
|
return c.to_vec3(out_socket.default_value)
|
||||||
|
@ -351,7 +357,9 @@ def parse_lightpath(node: bpy.types.ShaderNodeLightPath, out_socket: bpy.types.N
|
||||||
def parse_value(node: bpy.types.ShaderNodeValue, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
|
def parse_value(node: bpy.types.ShaderNodeValue, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr:
|
||||||
if node.arm_material_param:
|
if node.arm_material_param:
|
||||||
nn = 'param_' + c.node_name(node.name)
|
nn = 'param_' + c.node_name(node.name)
|
||||||
state.curshader.add_uniform('float {0}'.format(nn), link='{0}'.format(node.name))
|
value = c.to_vec1(node.outputs[0].default_value)
|
||||||
|
is_arm_mat_param = True
|
||||||
|
state.curshader.add_uniform('float {0}'.format(nn), link='{0}'.format(node.name), default_value=value, is_arm_mat_param=is_arm_mat_param)
|
||||||
return nn
|
return nn
|
||||||
else:
|
else:
|
||||||
return c.to_vec1(node.outputs[0].default_value)
|
return c.to_vec1(node.outputs[0].default_value)
|
||||||
|
|
|
@ -114,15 +114,22 @@ def parse_tex_image(node: bpy.types.ShaderNodeTexImage, out_socket: bpy.types.No
|
||||||
|
|
||||||
tex_name = c.node_name(node.name)
|
tex_name = c.node_name(node.name)
|
||||||
tex = c.make_texture(node, tex_name)
|
tex = c.make_texture(node, tex_name)
|
||||||
tex_link = node.name if node.arm_material_param else None
|
tex_link = None
|
||||||
|
tex_default_file = None
|
||||||
|
is_arm_mat_param = None
|
||||||
|
if node.arm_material_param:
|
||||||
|
tex_link = node.name
|
||||||
|
is_arm_mat_param = True
|
||||||
|
if tex['file'] is not None:
|
||||||
|
tex_default_file = tex['file']
|
||||||
|
|
||||||
if tex is not None:
|
if tex is not None:
|
||||||
state.curshader.write_textures += 1
|
state.curshader.write_textures += 1
|
||||||
if use_color_out:
|
if use_color_out:
|
||||||
to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB'
|
to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB'
|
||||||
res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link)}.rgb'
|
res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.rgb'
|
||||||
else:
|
else:
|
||||||
res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link)}.a'
|
res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.a'
|
||||||
state.curshader.write_textures -= 1
|
state.curshader.write_textures -= 1
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import arm.material.cycles as cycles
|
import arm.material.cycles as cycles
|
||||||
import arm.material.mat_state as mat_state
|
import arm.material.mat_state as mat_state
|
||||||
import arm.material.make_skin as make_skin
|
import arm.material.make_skin as make_skin
|
||||||
import arm.material.make_particle as make_particle
|
import arm.material.make_particle as make_particle
|
||||||
import arm.material.make_inst as make_inst
|
import arm.material.make_inst as make_inst
|
||||||
import arm.material.shader as shader
|
import arm.material.make_tess as make_tess
|
||||||
|
from arm.material.shader import Shader, ShaderContext
|
||||||
import arm.utils
|
import arm.utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,17 +36,45 @@ def write_vertpos(vert):
|
||||||
vert.write('gl_Position = WVP * spos;')
|
vert.write('gl_Position = WVP * spos;')
|
||||||
|
|
||||||
|
|
||||||
def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=False, write_nor=True):
|
def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor=True):
|
||||||
prep = ''
|
|
||||||
if declare:
|
|
||||||
prep = 'vec3 '
|
|
||||||
is_bone = con_mesh.is_elem('bone')
|
is_bone = con_mesh.is_elem('bone')
|
||||||
if is_bone:
|
if is_bone:
|
||||||
make_skin.skin_pos(vert)
|
make_skin.skin_pos(vert)
|
||||||
if write_nor:
|
if write_nor:
|
||||||
|
prep = 'vec3 ' if declare else ''
|
||||||
if is_bone:
|
if is_bone:
|
||||||
make_skin.skin_nor(vert, prep)
|
make_skin.skin_nor(vert, prep)
|
||||||
else:
|
else:
|
||||||
vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));')
|
vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));')
|
||||||
if con_mesh.is_elem('ipos'):
|
if con_mesh.is_elem('ipos'):
|
||||||
make_inst.inst_pos(con_mesh, vert)
|
make_inst.inst_pos(con_mesh, vert)
|
||||||
|
|
||||||
|
|
||||||
|
def write_tex_coords(con_mesh: ShaderContext, vert: Shader, frag: Shader, tese: Optional[Shader]):
|
||||||
|
rpdat = arm.utils.get_rp()
|
||||||
|
|
||||||
|
if con_mesh.is_elem('tex'):
|
||||||
|
vert.add_out('vec2 texCoord')
|
||||||
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
||||||
|
if mat_state.material.arm_tilesheet_flag:
|
||||||
|
if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On':
|
||||||
|
make_particle.write_tilesheet(vert)
|
||||||
|
else:
|
||||||
|
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
||||||
|
vert.write_attrib('texCoord = tex * texUnpack + tilesheetOffset;')
|
||||||
|
else:
|
||||||
|
vert.write_attrib('texCoord = tex * texUnpack;')
|
||||||
|
|
||||||
|
if tese is not None:
|
||||||
|
tese.write_pre = True
|
||||||
|
make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord'))
|
||||||
|
tese.write_pre = False
|
||||||
|
|
||||||
|
if con_mesh.is_elem('tex1'):
|
||||||
|
vert.add_out('vec2 texCoord1')
|
||||||
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
||||||
|
vert.write_attrib('texCoord1 = tex1 * texUnpack;')
|
||||||
|
if tese is not None:
|
||||||
|
tese.write_pre = True
|
||||||
|
make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1'))
|
||||||
|
tese.write_pre = False
|
||||||
|
|
|
@ -33,8 +33,6 @@ def make(context_id, rpasses, shadowmap=False):
|
||||||
parse_custom_particle = (cycles.node_by_name(mat_state.nodes, 'ArmCustomParticleNode') is not None)
|
parse_custom_particle = (cycles.node_by_name(mat_state.nodes, 'ArmCustomParticleNode') is not None)
|
||||||
|
|
||||||
if parse_opacity:
|
if parse_opacity:
|
||||||
frag.write('vec3 n;') # Discard at compile time
|
|
||||||
frag.write('float dotNV;')
|
|
||||||
frag.write('float opacity;')
|
frag.write('float opacity;')
|
||||||
|
|
||||||
if con_depth.is_elem('bone'):
|
if con_depth.is_elem('bone'):
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import bpy
|
import bpy
|
||||||
import arm.material.make_tess as make_tess
|
|
||||||
|
|
||||||
def make(con_mesh):
|
import arm.material.make_tess as make_tess
|
||||||
|
from arm.material.shader import ShaderContext
|
||||||
|
|
||||||
|
|
||||||
|
def make(con_mesh: ShaderContext):
|
||||||
vert = con_mesh.vert
|
vert = con_mesh.vert
|
||||||
frag = con_mesh.frag
|
frag = con_mesh.frag
|
||||||
geom = con_mesh.geom
|
geom = con_mesh.geom
|
||||||
|
@ -13,9 +16,26 @@ def make(con_mesh):
|
||||||
if frag.contains('dotNV') and not frag.contains('float dotNV'):
|
if frag.contains('dotNV') and not frag.contains('float dotNV'):
|
||||||
frag.write_init('float dotNV = max(dot(n, vVec), 0.0);')
|
frag.write_init('float dotNV = max(dot(n, vVec), 0.0);')
|
||||||
|
|
||||||
|
# n is not always defined yet (in some shadowmap shaders e.g.)
|
||||||
|
if not frag.contains('vec3 n'):
|
||||||
|
vert.add_out('vec3 wnormal')
|
||||||
|
vert.add_uniform('mat3 N', '_normalMatrix')
|
||||||
|
vert.write_attrib('wnormal = normalize(N * vec3(nor.xy, pos.w));')
|
||||||
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
||||||
|
|
||||||
|
# If not yet added, add nor vertex data
|
||||||
|
vertex_elems = con_mesh.data['vertex_elements']
|
||||||
|
has_normals = False
|
||||||
|
for elem in vertex_elems:
|
||||||
|
if elem['name'] == 'nor':
|
||||||
|
has_normals = True
|
||||||
|
break
|
||||||
|
if not has_normals:
|
||||||
|
vertex_elems.append({'name': 'nor', 'data': 'short2norm'})
|
||||||
|
|
||||||
write_wpos = False
|
write_wpos = False
|
||||||
if frag.contains('vVec') and not frag.contains('vec3 vVec'):
|
if frag.contains('vVec') and not frag.contains('vec3 vVec'):
|
||||||
if tese != None:
|
if tese is not None:
|
||||||
tese.add_out('vec3 eyeDir')
|
tese.add_out('vec3 eyeDir')
|
||||||
tese.add_uniform('vec3 eye', '_cameraPosition')
|
tese.add_uniform('vec3 eye', '_cameraPosition')
|
||||||
tese.write('eyeDir = eye - wposition;')
|
tese.write('eyeDir = eye - wposition;')
|
||||||
|
@ -31,7 +51,7 @@ def make(con_mesh):
|
||||||
export_wpos = False
|
export_wpos = False
|
||||||
if frag.contains('wposition') and not frag.contains('vec3 wposition'):
|
if frag.contains('wposition') and not frag.contains('vec3 wposition'):
|
||||||
export_wpos = True
|
export_wpos = True
|
||||||
if tese != None:
|
if tese is not None:
|
||||||
export_wpos = True
|
export_wpos = True
|
||||||
if vert.contains('wposition'):
|
if vert.contains('wposition'):
|
||||||
write_wpos = True
|
write_wpos = True
|
||||||
|
@ -50,7 +70,7 @@ def make(con_mesh):
|
||||||
vert.add_uniform('float posUnpack', link='_posUnpack')
|
vert.add_uniform('float posUnpack', link='_posUnpack')
|
||||||
vert.write_attrib('mposition = spos.xyz * posUnpack;')
|
vert.write_attrib('mposition = spos.xyz * posUnpack;')
|
||||||
|
|
||||||
if tese != None:
|
if tese is not None:
|
||||||
if frag_mpos:
|
if frag_mpos:
|
||||||
make_tess.interpolate(tese, 'mposition', 3, declare_out=True)
|
make_tess.interpolate(tese, 'mposition', 3, declare_out=True)
|
||||||
elif tese.contains('mposition') and not tese.contains('vec3 mposition'):
|
elif tese.contains('mposition') and not tese.contains('vec3 mposition'):
|
||||||
|
@ -72,7 +92,7 @@ def make(con_mesh):
|
||||||
vert.write_attrib('if (dim.y == 0) bposition.y = 0;')
|
vert.write_attrib('if (dim.y == 0) bposition.y = 0;')
|
||||||
vert.write_attrib('if (dim.x == 0) bposition.x = 0;')
|
vert.write_attrib('if (dim.x == 0) bposition.x = 0;')
|
||||||
|
|
||||||
if tese != None:
|
if tese is not None:
|
||||||
if frag_bpos:
|
if frag_bpos:
|
||||||
make_tess.interpolate(tese, 'bposition', 3, declare_out=True)
|
make_tess.interpolate(tese, 'bposition', 3, declare_out=True)
|
||||||
elif tese.contains('bposition') and not tese.contains('vec3 bposition'):
|
elif tese.contains('bposition') and not tese.contains('vec3 bposition'):
|
||||||
|
@ -93,7 +113,7 @@ def make(con_mesh):
|
||||||
vert.write('wtangent = normalize(N * tang.xyz);')
|
vert.write('wtangent = normalize(N * tang.xyz);')
|
||||||
vert.write_pre = False
|
vert.write_pre = False
|
||||||
|
|
||||||
if tese != None:
|
if tese is not None:
|
||||||
if frag_wtan:
|
if frag_wtan:
|
||||||
make_tess.interpolate(tese, 'wtangent', 3, declare_out=True)
|
make_tess.interpolate(tese, 'wtangent', 3, declare_out=True)
|
||||||
elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'):
|
elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'):
|
||||||
|
|
|
@ -132,31 +132,7 @@ def make_base(con_mesh, parse_opacity):
|
||||||
if not is_displacement and not vattr_written:
|
if not is_displacement and not vattr_written:
|
||||||
make_attrib.write_vertpos(vert)
|
make_attrib.write_vertpos(vert)
|
||||||
|
|
||||||
if con_mesh.is_elem('tex'):
|
make_attrib.write_tex_coords(con_mesh, vert, frag, tese)
|
||||||
vert.add_out('vec2 texCoord')
|
|
||||||
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
||||||
if mat_state.material.arm_tilesheet_flag:
|
|
||||||
if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On':
|
|
||||||
make_particle.write_tilesheet(vert)
|
|
||||||
else:
|
|
||||||
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
|
||||||
vert.write_attrib('texCoord = tex * texUnpack + tilesheetOffset;')
|
|
||||||
else:
|
|
||||||
vert.write_attrib('texCoord = tex * texUnpack;')
|
|
||||||
|
|
||||||
if tese is not None:
|
|
||||||
tese.write_pre = True
|
|
||||||
make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord'))
|
|
||||||
tese.write_pre = False
|
|
||||||
|
|
||||||
if con_mesh.is_elem('tex1'):
|
|
||||||
vert.add_out('vec2 texCoord1')
|
|
||||||
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
||||||
vert.write_attrib('texCoord1 = tex1 * texUnpack;')
|
|
||||||
if tese is not None:
|
|
||||||
tese.write_pre = True
|
|
||||||
make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1'))
|
|
||||||
tese.write_pre = False
|
|
||||||
|
|
||||||
if con_mesh.is_elem('col'):
|
if con_mesh.is_elem('col'):
|
||||||
vert.add_out('vec3 vcolor')
|
vert.add_out('vec3 vcolor')
|
||||||
|
@ -296,8 +272,6 @@ def make_forward_mobile(con_mesh):
|
||||||
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
||||||
frag.ins = vert.outs
|
frag.ins = vert.outs
|
||||||
|
|
||||||
make_attrib.write_vertpos(vert)
|
|
||||||
|
|
||||||
frag.add_include('compiled.inc')
|
frag.add_include('compiled.inc')
|
||||||
frag.write('vec3 basecol;')
|
frag.write('vec3 basecol;')
|
||||||
frag.write('float roughness;')
|
frag.write('float roughness;')
|
||||||
|
@ -320,14 +294,7 @@ def make_forward_mobile(con_mesh):
|
||||||
opac = mat_state.material.arm_discard_opacity
|
opac = mat_state.material.arm_discard_opacity
|
||||||
frag.write('if (opacity < {0}) discard;'.format(opac))
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
||||||
|
|
||||||
if con_mesh.is_elem('tex'):
|
make_attrib.write_tex_coords(con_mesh, vert, frag, tese)
|
||||||
vert.add_out('vec2 texCoord')
|
|
||||||
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
||||||
if mat_state.material.arm_tilesheet_flag:
|
|
||||||
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
|
||||||
vert.write('texCoord = tex * texUnpack + tilesheetOffset;')
|
|
||||||
else:
|
|
||||||
vert.write('texCoord = tex * texUnpack;')
|
|
||||||
|
|
||||||
if con_mesh.is_elem('col'):
|
if con_mesh.is_elem('col'):
|
||||||
vert.add_out('vec3 vcolor')
|
vert.add_out('vec3 vcolor')
|
||||||
|
@ -344,6 +311,8 @@ def make_forward_mobile(con_mesh):
|
||||||
make_attrib.write_norpos(con_mesh, vert)
|
make_attrib.write_norpos(con_mesh, vert)
|
||||||
frag.write_attrib('vec3 n = normalize(wnormal);')
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
||||||
|
|
||||||
|
make_attrib.write_vertpos(vert)
|
||||||
|
|
||||||
frag.add_include('std/math.glsl')
|
frag.add_include('std/math.glsl')
|
||||||
frag.add_include('std/brdf.glsl')
|
frag.add_include('std/brdf.glsl')
|
||||||
|
|
||||||
|
@ -474,8 +443,6 @@ def make_forward_solid(con_mesh):
|
||||||
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
||||||
frag.ins = vert.outs
|
frag.ins = vert.outs
|
||||||
|
|
||||||
make_attrib.write_vertpos(vert)
|
|
||||||
|
|
||||||
frag.add_include('compiled.inc')
|
frag.add_include('compiled.inc')
|
||||||
frag.write('vec3 basecol;')
|
frag.write('vec3 basecol;')
|
||||||
frag.write('float roughness;')
|
frag.write('float roughness;')
|
||||||
|
@ -512,6 +479,7 @@ def make_forward_solid(con_mesh):
|
||||||
vert.write('vcolor = col.rgb;')
|
vert.write('vcolor = col.rgb;')
|
||||||
|
|
||||||
make_attrib.write_norpos(con_mesh, vert, write_nor=False)
|
make_attrib.write_norpos(con_mesh, vert, write_nor=False)
|
||||||
|
make_attrib.write_vertpos(vert)
|
||||||
|
|
||||||
frag.add_out('vec4 fragColor')
|
frag.add_out('vec4 fragColor')
|
||||||
if blend and parse_opacity:
|
if blend and parse_opacity:
|
||||||
|
|
|
@ -64,6 +64,16 @@ class ParserState:
|
||||||
self.out_opacity: floatstr = '1.0'
|
self.out_opacity: floatstr = '1.0'
|
||||||
self.out_emission: floatstr = '0.0'
|
self.out_emission: floatstr = '0.0'
|
||||||
|
|
||||||
|
def reset_outs(self):
|
||||||
|
"""Reset the shader output values to their default values."""
|
||||||
|
self.out_basecol = 'vec3(0.8)'
|
||||||
|
self.out_roughness = '0.0'
|
||||||
|
self.out_metallic = '0.0'
|
||||||
|
self.out_occlusion = '1.0'
|
||||||
|
self.out_specular = '1.0'
|
||||||
|
self.out_opacity = '1.0'
|
||||||
|
self.out_emission = '0.0'
|
||||||
|
|
||||||
def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr]:
|
def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr]:
|
||||||
"""Return the shader output values as a tuple."""
|
"""Return the shader output values as a tuple."""
|
||||||
return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular,
|
return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular,
|
||||||
|
|
|
@ -109,19 +109,27 @@ class ShaderContext:
|
||||||
def get(self):
|
def get(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def add_constant(self, ctype, name, link=None):
|
def add_constant(self, ctype, name, link=None, default_value=None, is_arm_mat_param=None):
|
||||||
for c in self.constants:
|
for c in self.constants:
|
||||||
if c['name'] == name:
|
if c['name'] == name:
|
||||||
return
|
return
|
||||||
|
|
||||||
c = { 'name': name, 'type': ctype }
|
c = { 'name': name, 'type': ctype}
|
||||||
if link != None:
|
if link is not None:
|
||||||
c['link'] = link
|
c['link'] = link
|
||||||
|
if default_value is not None:
|
||||||
|
if ctype == 'float':
|
||||||
|
c['float'] = default_value
|
||||||
|
if ctype == 'vec3':
|
||||||
|
c['vec3'] = default_value
|
||||||
|
if is_arm_mat_param is not None:
|
||||||
|
c['is_arm_parameter'] = 'true'
|
||||||
self.constants.append(c)
|
self.constants.append(c)
|
||||||
|
|
||||||
def add_texture_unit(self, name, link=None, is_image=None,
|
def add_texture_unit(self, name, link=None, is_image=None,
|
||||||
addr_u=None, addr_v=None,
|
addr_u=None, addr_v=None,
|
||||||
filter_min=None, filter_mag=None, mipmap_filter=None):
|
filter_min=None, filter_mag=None, mipmap_filter=None,
|
||||||
|
default_value=None, is_arm_mat_param=None):
|
||||||
for c in self.tunits:
|
for c in self.tunits:
|
||||||
if c['name'] == name:
|
if c['name'] == name:
|
||||||
return
|
return
|
||||||
|
@ -141,6 +149,10 @@ class ShaderContext:
|
||||||
c['filter_mag'] = filter_mag
|
c['filter_mag'] = filter_mag
|
||||||
if mipmap_filter is not None:
|
if mipmap_filter is not None:
|
||||||
c['mipmap_filter'] = mipmap_filter
|
c['mipmap_filter'] = mipmap_filter
|
||||||
|
if default_value is not None:
|
||||||
|
c['default_image_file'] = default_value
|
||||||
|
if is_arm_mat_param is not None:
|
||||||
|
c['is_arm_parameter'] = 'true'
|
||||||
|
|
||||||
self.tunits.append(c)
|
self.tunits.append(c)
|
||||||
|
|
||||||
|
@ -238,7 +250,7 @@ class Shader:
|
||||||
def add_uniform(self, s, link=None, included=False, top=False,
|
def add_uniform(self, s, link=None, included=False, top=False,
|
||||||
tex_addr_u=None, tex_addr_v=None,
|
tex_addr_u=None, tex_addr_v=None,
|
||||||
tex_filter_min=None, tex_filter_mag=None,
|
tex_filter_min=None, tex_filter_mag=None,
|
||||||
tex_mipmap_filter=None):
|
tex_mipmap_filter=None, default_value=None, is_arm_mat_param=None):
|
||||||
ar = s.split(' ')
|
ar = s.split(' ')
|
||||||
# layout(RGBA8) image3D voxels
|
# layout(RGBA8) image3D voxels
|
||||||
utype = ar[-2]
|
utype = ar[-2]
|
||||||
|
@ -257,7 +269,8 @@ class Shader:
|
||||||
self.context.add_texture_unit(
|
self.context.add_texture_unit(
|
||||||
uname, link, is_image,
|
uname, link, is_image,
|
||||||
tex_addr_u, tex_addr_v,
|
tex_addr_u, tex_addr_v,
|
||||||
tex_filter_min, tex_filter_mag, tex_mipmap_filter)
|
tex_filter_min, tex_filter_mag, tex_mipmap_filter,
|
||||||
|
default_value=default_value, is_arm_mat_param=is_arm_mat_param)
|
||||||
else:
|
else:
|
||||||
# Prefer vec4[] for d3d to avoid padding
|
# Prefer vec4[] for d3d to avoid padding
|
||||||
if ar[0] == 'float' and '[' in ar[1]:
|
if ar[0] == 'float' and '[' in ar[1]:
|
||||||
|
@ -266,7 +279,7 @@ class Shader:
|
||||||
elif ar[0] == 'vec4' and '[' in ar[1]:
|
elif ar[0] == 'vec4' and '[' in ar[1]:
|
||||||
ar[0] = 'floats'
|
ar[0] = 'floats'
|
||||||
ar[1] = ar[1].split('[', 1)[0]
|
ar[1] = ar[1].split('[', 1)[0]
|
||||||
self.context.add_constant(ar[0], ar[1], link=link)
|
self.context.add_constant(ar[0], ar[1], link=link, default_value=default_value, is_arm_mat_param=is_arm_mat_param)
|
||||||
if top:
|
if top:
|
||||||
if not included and s not in self.uniforms_top:
|
if not included and s not in self.uniforms_top:
|
||||||
self.uniforms_top.append(s)
|
self.uniforms_top.append(s)
|
||||||
|
|
|
@ -363,6 +363,7 @@ class ReplaceNodesOperator(bpy.types.Operator):
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
arm.logicnode.arm_nodes.register()
|
||||||
arm.logicnode.arm_sockets.register()
|
arm.logicnode.arm_sockets.register()
|
||||||
|
|
||||||
bpy.utils.register_class(ArmLogicTree)
|
bpy.utils.register_class(ArmLogicTree)
|
||||||
|
@ -403,3 +404,4 @@ def unregister():
|
||||||
bpy.utils.register_class(ARM_MT_NodeAddOverride.overridden_menu)
|
bpy.utils.register_class(ARM_MT_NodeAddOverride.overridden_menu)
|
||||||
|
|
||||||
arm.logicnode.arm_sockets.unregister()
|
arm.logicnode.arm_sockets.unregister()
|
||||||
|
arm.logicnode.arm_nodes.unregister()
|
||||||
|
|
|
@ -11,7 +11,7 @@ import arm.proxy
|
||||||
import arm.utils
|
import arm.utils
|
||||||
|
|
||||||
# Armory version
|
# Armory version
|
||||||
arm_version = '2021.5'
|
arm_version = '2021.7'
|
||||||
arm_commit = '$Id$'
|
arm_commit = '$Id$'
|
||||||
|
|
||||||
def get_project_html5_copy(self):
|
def get_project_html5_copy(self):
|
||||||
|
@ -286,6 +286,7 @@ def init_properties():
|
||||||
default=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False),
|
default=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False),
|
||||||
size=20,
|
size=20,
|
||||||
subtype='LAYER')
|
subtype='LAYER')
|
||||||
|
bpy.types.Object.arm_relative_physics_constraint = BoolProperty(name="Relative Physics Constraint", description="Add physics constraint relative to the parent object or collection when spawned", default=False)
|
||||||
bpy.types.Object.arm_animation_enabled = BoolProperty(name="Animation", description="Enable skinning & timeline animation", default=True)
|
bpy.types.Object.arm_animation_enabled = BoolProperty(name="Animation", description="Enable skinning & timeline animation", default=True)
|
||||||
bpy.types.Object.arm_tilesheet = StringProperty(name="Tilesheet", description="Set tilesheet animation", default='')
|
bpy.types.Object.arm_tilesheet = StringProperty(name="Tilesheet", description="Set tilesheet animation", default='')
|
||||||
bpy.types.Object.arm_tilesheet_action = StringProperty(name="Tilesheet Action", description="Set startup action", default='')
|
bpy.types.Object.arm_tilesheet_action = StringProperty(name="Tilesheet Action", description="Set startup action", default='')
|
||||||
|
|
|
@ -211,8 +211,11 @@ class ARM_PT_PhysicsPropsPanel(bpy.types.Panel):
|
||||||
layout.prop(obj, 'arm_rb_trigger')
|
layout.prop(obj, 'arm_rb_trigger')
|
||||||
layout.prop(obj, 'arm_rb_ccd')
|
layout.prop(obj, 'arm_rb_ccd')
|
||||||
|
|
||||||
if obj.soft_body != None:
|
if obj.soft_body is not None:
|
||||||
layout.prop(obj, 'arm_soft_body_margin')
|
layout.prop(obj, 'arm_soft_body_margin')
|
||||||
|
|
||||||
|
if obj.rigid_body_constraint is not None:
|
||||||
|
layout.prop(obj, 'arm_relative_physics_constraint')
|
||||||
|
|
||||||
# Menu in data region
|
# Menu in data region
|
||||||
class ARM_PT_DataPropsPanel(bpy.types.Panel):
|
class ARM_PT_DataPropsPanel(bpy.types.Panel):
|
||||||
|
@ -626,7 +629,6 @@ class ARM_PT_ArmoryExporterPanel(bpy.types.Panel):
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.alignment = 'EXPAND'
|
row.alignment = 'EXPAND'
|
||||||
row.scale_y = 1.3
|
row.scale_y = 1.3
|
||||||
row.enabled = wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0
|
|
||||||
row.operator("arm.build_project", icon="MOD_BUILD")
|
row.operator("arm.build_project", icon="MOD_BUILD")
|
||||||
# row.operator("arm.patch_project")
|
# row.operator("arm.patch_project")
|
||||||
row.operator("arm.publish_project", icon="EXPORT")
|
row.operator("arm.publish_project", icon="EXPORT")
|
||||||
|
@ -1053,10 +1055,15 @@ class ArmoryStopButton(bpy.types.Operator):
|
||||||
return{'FINISHED'}
|
return{'FINISHED'}
|
||||||
|
|
||||||
class ArmoryBuildProjectButton(bpy.types.Operator):
|
class ArmoryBuildProjectButton(bpy.types.Operator):
|
||||||
'''Build and compile project'''
|
"""Build and compile project"""
|
||||||
bl_idname = 'arm.build_project'
|
bl_idname = 'arm.build_project'
|
||||||
bl_label = 'Build'
|
bl_label = 'Build'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
wrd = bpy.data.worlds['Arm']
|
||||||
|
return wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# Compare version Blender and Armory (major, minor)
|
# Compare version Blender and Armory (major, minor)
|
||||||
if not arm.utils.compare_version_blender_arm():
|
if not arm.utils.compare_version_blender_arm():
|
||||||
|
@ -1093,10 +1100,15 @@ class ArmoryBuildProjectButton(bpy.types.Operator):
|
||||||
return{'FINISHED'}
|
return{'FINISHED'}
|
||||||
|
|
||||||
class ArmoryPublishProjectButton(bpy.types.Operator):
|
class ArmoryPublishProjectButton(bpy.types.Operator):
|
||||||
'''Build project ready for publishing'''
|
"""Build project ready for publishing."""
|
||||||
bl_idname = 'arm.publish_project'
|
bl_idname = 'arm.publish_project'
|
||||||
bl_label = 'Publish'
|
bl_label = 'Publish'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
wrd = bpy.data.worlds['Arm']
|
||||||
|
return wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# Compare version Blender and Armory (major, minor)
|
# Compare version Blender and Armory (major, minor)
|
||||||
if not arm.utils.compare_version_blender_arm():
|
if not arm.utils.compare_version_blender_arm():
|
||||||
|
|
|
@ -300,7 +300,7 @@ script_warnings: Dict[str, List[Tuple[str, str]]] = {} # Script name -> List of
|
||||||
# See https://regex101.com/r/bbrCzN/8
|
# See https://regex101.com/r/bbrCzN/8
|
||||||
RX_MODIFIERS = r'(?P<modifiers>(?:public\s+|private\s+|static\s+|inline\s+|final\s+)*)?' # Optional modifiers
|
RX_MODIFIERS = r'(?P<modifiers>(?:public\s+|private\s+|static\s+|inline\s+|final\s+)*)?' # Optional modifiers
|
||||||
RX_IDENTIFIER = r'(?P<identifier>[_$a-z]+[_a-z0-9]*)' # Variable name, follow Haxe rules
|
RX_IDENTIFIER = r'(?P<identifier>[_$a-z]+[_a-z0-9]*)' # Variable name, follow Haxe rules
|
||||||
RX_TYPE = r'(?::\s+(?P<type>[_a-z]+[\._a-z0-9]*))?' # Optional type annotation
|
RX_TYPE = r'(?:\s*:\s*(?P<type>[_a-z]+[\._a-z0-9]*))?' # Optional type annotation
|
||||||
RX_VALUE = r'(?:\s*=\s*(?P<value>(?:\".*\")|(?:[^;]+)|))?' # Optional default value
|
RX_VALUE = r'(?:\s*=\s*(?P<value>(?:\".*\")|(?:[^;]+)|))?' # Optional default value
|
||||||
|
|
||||||
PROP_REGEX_RAW = fr'@prop\s+{RX_MODIFIERS}(?P<attr_type>var|final)\s+{RX_IDENTIFIER}{RX_TYPE}{RX_VALUE};'
|
PROP_REGEX_RAW = fr'@prop\s+{RX_MODIFIERS}(?P<attr_type>var|final)\s+{RX_IDENTIFIER}{RX_TYPE}{RX_VALUE};'
|
||||||
|
|
Loading…
Reference in a new issue