3
$\begingroup$

Back-story: I'm using blender as a level editor for a game I'm making, this means I write an addon in Python to export the relevant data in my format, and manipulate the blender interface to add options I need. Fixtures and Bodies (basically shapes) are easy to represent with 2D meshes, and I use Properties to add my specific options. Whenever I can, I exploit existing blender options (like the rigid body stuff) instead of adding my own. I am no avid blender user otherwise, so I guess that makes me a noob vs all everyday users.

[TL;DR: just read from here...]

What blender object resembles/represents Box2D Joints the best? And if none is perfect, what can I start with and how do I modify it to suit my needs?

I need it to have:

  • Two "object properties" (references to other existing objects) which will represent which two bodies to connect. In Box2D these are called BodyA and BodyB.
  • Two "point/vector" properties. These would represent what specific points on the two bodies to link together. Box2D name: AnchorA and AnchorB

What would also be nice:

  • Have it visualize the joint between the two anchor points, if it draws a line or something that'd be great.
  • Really nice if it actually restricted the objects to, say, a certain distance away from each other when I move them.

[...to here]

Then, I'm sure I could add more specific properties like joint type, break strength, etc. myself through Python Property registration.

Note: I am a programmer; I'm not afraid to code.

Oh, and before you mention it: I have seen "rigid body constraints", but among other things they don't satisfy condition 2. I've also found them generally messy and hard to understand (if the constraint is already applied to a body, what does it need two parent properties for?) I'm assuming a lot of this is because they are focused towards 3D not 2D, but am open for suggestions if someone still sees an opportunity there.

$\endgroup$
  • $\begingroup$ I would draw them with custom opengl overlay over viewport with bgl module and keep the custom objects with custom everything under scene static collection property. This would allow for total control and customization. You could represent these with line mesh objects with hooks on ends or bones with some constaraints but imho this clutters scene and is not that well customizable. $\endgroup$ – Jaroslav Jerryno Novotny Apr 21 '15 at 17:47
  • $\begingroup$ @Jerryno Could you link to some documentation on the things you take up? Had no idea about bgl, that seems awesome! $\endgroup$ – Ludwik Apr 21 '15 at 18:58
  • $\begingroup$ Sure check out: blender.stackexchange.com/questions/21463/… or check the Screencast Keys addon code. The only limitation is that the opengl draw loop needs to run and is managed inside a modal operator, which blocks for example the F8 addons reload etc. But other than that should be fine. You can draw inside 3d space with POST_VIEW tag or in 2d with POST_PIXEL. $\endgroup$ – Jaroslav Jerryno Novotny Apr 22 '15 at 11:03
  • $\begingroup$ Yeah, the screencast leys source should have what I need, thanks! Any idea on how to go about the "object references"? $\endgroup$ – Ludwik Apr 22 '15 at 11:47
  • 1
    $\begingroup$ try bpy.props.FloatVectorProperty() it has default dimension of 3 but can be customized: blender.org/api/blender_python_api_2_67_1/bpy.props.html Yea I guess its not good to leave it all in comments, ill write the answer with some examples. $\endgroup$ – Jaroslav Jerryno Novotny Apr 22 '15 at 14:59
2
$\begingroup$

Storing the data in collection property:

import bpy

# specify the data structure - each item will be group of properties:
class Box2DJoint(bpy.types.PropertyGroup):
    # name = bpy.props.StringProperty()
    body_a = bpy.props.StringProperty()    # reference object name
    body_b = bpy.props.StringProperty()    # reference object name
    anchor_a = bpy.props.FloatVectorProperty()
    anchor_b = bpy.props.FloatVectorProperty()

# store them under scene:
bpy.utils.register_module(__name__)
bpy.types.Scene.box_2d_joints = bpy.props.CollectionProperty(type=Box2DJoint)

# populate like so:
new_joint = bpy.context.scene.box_2d_joints.add()
new_joint.name = "silly_box_joint"
new_joint.body_a = 'Some_object_name'
# ...etc.

# remove with index:
id = bpy.context.scene.box_2d_joints.find("silly_box_joint")
bpy.context.scene.box_2d_joints.remove(id)

The advantage of the data storage like this is you can also display it in UI lists which is nice: Populate a list with custom property dictionary data

If the user renames objects in blender or deletes them, the references must be adjusted: Execute a python function whenever the user interacts with the program. This offers the most flexibility, the code can catch almost anything. The handicap is that the frame change handler runs all the time.

Other approach would be to give objects some random hash which is stored with object name and searched for when the object is not found. Here is how to set it up for objects:

import bpy
import string
import random

def random_id(length = 12):
    return "".join([random.choice(string.ascii_uppercase) for i in range(length)])

bpy.types.Object.hash = bpy.props.StringProperty()
bpy.context.object.hash = random_id()

To check if object name exists:

if bpy.data.objects.get("FooBar") is not None:
    # do something with bpy.data.objects['FooBar']
else:
    # deal with it
    # look through all objects for hash and if found update name reference
    # if hash not found object is deleted or a ninja

Use bgl module to display such custom objects in view-port: Draw points on screen with bgl? (python)

The drawing code runs inside modal operator and has access to bpy.context, you can draw points, lines, triangles etc. in 3d float coords (init with 'POST_VIEW' tag) or in 2d pixel coords (init with 'POST_PIXEL'). BGL Wrapper Reference. The BLF module can be used to draw text. There are python templates in text editor for this.

Keeping this simple is good because drawing with python is not very fast. Too many draw-calls and Blender view-port fps will suffer.

$\endgroup$
  • $\begingroup$ Hi again, I'm in the process of implementing all this but I encountered a problem: when using the modal operator, the interface is blocked; I cannot zoom, pan, or perform any other tasks. I'd like those lines to be drawn all the time when a box2dJoint exists, but obviously I have to be able to do ohter things too. How do I drawstuff non-blockingly? $\endgroup$ – Ludwik Apr 25 '15 at 7:59
  • 1
    $\begingroup$ @Ludwik Have the modal loop return {'PASS_THROUGH'} instead of {RUNNING_MODAL}. That way the event catched by the modal operator will be also passed to blender. $\endgroup$ – Jaroslav Jerryno Novotny Apr 25 '15 at 8:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.