Merge pull request #2047 from QuantumCoderQC/bcon_nodes

New Physics Constraint nodes. Improve Bullet Physics constraint trait
This commit is contained in:
Lubos Lenco 2020-12-06 09:19:14 +01:00 committed by GitHub
commit 9f35d55bd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 708 additions and 34 deletions

View file

@ -0,0 +1,135 @@
package armory.logicnode;
import armory.trait.physics.PhysicsConstraint;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintType;
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object;
import armory.trait.physics.RigidBody;
import armory.logicnode.PhysicsConstraintNode;
class AddPhysicsConstraintNode extends LogicNode {
public var property0: String;//Type
public var object: Object;
public var rb1: Object;
public var rb2: Object;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var pivotObject:Object = inputs[1].get();
rb1 = inputs[2].get();
rb2 = inputs[3].get();
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
var breakingThreshold: Float = inputs[6].get();
var type: ConstraintType = 0;
if (pivotObject == null || rb1 == null || rb2 == null) return;
#if arm_physics
var con: PhysicsConstraint = pivotObject.getTrait(PhysicsConstraint);
if(con == null)
{
switch(property0)
{
case 'Fixed':
type = Fixed;
case 'Point':
type = Point;
case 'Hinge':
type = Hinge;
case 'Slider':
type = Slider;
case 'Piston':
type = Piston;
case 'Generic Spring':
type = Generic;
}
if(! breakable) breakingThreshold = 0.0;
if(type != Generic) {
con = new PhysicsConstraint(rb1, rb2, type, disableCollisions, breakingThreshold);
switch (type)
{
case Hinge:
var setLimit:Bool = inputs[7].get();
var low:Float = inputs[8].get();
var up:Float = inputs[9].get();
con.setHingeConstraintLimits(setLimit, low, up);
case Slider:
var setLimit:Bool = inputs[7].get();
var low:Float = inputs[8].get();
var up:Float = inputs[9].get();
con.setSliderConstraintLimits(setLimit, low, up);
case Piston:
var setLinLimit:Bool = inputs[7].get();
var linLow:Float = inputs[8].get();
var linUp:Float = inputs[9].get();
var setAngLimit:Bool = inputs[10].get();
var angLow:Float = inputs[11].get();
var angUp:Float = inputs[12].get();
con.setPistonConstraintLimits(setLinLimit, linLow, linUp, setAngLimit, angLow, angUp);
default:
}
}
else
{
var spring: Bool = false;
var prop: PhysicsConstraintNode;
for(inp in 7...inputs.length)
{
prop = inputs[inp].get();
if(prop == null) continue;
if(prop.isSpring)
{
spring = true;
break;
}
}
if(spring) {
con = new PhysicsConstraint(rb1, rb2, GenericSpring, disableCollisions, breakingThreshold);
}
else {
con = new PhysicsConstraint(rb1, rb2, Generic, disableCollisions, breakingThreshold);
}
for(inp in 7...inputs.length)
{
prop = inputs[inp].get();
if(prop == null) continue;
(inp + ': ');
if(prop.isSpring)
{
con.setSpringParams(prop.isSpring, prop.value1, prop.value2, prop.axis, prop.isAngular);
}
else
{
con.setGenericConstraintLimits(true, prop.value1, prop.value2, prop.axis, prop.isAngular);
}
}
}
pivotObject.addTrait(con);
}
#end
runOutput(0);
}
}

View file

@ -90,8 +90,7 @@ class AddRigidBodyNode extends LogicNode {
rb.angularDamping = angDamp;
if(margin) rb.collisionMargin = marginLen;
if(useDeactiv) {
rb.setDeactivation(true);
rb.setDeactivationParams(linearVelThreshold, angVelThreshold, 0.0);
rb.setUpDeactivation(true, linearVelThreshold, angVelThreshold, 0.0);
}
}

View file

@ -0,0 +1,52 @@
package armory.logicnode;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object;
class PhysicsConstraintNode extends LogicNode {
public var property0: String;//Linear or Angular
public var property1: String;//Axis
public var property2: String;//Is a spring
public var value1: Float;//Lower limit or Spring Stiffness
public var value2: Float;//Upper limit or Spring Damping
public var isAngular: Bool;
public var axis: ConstraintAxis;
public var isSpring: Bool;
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): PhysicsConstraintNode {
value1 = inputs[0].get();
value2 = inputs[1].get();
if(property0 == 'Linear') {
isAngular = false;
}
else{
isAngular = true;
}
if(property2 == 'true'){
isSpring = true;
}
else {
isSpring = false;
}
switch (property1){
case 'X':
axis = X;
case 'Y':
axis = Y;
case 'Z':
axis = Z;
}
return this;
}
}

View file

@ -1,19 +1,29 @@
package armory.trait.physics.bullet;
import iron.Scene;
import iron.object.Object;
#if arm_bullet
import Math;
import iron.math.Quat;
import armory.trait.physics.RigidBody;
import armory.trait.physics.PhysicsWorld;
/**
* A trait to add Bullet physics constraints
**/
class PhysicsConstraint extends iron.Trait {
var body1: String;
var body2: String;
var type: String;
var disableCollisions: Bool;
static var nextId:Int = 0;
public var id:Int = 0;
var physics: PhysicsWorld;
var body1: Object;
var body2: Object;
var type: ConstraintType;
public var disableCollisions: Bool;
var breakingThreshold: Float;
var limits: Array<Float>;
var con: bullet.Bt.TypedConstraint = null;
public var con: bullet.Bt.TypedConstraint = null;
static var nullvec = true;
static var vec1: bullet.Bt.Vector3;
@ -23,7 +33,27 @@ class PhysicsConstraint extends iron.Trait {
static var trans2: bullet.Bt.Transform;
static var transt: bullet.Bt.Transform;
public function new(body1: String, body2: String, type: String, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) {
/**
* Function to initialize physics constraint trait.
*
* @param object Pivot object to which this constraint trait will be added. The constraint limits are applied along the local axes of this object. This object need not
* be a Rigid Body. Typically an `Empty` object may be used. Moving/rotating/parenting this pivot object has no effect once the constraint trait is added. Removing
* the pivot object removes the constraint.
*
* @param body1 First rigid body to be constrained. This rigid body may be constrained by other constraints.
*
* @param body2 Second rigid body to be constrained. This rigid body may be constrained by other constraints.
*
* @param type Type of the constraint.
*
* @param disableCollisions Disable collisions between constrained objects.
*
* @param breakingThreshold Break the constraint if stress on this constraint exceeds this value. Set to 0 to make un-breakable.
*
* @param limits Constraint limits. This may be set before adding the trait to pivot object using the set limits functions.
*
**/
public function new(body1: Object, body2: Object, type: ConstraintType, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) {
super();
if (nullvec) {
@ -40,46 +70,51 @@ class PhysicsConstraint extends iron.Trait {
this.type = type;
this.disableCollisions = disableCollisions;
this.breakingThreshold = breakingThreshold;
if(limits == null) limits = [for(i in 0...36) 0];
this.limits = limits;
notifyOnInit(init);
notifyOnAdd(init);
}
function init() {
var physics = PhysicsWorld.active;
var target1 = iron.Scene.active.getChild(body1);
var target2 = iron.Scene.active.getChild(body2);
if (target1 == null || target2 == null) return;
physics = PhysicsWorld.active;
var target1 = body1;
var target2 = body2;
if (target1 == null || target2 == null) return;//no objects selected
var rb1: RigidBody = target1.getTrait(RigidBody);
var rb2: RigidBody = target2.getTrait(RigidBody);
if (rb1 != null && rb1.ready && rb2 != null && rb2.ready) {
if (rb1 != null && rb1.ready && rb2 != null && rb2.ready) {//Check if rigid bodies are ready
var t = object.transform;
var t1 = target1.transform;
var t2 = target2.transform;
trans1.setIdentity();
vec1.setX(t.worldx() - t1.worldx());
vec1.setY(t.worldy() - t1.worldy());
vec1.setZ(t.worldz() - t1.worldz());
trans1.setOrigin(vec1);
//trans1.setRotation(new bullet.Bt.Quaternion(t1.rot.x, t1.rot.y, t1.rot.z, t1.rot.w));
trans2.setIdentity();
vec2.setX(t.worldx() - t2.worldx());
vec2.setY(t.worldy() - t2.worldy());
vec2.setZ(t.worldz() - t2.worldz());
trans2.setOrigin(vec2);
//trans2.setRotation(new bullet.Bt.Quaternion(t2.rot.x, t2.rot.y, t2.rot.z, t2.rot.w));
trans1.setRotation(new bullet.Bt.Quaternion(t.rot.x, t.rot.y, t.rot.z, t.rot.w));
trans2.setRotation(new bullet.Bt.Quaternion(t.rot.x, t.rot.y, t.rot.z, t.rot.w));
if (type == "GENERIC" || type == "FIXED") {
if (type == Generic || type == Fixed) {
#if hl
var c = new bullet.Bt.Generic6DofConstraint(rb1.body, rb2.body, trans1, trans2, false);
#else
var c = bullet.Bt.Generic6DofConstraint.new2(rb1.body, rb2.body, trans1, trans2, false);
#end
if (type == "FIXED") {
if (type == Fixed) {
vec1.setX(0);
vec1.setY(0);
vec1.setZ(0);
@ -88,8 +123,7 @@ class PhysicsConstraint extends iron.Trait {
c.setAngularLowerLimit(vec1);
c.setAngularUpperLimit(vec1);
}
else if (type == "GENERIC") {
else if (type == ConstraintType.Generic) {
if (limits[0] == 0) {
limits[1] = 1.0;
limits[2] = -1.0;
@ -133,8 +167,7 @@ class PhysicsConstraint extends iron.Trait {
}
con = cast c;
}
else if (type == "GENERIC_SPRING"){
else if (type == ConstraintType.GenericSpring){
var c = new bullet.Bt.Generic6DofSpringConstraint(rb1.body, rb2.body, trans1, trans2, false);
if (limits[0] == 0) {
@ -228,12 +261,11 @@ class PhysicsConstraint extends iron.Trait {
con = cast c;
}
else if (type == "POINT"){
else if (type == ConstraintType.Point){
var c = new bullet.Bt.Point2PointConstraint(rb1.body, rb2.body, vec1, vec2);
con = cast c;
}
else if (type == "HINGE") {
else if (type == ConstraintType.Hinge) {
var axis = vec3;
var _softness: Float = 0.9;
var _biasFactor: Float = 0.3;
@ -243,7 +275,7 @@ class PhysicsConstraint extends iron.Trait {
axis.setY(t.up().y);
axis.setZ(t.up().z);
var c = new bullet.Bt.HingeConstraint(rb1.body, rb2.body, vec1, vec2, axis, axis);
var c = new bullet.Bt.HingeConstraint(rb1.body, rb2.body, vec1, vec2, axis, axis, false);
if (limits[0] != 0) {
c.setLimit(limits[1], limits[2], _softness, _biasFactor, _relaxationFactor);
@ -251,7 +283,7 @@ class PhysicsConstraint extends iron.Trait {
con = cast c;
}
else if (type == "SLIDER") {
else if (type == ConstraintType.Slider) {
var c = new bullet.Bt.SliderConstraint(rb1.body, rb2.body, trans1, trans2, true);
if (limits[0] != 0) {
@ -261,7 +293,7 @@ class PhysicsConstraint extends iron.Trait {
con = cast c;
}
else if (type == "PISTON") {
else if (type == ConstraintType.Piston) {
var c = new bullet.Bt.SliderConstraint(rb1.body, rb2.body, trans1, trans2, true);
if (limits[0] != 0) {
@ -282,16 +314,143 @@ class PhysicsConstraint extends iron.Trait {
if (breakingThreshold > 0) con.setBreakingImpulseThreshold(breakingThreshold);
physics.world.addConstraint(con, disableCollisions);
physics.addPhysicsConstraint(this);
id = nextId;
nextId++;
notifyOnRemove(removeFromWorld);
}
else notifyOnInit(init); // Rigid body not initialized yet
else this.remove(); // Rigid body not initialized yet. Remove trait without adding constraint
}
public function removeFromWorld() {
physics.removePhysicsConstraint(this);
}
/**
* Function to set constraint limits when using Hinge constraint. May be used after initalizing this trait but before adding it
* to the pivot object
**/
public function setHingeConstraintLimits(angLimit: Bool, lowerAngLimit: Float, upperAngLimit: Float) {
angLimit? limits[0] = 1 : limits[0] = 0;
limits[1] = lowerAngLimit * (Math.PI/ 180);
limits[2] = upperAngLimit * (Math.PI/ 180);
}
/**
* Function to set constraint limits when using Slider constraint. May be used after initalizing this trait but before adding it
* to the pivot object
**/
public function setSliderConstraintLimits(linLimit: Bool, lowerLinLimit: Float, upperLinLimit: Float) {
linLimit? limits[0] = 1 : limits[0] = 0;
limits[1] = lowerLinLimit;
limits[2] = upperLinLimit;
}
/**
* Function to set constraint limits when using Piston constraint. May be used after initalizing this trait but before adding it
* to the pivot object
**/
public function setPistonConstraintLimits(linLimit: Bool, lowerLinLimit: Float, upperLinLimit: Float, angLimit: Bool, lowerAngLimit: Float, upperAngLimit: Float) {
linLimit? limits[0] = 1 : limits[0] = 0;
limits[1] = lowerLinLimit;
limits[2] = upperLinLimit;
angLimit? limits[3] = 1 : limits[3] = 0;
limits[4] = lowerAngLimit * (Math.PI/ 180);
limits[5] = upperAngLimit * (Math.PI/ 180);
}
/**
* Function to set customized constraint limits when using Generic/ Generic Spring constraint. May be used after initalizing this trait but before adding it
* to the pivot object. Multiple constarints may be set by calling this function with different parameters.
**/
public function setGenericConstraintLimits(setLimit: Bool = false, lowerLimit: Float = 1.0, upperLimit: Float = -1.0, axis: ConstraintAxis = X, isAngular: Bool = false) {
var i = 0;
var j = 0;
var radian = (Math.PI/ 180);
switch (axis){
case X:
i = 0;
case Y:
i = 3;
case Z:
i = 6;
}
isAngular? j = 9 : j = 0;
isAngular? radian = (Math.PI/ 180) : radian = 1;
setLimit? limits[i + j] = 1 : 0;
limits[i + j + 1] = lowerLimit * radian;
limits[i + j + 2] = upperLimit * radian;
}
/**
* Function to set customized spring parameters when using Generic/ Generic Spring constraint. May be used after initalizing this trait but before adding it
* to the pivot object. Multiple parameters to different axes may be set by calling this function with different parameters.
**/
public function setSpringParams(setSpring: Bool = false, stiffness: Float = 10.0, damping: Float = 0.5, axis: ConstraintAxis = X, isAngular: Bool = false) {
var i = 0;
var j = 0;
switch (axis){
case X:
i = 18;
case Y:
i = 21;
case Z:
i = 24;
}
isAngular? j = 9 : j = 0;
setSpring? limits[i + j] = 1 : 0;
limits[i + j + 1] = stiffness;
limits[i + j + 2] = damping;
}
public function delete() {
#if js
bullet.Bt.Ammo.destroy(con);
#else
con.delete();
#end
}
}
@:enum abstract ConstraintType(Int) from Int to Int {
var Fixed = 0;
var Point = 1;
var Hinge = 2;
var Slider = 3;
var Piston = 4;
var Generic = 5;
var GenericSpring = 6;
var Motor = 7;
}
@:enum abstract ConstraintAxis(Int) from Int to Int {
var X = 0;
var Y = 1;
var Z = 2;
}
#end

View file

@ -0,0 +1,41 @@
package armory.trait.physics.bullet;
import iron.Scene;
import iron.object.Object;
#if arm_bullet
/**
* A helper trait to add physics constraints when exporting via Blender.
* This trait will be automatically removed once the constraint is added. Note that this trait
* uses object names instead of object reference.
**/
class PhysicsConstraintExportHelper extends iron.Trait {
var body1: String;
var body2: String;
var type: Int;
var disableCollisions: Bool;
var breakingThreshold: Float;
var limits: Array<Float>;
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) {
super();
this.body1 = body1;
this.body2 = body2;
this.type = type;
this.disableCollisions = disableCollisions;
this.breakingThreshold = breakingThreshold;
this.limits = limits;
notifyOnInit(init);
}
function init() {
var target1 = Scene.active.getChild(body1);
var target2 = Scene.active.getChild(body2);
object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits));
this.remove();
}
}
#end

View file

@ -51,6 +51,7 @@ class PhysicsWorld extends Trait {
var contacts: Array<ContactPair>;
var preUpdates: Array<Void->Void> = null;
public var rbMap: Map<Int, RigidBody>;
public var conMap: Map<Int, PhysicsConstraint>;
public var timeScale = 1.0;
var timeStep = 1 / 60;
var maxSteps = 1;
@ -97,6 +98,7 @@ class PhysicsWorld extends Trait {
contacts = [];
rbMap = new Map();
conMap = new Map();
active = this;
// Ensure physics are updated first in the lateUpdate list
@ -164,6 +166,11 @@ class PhysicsWorld extends Trait {
rbMap.set(body.id, body);
}
public function addPhysicsConstraint(constraint: PhysicsConstraint) {
world.addConstraint(constraint.con, constraint.disableCollisions);
conMap.set(constraint.id, constraint);
}
public function removeRigidBody(body: RigidBody) {
if (body.destroyed) return;
body.destroyed = true;
@ -172,6 +179,12 @@ class PhysicsWorld extends Trait {
body.delete();
}
public function removePhysicsConstraint(constraint: PhysicsConstraint) {
if(world != null) world.removeConstraint(constraint.con);
conMap.remove(constraint.id);
constraint.delete();
}
// public function addKinematicCharacterController(controller:KinematicCharacterController) {
// if (!pairCache){ // Only create PairCache if needed
// world.getPairCache().setInternalGhostPairCallback(BtGhostPairCallbackPointer.create());

View file

@ -392,8 +392,11 @@ class RigidBody extends iron.Trait {
// body.setDeactivationTime(time); // not available in ammo
}
public function setDeactivation(useDeactivation: Bool) {
public function setUpDeactivation(useDeactivation: Bool, linearThreshold: Float, angularThreshold: Float, time: Float) {
this.useDeactivation = useDeactivation;
this.deactivationParams[0] = linearThreshold;
this.deactivationParams[1] = angularThreshold;
this.deactivationParams[2] = time;
}
public function isTriggerObject(isTrigger: Bool) {

View file

@ -2710,6 +2710,8 @@ class ArmoryExporter:
rb2 = rbc.object2
if rb1 is None or rb2 is None:
return
if rbc.type == "MOTOR":
return
ArmoryExporter.export_physics = True
phys_pkg = 'bullet' if bpy.data.worlds['Arm'].arm_physics_engine == 'Bullet' else 'oimo'
@ -2717,16 +2719,18 @@ class ArmoryExporter:
trait = {
'type': 'Script',
'class_name': 'armory.trait.physics.' + phys_pkg + '.PhysicsConstraint',
'class_name': 'armory.trait.physics.' + phys_pkg + '.PhysicsConstraintExportHelper',
'parameters': [
"'" + rb1.name + "'",
"'" + rb2.name + "'",
"'" + rbc.type + "'",
str(rbc.disable_collisions).lower(),
str(breaking_threshold)
]
}
if rbc.type == "FIXED":
trait['parameters'].insert(2,str(0))
if rbc.type == "POINT":
trait['parameters'].insert(2,str(1))
if rbc.type == "GENERIC":
limits = [
1 if rbc.use_limit_lin_x else 0,
@ -2748,6 +2752,7 @@ class ArmoryExporter:
rbc.limit_ang_z_lower,
rbc.limit_ang_z_upper
]
trait['parameters'].insert(2,str(5))
trait['parameters'].append(str(limits))
if rbc.type == "GENERIC_SPRING":
limits = [
@ -2788,6 +2793,7 @@ class ArmoryExporter:
rbc.spring_stiffness_ang_z,
rbc.spring_damping_ang_z
]
trait['parameters'].insert(2,str(6))
trait['parameters'].append(str(limits))
if rbc.type == "HINGE":
limits = [
@ -2795,6 +2801,7 @@ class ArmoryExporter:
rbc.limit_ang_z_lower,
rbc.limit_ang_z_upper
]
trait['parameters'].insert(2,str(2))
trait['parameters'].append(str(limits))
if rbc.type == "SLIDER":
limits = [
@ -2802,6 +2809,7 @@ class ArmoryExporter:
rbc.limit_lin_x_lower,
rbc.limit_lin_x_upper
]
trait['parameters'].insert(2,str(3))
trait['parameters'].append(str(limits))
if rbc.type == "PISTON":
limits = [
@ -2812,6 +2820,7 @@ class ArmoryExporter:
rbc.limit_ang_x_lower,
rbc.limit_ang_x_upper
]
trait['parameters'].insert(2,str(4))
trait['parameters'].append(str(limits))
o['traits'].append(trait)

View file

@ -0,0 +1,176 @@
from arm.logicnode.arm_nodes import *
class AddPhysicsConstraintNode(ArmLogicTreeNode):
"""
Add a physics constraint to constrain two rigid bodies if not already present.
@option Fixed: No fredom of movement. Relative positions and rotations of rigid bodies are fixed
@option Point: Both rigid bodies are constrained at the pivot object.
@option Hinge: Constrained objects can move only along angular Z axis of the pivot object.
@option Slider: Constrained objects can move only along linear X axis of the pivot object.
@option Piston: Constrained objects can move only and rotate along X axis of the pivot object.
@option GenericSpring: Fully custimizable generic 6 degree of freedom constraint with optional springs. All liner and angular axes can be constrained
along with spring options. Use `Physics Constraint Node` to set a combination of constraints and springs.
@seeNode Physics Constraint
@input Pivot object: The object to which the physics constraint traint is applied. This object will not be affected by the constraint
but is necessary to specify the constraint axes and location. Hence, the pivot object need not be a rigid body. Typically an `Empty`
object may be used. Each pivot object can have only one constraint trait applied. Moving/rotating/parenting the pivot object after the constraint
is applied has no effect. However, removig the pivot object removes the constraint and `RB 1` and `RB 2` are no longer constrained.
@input RB 1: The first rigid body to be constrained. Must be a rigid body. This object can be constrained by more than one constraint.
@input RB 2: The second rigid body to be constrained. Must be a rigid body. This object can be constrained by more than one constraint.
@input Disable Collisions: Disable collisions between `RB 1` and `RB 2`
@input Breakable: Constraint can break if stress on the constraint is more than the set threshold. Disable this option to disable breaking.
@input Breaking threshold: Stress on the constraint above which the constraint breaks. Depends on the mass, velocity of rigid bodies and type of constraint.
@input Limit Lower: Lower limit of the consraint in that particular axis
@input Limit Upper: Upper limit of the constraint in that particular axis. (`lower limit` = `upper limit`) --> Fully constrained. (`lower limit` < `upper limit`) --> Partially constrained
(`lower limit` > `upper limit`) --> Full freedom.
@input Angular limits: Limits to constarin rotation. Specified in degrees. Range (-360 to +360)
@input Add Constarint: Option to add custom constraint to `Generic Spring` type.
"""
bl_idname = 'LNAddPhysicsConstraintNode'
bl_label = 'Add Physics Constraint'
arm_sction = 'add'
arm_version = 1
@staticmethod
def get_enum_id_value(obj, prop_name, value):
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
@staticmethod
def get_count_in(type_name):
return {
'Fixed': 0,
'Point': 1,
'Hinge': 2,
'Slider': 3,
'Piston': 4,
'Generic Spring': 5
}.get(type_name, 0)
def get_enum(self):
return self.get('property0', 0)
def set_enum(self, value):
# Checking the selection of another type
select_current = self.get_enum_id_value(self, 'property0', value)
select_prev = self.property0
#Check if a different type is selected
if select_prev != select_current:
print('New value selected')
# Arguements for type Fixed
if (self.get_count_in(select_current) == 0):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
# Arguements for type Point
if (self.get_count_in(select_current) == 1):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
#Arguements for type Hinge
if (self.get_count_in(select_current) == 2):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
#Z ang limits
self.add_input('NodeSocketBool', 'Z angle')
self.add_input('NodeSocketFloat', 'Z ang lower', -45.0)
self.add_input('NodeSocketFloat', 'Z ang upper', 45.0)
#Arguements for type Slider
if (self.get_count_in(select_current) == 3):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
#X lin limits
self.add_input('NodeSocketBool', 'X linear')
self.add_input('NodeSocketFloat', 'X lin lower')
self.add_input('NodeSocketFloat', 'X lin upper')
#Arguements for type Piston
if (self.get_count_in(select_current) == 4):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
#X lin limits
self.add_input('NodeSocketBool', 'X linear')
self.add_input('NodeSocketFloat', 'X lin lower')
self.add_input('NodeSocketFloat', 'X lin upper')
#X ang limits
self.add_input('NodeSocketBool', 'X angle')
self.add_input('NodeSocketFloat', 'X ang lower', -45.0)
self.add_input('NodeSocketFloat', 'X ang upper', 45.0)
#Arguements for type GenericSpring
if (self.get_count_in(select_current) == 5):
while (len(self.inputs) > 7):
self.inputs.remove(self.inputs.values()[-1])
self['property0'] = value
property0: EnumProperty(
items = [('Fixed', 'Fixed', 'Fixed'),
('Point', 'Point', 'Point'),
('Hinge', 'Hinge', 'Hinge'),
('Slider', 'Slider', 'Slider'),
('Piston', 'Piston', 'Piston'),
('Generic Spring', 'Generic Spring', 'Generic Spring')],
name='Type', default='Fixed', set=set_enum, get=get_enum)
def __init__(self):
array_nodes[str(id(self))] = self
def init(self, context):
super(AddPhysicsConstraintNode, self).init(context)
self.add_input('ArmNodeSocketAction', 'In')
self.add_input('ArmNodeSocketObject', 'Pivot Object')
self.add_input('ArmNodeSocketObject', 'RB 1')
self.add_input('ArmNodeSocketObject', 'RB 2')
self.add_input('NodeSocketBool', 'Disable Collissions')
self.add_input('NodeSocketBool', 'Breakable')
self.add_input('NodeSocketFloat', 'Breaking Threshold')
self.add_output('ArmNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
#GenericSpring:
if (self.get_count_in(self.property0) == 5):
grid0 = layout.grid_flow(row_major=True, columns=1, align=True)
grid0.label(text="Possible Constraints:")
grid0.label(text="Linear [X, Y, Z]")
grid0.label(text="Angular [X, Y, Z]")
grid0.label(text="Spring Linear [X, Y, Z]")
grid0.label(text="Spring Angular [X, Y, Z]")
row = layout.row(align=True)
column = row.column(align=True)
op = column.operator('arm.node_add_input', text='Add Constraint', icon='PLUS', emboss=True)
op.node_index = str(id(self))
op.socket_type = 'NodeSocketShader'
op.name_format = 'Constraint {0}'.format(len(self.inputs) - 6)
column1 = row.column(align=True)
op = column1.operator('arm.node_remove_input', text='', icon='X', emboss=True)
op.node_index = str(id(self))
#Static inputs
if len(self.inputs) < 8:
column1.enabled = False
#Max Possible inputs
if len(self.inputs) > 18:
column.enabled = False

View file

@ -0,0 +1,87 @@
from arm.logicnode.arm_nodes import *
class PhysicsConstraintNode(ArmLogicTreeNode):
"""
Custom physics constraint to add to `Add Physics Constarint` node.
@option Linear/Angualr: Select if constrint is applied along linear or angular axis.
@option Axis: Local axis of the pivot object along which the constraint is applied.
@option Spring: Constraint is a Spring along the selected axis.
@input Limit Lower: Lower limit of the consraint in that particular axis
@input Limit Upper: Upper limit of the constraint in that particular axis. (`lower limit` = `upper limit`) --> Fully constrained. (`lower limit` < `upper limit`) --> Partially constrained
(`lower limit` > `upper limit`) --> Full freedom.
@seeNode Add Physics Constraint
"""
bl_idname = 'LNPhysicsConstraintNode'
bl_label = 'Physics Constraint'
arm_sction = 'add'
arm_version = 1
def update_spring(self, context):
self.update_sockets(context)
property0: EnumProperty(
items = [('Linear', 'Linear', 'Linear'),
('Angular', 'Angular', 'Angular')],
name='Type', default='Linear')
@property
def property1(self):
if(self.property1_ == 'X'):
return 'X'
if(self.property1_ == 'Y'):
return 'Y'
if(self.property1_ == 'Z'):
return 'Z'
property1_: EnumProperty(
items = [('X', 'X', 'X'),
('Y', 'Y', 'Y'),
('Z', 'Z', 'Z')],
name='Axis', default='X')
@property
def property2(self):
if(self.property2_):
return 'true' if self.property2_ else 'false'
property2_: BoolProperty(
name="Spring",
description="Is a spring constraint",
default=False,
update=update_spring
)
def __init__(self):
array_nodes[str(id(self))] = self
def init(self, context):
super(PhysicsConstraintNode, self).init(context)
self.add_input('NodeSocketFloat', 'Lower limit')
self.add_input('NodeSocketFloat', 'Upper limit')
self.add_output('NodeSocketShader', 'Constraint')
def update_sockets(self, context):
while (len(self.inputs) > 0):
self.inputs.remove(self.inputs.values()[-1])
# Add dynamic input sockets
if self.property2_:
self.add_input('NodeSocketFloat', 'Stiffness', 10.0)
self.add_input('NodeSocketFloat', 'Damping', 0.5)
else:
self.add_input('NodeSocketFloat', 'Lower limit')
self.add_input('NodeSocketFloat', 'Upper limit')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
layout.prop(self, 'property1_')
layout.prop(self, 'property2_')