Soft bodies

This commit is contained in:
Lubos Lenco 2017-01-10 10:41:06 +01:00
parent 8da28b15a6
commit 13a3526273
6 changed files with 236 additions and 5 deletions

View file

@ -670,9 +670,9 @@ class ConvexHull {
computeFaceNormals();
}
var cb = new Vec4();
var ab = new Vec4();
function computeFaceNormals() {
var cb = new Vec4();
var ab = new Vec4();
for (f in 0...face3s.length) {
var face = face3s[f];
var va = vertices[face.a];

View file

@ -33,7 +33,12 @@ class PhysicsWorld extends Trait {
public static var active:PhysicsWorld = null;
#if arm_physics_soft
public var world:BtSoftRigidDynamicsWorldPointer;
#else
public var world:BtDiscreteDynamicsWorldPointer;
#end
var dispatcher:BtCollisionDispatcherPointer;
var contacts:Array<ContactPair> = [];
var preUpdates:Array<Void->Void> = null;
@ -60,13 +65,29 @@ class PhysicsWorld extends Trait {
var broadphase = BtDbvtBroadphase.create();
#if arm_physics_soft
var collisionConfiguration = BtSoftBodyRigidBodyCollisionConfiguration.create();
#else
var collisionConfiguration = BtDefaultCollisionConfiguration.create();
dispatcher = BtCollisionDispatcher.create(collisionConfiguration);
#end
dispatcher = BtCollisionDispatcher.create(collisionConfiguration);
var solver = BtSequentialImpulseConstraintSolver.create();
world = BtDiscreteDynamicsWorld.create(dispatcher, broadphase, solver, collisionConfiguration);
var gravity = iron.Scene.active.raw.gravity == null ? [0, 0, -9.81] : iron.Scene.active.raw.gravity;
#if arm_physics_soft
var softSolver = BtDefaultSoftBodySolver.create();
world = BtSoftRigidDynamicsWorld.create(dispatcher, broadphase, solver, collisionConfiguration, softSolver);
#if js
world.ptr.getWorldInfo().set_m_gravity(BtVector3.create(gravity[0], gravity[1], gravity[2]).value);
#elseif cpp
world.ptr.getWorldInfo().m_gravity = BtVector3.create(gravity[0], gravity[1], gravity[2]).value;
#end
#else
world = BtDiscreteDynamicsWorld.create(dispatcher, broadphase, solver, collisionConfiguration);
#end
world.ptr.setGravity(BtVector3.create(gravity[0], gravity[1], gravity[2]).value);
Scene.active.notifyOnInit(function() {

View file

@ -0,0 +1,172 @@
package armory.trait.internal;
import iron.math.Vec4;
import iron.math.Mat4;
import iron.Trait;
import iron.object.MeshObject;
import iron.data.MeshData;
import iron.data.SceneFormat;
#if arm_physics_soft
import armory.trait.internal.RigidBody;
import armory.trait.internal.PhysicsWorld;
import haxebullet.Bullet;
#end
class SoftBody extends Trait {
#if (!arm_physics_soft)
public function new() { super(); }
#else
static var physics:PhysicsWorld = null;
var shape:SoftShape;
var bend:Float;
var mass:Float;
var margin:Float;
var softBody:BtSoftBodyPointer;
public function new(shape = SoftShape.Cloth, bend = 0.5, mass = 1.0, margin = 0.04) {
super();
this.shape = shape;
this.bend = bend;
this.mass = mass;
this.margin = margin;
iron.system.Tween.timer(3, function() {
notifyOnInit(init);
});
// notifyOnInit(init);
}
function init() {
if (physics == null) physics = armory.trait.internal.PhysicsWorld.active;
var softBodyHelpers = BtSoftBodyHelpers.create();
var mo = cast(object, MeshObject);
mo.frustumCulling = false;
var mesh = mo.data.mesh;
var positions:haxe.ds.Vector<kha.FastFloat> = cast haxe.ds.Vector.fromData(mesh.positions.copy());
for (i in 0...Std.int(positions.length / 3)) {
positions[i * 3] *= object.transform.scale.x;
positions[i * 3 + 1] *= object.transform.scale.y;
positions[i * 3 + 2] *= object.transform.scale.z;
positions[i * 3] += object.transform.loc.x;
positions[i * 3 + 1] += object.transform.loc.y;
positions[i * 3 + 2] += object.transform.loc.z;
}
object.transform.scale.set(1, 1, 1);
object.transform.loc.set(0, 0, 0);
object.transform.buildMatrix();
var wrdinfo = physics.world.ptr.getWorldInfo();
var vecind = haxe.ds.Vector.fromData(mesh.indices[0]);
var numtri = Std.int(mesh.indices[0].length / 3);
#if js
softBody = softBodyHelpers.CreateFromTriMesh(wrdinfo, positions, vecind, numtri);
#elseif cpp
untyped __cpp__("softBody = softBodyHelpers->CreateFromTriMesh(wrdinfo, positions->Pointer(), vecind->Pointer(), numtri);");
#end
// softBody.ptr.generateClusters(4);
#if js
var cfg = softBody.ptr.get_m_cfg();
cfg.set_viterations(10);
cfg.set_piterations(10);
// cfg.set_collisions(0x0001 + 0x0020 + 0x0040); // self collision
// cfg.set_collisions(0x11); // Soft-rigid, soft-soft
if (shape == SoftShape.Volume) {
cfg.set_kDF(0.1);
cfg.set_kDP(0.01);
cfg.set_kPR(bend);
}
#elseif cpp
var cfg = softBody.ptr.m_cfg;
cfg.viterations = 10;
cfg.piterations = 10;
// cfg.collisions = 0x0001 + 0x0020 + 0x0040;
if (shape == SoftShape.Volume) {
cfg.kDF = 0.1;
cfg.kDP = 0.01;
cfg.kPR = bend;
}
#end
softBody.ptr.setTotalMass(mass, false);
softBody.ptr.getCollisionShape().setMargin(0.2);
physics.world.ptr.addSoftBody(softBody, 1, -1);
softBody.ptr.setActivationState(4);
notifyOnUpdate(update);
}
var va = new Vec4();
var vb = new Vec4();
var vc = new Vec4();
var cb = new Vec4();
var ab = new Vec4();
function update() {
var mesh = cast(object, MeshObject).data.mesh;
var v = mesh.vertexBuffer.lock();
var l = mesh.structLength;
var numVerts = Std.int(v.length / l);
#if js
var nodes = softBody.ptr.get_m_nodes();
#elseif cpp
var nodes = softBody.ptr.m_nodes;
#end
for (i in 0...numVerts) {
var node = nodes.at(i);
#if js
var nodePos = node.get_m_x();
var nodeNor = node.get_m_n();
#elseif cpp
var nodePos = node.m_x;
var nodeNor = node.m_n;
#end
v.set(i * l, nodePos.x());
v.set(i * l + 1, nodePos.y());
v.set(i * l + 2, nodePos.z());
v.set(i * l + 3, nodeNor.x());
v.set(i * l + 4, nodeNor.y());
v.set(i * l + 5, nodeNor.z());
}
// for (i in 0...Std.int(mesh.indices[0].length / 3)) {
// var a = mesh.indices[0][i * 3];
// var b = mesh.indices[0][i * 3 + 1];
// var c = mesh.indices[0][i * 3 + 2];
// va.set(v.get(a * l), v.get(a * l + 1), v.get(a * l + 2));
// vb.set(v.get(b * l), v.get(b * l + 1), v.get(b * l + 2));
// vc.set(v.get(c * l), v.get(c * l + 1), v.get(c * l + 2));
// cb.subvecs(vc, vb);
// ab.subvecs(va, vb);
// cb.cross(ab);
// cb.normalize();
// v.set(a * l + 3, cb.x);
// v.set(a * l + 4, cb.y);
// v.set(a * l + 5, cb.z);
// v.set(b * l + 3, cb.x);
// v.set(b * l + 4, cb.y);
// v.set(b * l + 5, cb.z);
// v.set(c * l + 3, cb.x);
// v.set(c * l + 4, cb.y);
// v.set(c * l + 5, cb.z);
// }
mesh.vertexBuffer.unlock();
}
#end
}
@:enum abstract SoftShape(Int) from Int {
var Cloth = 0;
var Volume = 1;
}

View file

@ -2713,6 +2713,30 @@ class ArmoryExporter:
x['parameters'].append(rb.type == 'PASSIVE')
o['traits'].append(x)
# Soft bodies modifier
soft_type = -1
soft_mod = None
for m in bobject.modifiers:
if m.type == 'CLOTH':
soft_type = 0
soft_mod = m
break
elif m.type == 'SOFT_BODY':
soft_type = 1 # Volume
soft_mod = m
break
if soft_type >= 0:
assets.add_khafile_def('arm_physics_soft')
cloth_trait = {}
cloth_trait['type'] = 'Script'
cloth_trait['class_name'] = 'armory.trait.internal.SoftBody'
if soft_type == 0:
bend = soft_mod.settings.bending_stiffness
elif soft_type == 1:
bend = (soft_mod.settings.bend + 1.0) * 10
cloth_trait['parameters'] = [soft_type, bend, soft_mod.settings.mass, bobject.soft_body_margin]
o['traits'].append(cloth_trait)
if type == NodeTypeCamera:
# Debug console enabled, attach console overlay to each camera
if bpy.data.worlds['Arm'].arm_play_console:

View file

@ -106,6 +106,7 @@ def init_properties():
bpy.types.Object.game_visible = bpy.props.BoolProperty(name="Visible", description="Render this object", default=True)
bpy.types.Object.spawn = bpy.props.BoolProperty(name="Spawn", description="Auto-add this object when creating scene", default=True)
bpy.types.Object.mobile = bpy.props.BoolProperty(name="Mobile", description="Object moves during gameplay", default=True)
bpy.types.Object.soft_body_margin = bpy.props.FloatProperty(name="Soft Body Margin", description="Collision margin", default=0.04)
# - Clips
bpy.types.Object.bone_animation_enabled = bpy.props.BoolProperty(name="Bone Animation", description="Enable skinning", default=True)
bpy.types.Object.object_animation_enabled = bpy.props.BoolProperty(name="Object Animation", description="Enable timeline animation", default=True)

View file

@ -109,7 +109,6 @@ class ObjectPropsPanel(bpy.types.Panel):
layout.prop(animitem, "loop_prop")
layout.prop(animitem, "reflect_prop")
# Menu in modifiers region
class ModifiersPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
@ -130,6 +129,20 @@ class ModifiersPropsPanel(bpy.types.Panel):
layout.prop(bpy.data.worlds['Arm'], 'generate_ocean_water_color')
layout.prop(bpy.data.worlds['Arm'], 'generate_ocean_fade')
class PhysicsPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "physics"
def draw(self, context):
layout = self.layout
obj = bpy.context.object
if obj == None:
return
layout.prop(obj, 'soft_body_margin')
# Menu in data region
class DataPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"