Skip to content

Commit

Permalink
Fast joint creation
Browse files Browse the repository at this point in the history
  • Loading branch information
JustasB committed Aug 8, 2019
1 parent 31657cb commit 265436e
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 102 deletions.
File renamed without changes.
15 changes: 15 additions & 0 deletions blenderneuron/blender/properties/rootgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ class CUSTOM_NEURON_LayerAlignment(PropertyGroup, BlenderNodeClass):
" deviate from their original positions"
)

spring_stiffness = FloatProperty(
default=10,
min=0,
name="Joint Stiffness",
description="Joint stiffness coefficient"
)

spring_damping = FloatProperty(
default=0.5,
min=0,
max=1,
name="Joint Damping",
description="Joint damping coefficient"
)

physics_steps_per_sec = FloatProperty(
default=120,
min=1,
Expand Down
60 changes: 5 additions & 55 deletions blenderneuron/blender/views/curvecontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def unlink(self):
self.linked = False


def add_tip(self, tip_template, joint_template):
def add_tip(self, tip_template, empty_obj):
ob = self.get_object()

if ob.type != 'MESH':
Expand All @@ -381,70 +381,20 @@ def add_tip(self, tip_template, joint_template):
bpy.context.scene.objects.link(tip_object)
self.tip_name = tip_object.name

self.create_joint_between(ob, tip_object, tip_loc, joint_template, max_bend_angle=0)

def create_joint_with(self, child, joint_template, max_bend_angle):
self.create_joint_between(self.get_object(), child.get_object(), child.origin, joint_template, max_bend_angle)

def create_joint_between(self, parent_object, child_object, joint_location, joint_template, max_bend_angle):

# Create and link an "Empty" object - which serves as the joint

# COPY METHOD
# empty_template = bpy.data.objects.get(joint_template)
# if empty_template is not None:
# empty = create_many_copies(empty_template, 1)[0]
# # This does not work - the copied constraints don't seem to be functional
# # Too tired to report a bug... ugh
# # empty = empty_template.copy()
# # bpy.context.scene.objects.link(empty)
#
# # CREATE NEW METHOD
# else:

# This appears to be the only reliable way to add a constraint
# ... it's terribly slow. Profiler shows constraint_add() takes the most time
# I have not found any other way to add stable joints ... ugh
bpy.ops.object.empty_add(type='SPHERE')
bpy.ops.rigidbody.constraint_add()
empty = bpy.context.object
empty.name = joint_template
self.create_joint_between(ob, tip_object, tip_loc, empty_obj)

def create_joint_with(self, child, empty_obj):
self.create_joint_between(self.get_object(), child.get_object(), child.origin, empty_obj)

def create_joint_between(self, parent_object, child_object, joint_location, empty):
empty.location = joint_location
empty.empty_draw_type = 'SPHERE'
empty.empty_draw_size = 0.5

# Create parent-child relationship between the parent section and the empty
empty.parent = parent_object
empty.matrix_parent_inverse = parent_object.matrix_world.inverted()

# Set the joint params
constraint = empty.rigid_body_constraint
constraint.type = 'GENERIC'

constraint.use_limit_lin_x = \
constraint.use_limit_lin_y = \
constraint.use_limit_lin_z = \
constraint.use_limit_ang_x = \
constraint.use_limit_ang_y = \
constraint.use_limit_ang_z = True

constraint.limit_lin_x_lower = \
constraint.limit_lin_y_lower = \
constraint.limit_lin_z_lower = 0

constraint.limit_lin_x_upper = \
constraint.limit_lin_y_upper = \
constraint.limit_lin_z_upper = 0

constraint.limit_ang_x_lower = \
constraint.limit_ang_y_lower = \
constraint.limit_ang_z_lower = -pi / 180 * max_bend_angle

constraint.limit_ang_x_upper = \
constraint.limit_ang_y_upper = \
constraint.limit_ang_z_upper = pi / 180 * max_bend_angle

constraint.object1 = parent_object # parent
constraint.object2 = child_object
Expand Down
5 changes: 4 additions & 1 deletion blenderneuron/blender/views/objectview.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ def containers_to_mesh(self):
self.select_containers()

# Convert the selected container curves to mesh
bpy.context.scene.objects.active = bpy.context.selected_objects[0]
active_ob = next(o for o in bpy.context.scene.objects if o.select)
bpy.context.scene.objects.active = active_ob

#context = get_operator_context_override(selected_object=active_ob)
bpy.ops.object.convert(target='MESH', keep_original=False)

def mesh_containers_to_curves(self):
Expand Down
141 changes: 119 additions & 22 deletions blenderneuron/blender/views/physicsmeshsectionobjectview.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import bpy, math
import numpy as np
from blenderneuron.blender.utils import create_many_copies
from queue import Queue
from blenderneuron.blender.utils import get_operator_context_override
from math import sqrt, pi

class PhysicsMeshSectionObjectView(SectionObjectView):
def __init__(self, group):
Expand All @@ -17,13 +20,23 @@ def __init__(self, group):
# Don't add the extra end caps
self.closed_ends = False

self.joint_count = 0
self.tip_template = self.create_tip_template()
self.joint_template = None
self.joint_pool = None

@property
def max_bend_angle(self):
return self.group.ui_group.layer_aligner_settings.max_bend_angle

@property
def spring_stiffness(self):
return self.group.ui_group.layer_aligner_settings.spring_stiffness

@property
def spring_damping(self):
return self.group.ui_group.layer_aligner_settings.spring_damping

@property
def max_section_length(self):
return self.group.ui_group.layer_aligner_settings.max_section_length
Expand All @@ -45,6 +58,8 @@ def show(self):

self.make_containers_rigid_bodies()

self.create_joint_pool()

# Joints will allow sections to rotate around the branch point
self.add_branch_joints()

Expand All @@ -55,19 +70,106 @@ def show(self):

self.setup_physics_sim()

def create_joint_pool(self):

# Create one copy of the joint with the rigid body constraint
self.create_joint_template()

self.count_joints()

# Use the particle method to create many copies efficiently
joint_pool = create_many_copies(self.joint_template, self.joint_count)

# Assign the newly created empties to the constraints group (used by rigid world)
bpy.context.scene.objects.active = self.joint_template
bpy.ops.group.objects_add_active()

# Create a pool of pre-created joints
q = Queue()

for j in joint_pool:
q.put(j)

self.joint_pool = q

def count_joints(self, section=None):
if section is None:
self.joint_count = 0

for root in self.group.roots.values():
self.count_joints(root)

return self.joint_count

# Add contributions by split sections
if section.was_split:
self.joint_count += len(section.split_sections)-1

child_count = len(section.children)

# Each child gets a joint
self.joint_count += child_count

# Each tip gets a joint
if child_count == 0:
self.joint_count += 1

for child in section.children:
self.count_joints(child)

def create_joint_template(self):
# This does not work - the copy()s of the template do not seem to
# copy over constraint data (values do but not behavior)
# empty = bpy.data.objects.new("JointTemplate", None) # None creates "Empty"
#
# # Add rigid body constraint to the empty - only works when linked to scene
# bpy.context.scene.objects.link(empty)
# bpy.context.scene.objects.active = empty
# bpy.ops.rigidbody.constraint_add()
#
# return empty.name

return "JointTemplate"
empty = bpy.data.objects.new("JointTemplate", None) # None creates "Empty"

# Add rigid body constraint to the empty - only works when linked to scene
bpy.context.scene.objects.link(empty)
bpy.context.scene.objects.active = empty
bpy.ops.rigidbody.constraint_add()

empty.empty_draw_type = 'SPHERE'
empty.empty_draw_size = 0.5

# Set the joint params
constraint = empty.rigid_body_constraint
constraint.type = 'GENERIC_SPRING'

constraint.use_spring_ang_x = \
constraint.use_spring_ang_y = \
constraint.use_spring_ang_z = True

constraint.spring_stiffness_ang_x = \
constraint.spring_stiffness_ang_y = \
constraint.spring_stiffness_ang_z = self.spring_stiffness

constraint.spring_damping_ang_x = \
constraint.spring_damping_ang_y = \
constraint.spring_damping_ang_z = self.spring_damping

constraint.use_limit_lin_x = \
constraint.use_limit_lin_y = \
constraint.use_limit_lin_z = \
constraint.use_limit_ang_x = \
constraint.use_limit_ang_y = \
constraint.use_limit_ang_z = True

constraint.limit_lin_x_lower = \
constraint.limit_lin_y_lower = \
constraint.limit_lin_z_lower = 0

constraint.limit_lin_x_upper = \
constraint.limit_lin_y_upper = \
constraint.limit_lin_z_upper = 0

constraint.limit_ang_x_lower = \
constraint.limit_ang_y_lower = \
constraint.limit_ang_z_lower = -pi / 180 * self.max_bend_angle

constraint.limit_ang_x_upper = \
constraint.limit_ang_y_upper = \
constraint.limit_ang_z_upper = pi / 180 * self.max_bend_angle

self.joint_template = empty

return empty.name

def create_tip_template(self):
mesh = bpy.data.meshes.new('TipTemplateMesh')
Expand All @@ -79,7 +181,6 @@ def create_tip_template(self):

return object.name


def create_container_for_each_section(self, root, recursive=True, is_top_level=True):
if is_top_level:
origin_type = "center"
Expand Down Expand Up @@ -145,16 +246,14 @@ def add_branch_tips(self):
self.add_branch_tip_mesh(root)

def add_branch_tip_mesh(self, root):
if self.joint_template is None:
self.joint_template = self.create_joint_template()

if not any(root.children):
if root.was_split:
root_cont = self.containers[root.split_sections[-1].hash]
else:
root_cont = self.containers[root.hash]

root_cont.add_tip(self.tip_template, self.joint_template)
root_cont.add_tip(self.tip_template, self.joint_pool.get())
del root_cont

# Recursively create the tip meshes for leaf sections
Expand All @@ -163,9 +262,6 @@ def add_branch_tip_mesh(self, root):


def add_branch_joints(self):
if self.joint_template is None:
self.joint_template = self.create_joint_template()

# Recursively add the joints between parent-child sections
for root in self.group.roots.values():
self.add_joints_to_children(root)
Expand All @@ -178,8 +274,9 @@ def add_joints_to_children(self, root):
for i, split_sec in enumerate(root.split_sections[:-1]):
start_cont = self.containers[split_sec.hash]
end_cont = self.containers[root.split_sections[i+1].hash]
start_cont.create_joint_with(end_cont, self.joint_template, self.max_bend_angle)
start_cont.create_joint_with(end_cont, self.joint_pool.get())
del start_cont, end_cont

# Then link the last split section with original children
root_cont = self.containers[root.split_sections[-1].hash]

Expand All @@ -192,7 +289,7 @@ def add_joints_to_children(self, root):
child_hash = (child.split_sections[0] if child.was_split else child).hash

child_cont = self.containers[child_hash]
root_cont.create_joint_with(child_cont, self.joint_template, self.max_bend_angle)
root_cont.create_joint_with(child_cont, self.joint_pool.get())
del child_cont

del root_cont
Expand Down Expand Up @@ -222,7 +319,7 @@ def remove(self):
bpy.data.objects.remove(tip_temp)

if self.joint_template is not None:
tmpl = bpy.data.objects.get(self.joint_template)
tmpl = bpy.data.objects.get(self.joint_template.name)
if tmpl:
bpy.data.objects.remove(tmpl)

Expand Down
48 changes: 24 additions & 24 deletions blenderneuron/config.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
[
{
"NEURON_last_command": "import blenderneuron as bn; print(bn.bl_info);",
"NEURON_launch_command": "nrniv -python -c 'from blenderneuron import neuronstart'",
"default_ip": {
"Blender": "192.168.0.100",
"Control": "127.0.0.1",
"NEURON": "192.168.162.128"
},
"default_port": {
"Blender": "5201",
"Control": "",
"NEURON": "5202"
},
"end_types": [
"NEURON",
"Blender",
"Control"
],
"imports": {
"Blender": "import bpy, mathutils",
"NEURON": "from neuron import h"
}
}
[
{
"NEURON_last_command": "import blenderneuron as bn; print(bn.bl_info);",
"NEURON_launch_command": "nrniv -python -c 'from blenderneuron import neuronstart'",
"default_ip": {
"Blender": "127.0.0.1",
"Control": "127.0.0.1",
"NEURON": "192.168.162.128"
},
"default_port": {
"Blender": "",
"Control": "",
"NEURON": "5202"
},
"end_types": [
"NEURON",
"Blender",
"Control"
],
"imports": {
"Blender": "import bpy, mathutils",
"NEURON": "from neuron import h"
}
}
]
File renamed without changes.

0 comments on commit 265436e

Please sign in to comment.