Merge pull request #2047 from QuantumCoderQC/bcon_nodes
New Physics Constraint nodes. Improve Bullet Physics constraint trait
This commit is contained in:
commit
9f35d55bd3
135
Sources/armory/logicnode/AddPhysicsConstraintNode.hx
Normal file
135
Sources/armory/logicnode/AddPhysicsConstraintNode.hx
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
52
Sources/armory/logicnode/PhysicsConstraintNode.hx
Normal file
52
Sources/armory/logicnode/PhysicsConstraintNode.hx
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
176
blender/arm/logicnode/physics/LN_add_physics_constraint.py
Normal file
176
blender/arm/logicnode/physics/LN_add_physics_constraint.py
Normal 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
|
||||
|
87
blender/arm/logicnode/physics/LN_physics_constraint.py
Normal file
87
blender/arm/logicnode/physics/LN_physics_constraint.py
Normal 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_')
|
||||
|
Loading…
Reference in a new issue