Here comes my first try in blender scripting and python programming.
I just needed a tool for importing 3d molecular models in blender and since I didn’t find one I started programming.
It’s more a wizard than a conversion tool as the pdb-files only provides atom types and positions and connectivity tables. The script builds balls of different sizes for any atom with materials linked to the atom types. When chosen the atoms are connected by sticks with rounded ends.
The tool is now working somehow. You can download it at http://www.malte-reimold.de/blender/pdb2blend.html as well as some pdb molecule files for testing purposes for those who are not familiar with molecule models.
I built this script with blender 2.41 and Python 2.4 on WinXP.
Please try the script and give me some feedback about functionality on other platforms or versions.
Sorry for pushing it up again - I changed the whole post and tried to point out more clearly, what this script is all about because I had the impression that I wrote it quite complicated the first time.
I also added some pictures to show how it works…
It’s great that someone else need’s such kind of script too!
If used in publications, you can cite it as: pdb2blend, Malte Reimold 2006.
Until now only a few atom types are supported (C,H,N,O,P,B) as I needed those but the list can be expanded easily. So if you need other nuclei, just tell me. (I wanted to expand the list anyway)
Atom radii are from literature. It can be discussed, if an option for Van der Waals radii would be interesting too.
However most interesting for me is, if the script works with other pdb files. If some problems arise using other pdb files please send them to me, so I can adjust the import function.
This DNA-file takes a long time when using standard refinement values. Just go to lower refinement values then it doesn’t take so long.
The other two example files are not as big and now the links work.
Does anyone really need VdW radii for visualisation purposes? If so, I would add this functionality…
The meshes are definetly linked to the scene. Sometimes they are far out of focus, and (I don’t know why) the screen does not refresh properly so you have to zoom (or move or change view or whatever) before seeing anything.
I think adding a scene.update(); after adding the objects/meshes should solve the “having to zoom” problem. Just put that in before your Blender.Redraw()
Would it be hard to create your own pdb file… I’d love a c60 shaped mesh with twice the molecules… that’s a basic shape I’ve been looking for… accidentally hehe
[edit] perhaps an option… don’t create a completely new object with a completely new mesh for each molecule/stick, but instead basicly create an object with the same mesh but just a different position/rotation.
That would save on a lot of data and would make it heaps easier to edit the result.[/edit]
Lots of helpful advices. Thanks.
I already tried out the refresh stuff and it works fine.
Is it true that one object can only contain one mesh? I tried to link more than one mesh to one object but then I only maintained the last mesh.
However. For the atoms it would be possible to use one mesh for all atoms of the same kind.
Is it true that material is linked to the mesh? So it is not possible to have different objects with different materials but the same mesh, right?
This is my first try in blender scripting and python programming so my insight into this stuff is not very deep. (and the Python API reference isn’t very helpful I have to say)
However - in the case of sticks they have to be different meshes anyway due to different bond length (they cannot be scaled because then the ends would look deformed).
Understanding Blender will make your job easier, so:
All blender objects have two parts. The Object part contains the location, size, and rotation data. The ObData part contains the geometry data for meshes, metaballs, curves, etc. The OOPS window or Outliner will show you this structure
The Object holds a link to the ObData. Any given ObData may be linked to many Objects. An ObData may hold multiple sets of geometry. ( create a mesh. go into edit mode and add more mesh stuff -> all one thing )
Materials can be linked either to the ObData part or to the Object part. You can have up to 16 materials in the Materials list. See Editing context - F9.
There is only one link
However. For the atoms it would be possible to use one mesh for all atoms of the same kind.
Yes. Multiple Objects can be linked to the same ObData.
So it is not possible to have different objects with different materials but the same mesh, right?
I recently wrote the Caliper script which is something completely different but does also deal with an issue you have.
The Caliper script creates an “arrow” between two points, which is basicly exactly what you’re doing with the struts… you might want to look at the way I did it. Though it’s a completely different approach.
Basicly what I did is:
Since I have 2 points I can figure out where the midpoint is so I create the arrow/strut object there.
Figure out the angle between the two points so that I can rotate the object so that it lines up between the two points.
Find the distance between the two points.
Create the mesh (half on each side of the object) and locate the verts + half the distance (or minus half depending on what side).
The Object holds a link to the ObData. Any given ObData may be linked to many Objects. An ObData may hold multiple sets of geometry. ( create a mesh. go into edit mode and add more mesh stuff -> all one thing )
Works fine in Blender --> and leads to ONE Mesh-datablock. The question is, if I can build up severel sets of Mesh data and add those to an object in a python script?
However - your infos help a lot to optimize the script!
Since I have 2 points I can figure out where the midpoint is so I create the arrow/strut object there.
Figure out the angle between the two points so that I can rotate the object so that it lines up between the two points.
Find the distance between the two points.
Create the mesh (half on each side of the object) and locate the verts + half the distance (or minus half depending on what side).
This is mainly what my script does (in stick mode)! Calculating distances and angles between two connected atoms, building the “pill” of corresponding length (half of it upwards, the other half downwards) at the origin and moving/rotating it to the right position.
Maybe I misread how to run this script, but I haven’t got it working yet. Trying to import the file doesn’t seem to do anything. Pressing the cancel button just about always returns errors about invalid MVert tuples.
I’ll try again later today with recent cvs. Failing that, I’ll try to debug what is going on.
Just pressing import and choosing a pdb file should work. If not so there is definetly a problem. I can’t interpretate this error message so easily. It seems like the script would start to build up something, becoming stuck somehow.
Did you use the latest blender and python version? If not, please tell me which version you used.
Did you use one of the example pdb-files or did you use another one? In the latter case please send me the pdb file you used. Unfortunatly I had to figure out that this pdb format causes trouble in some cases. Sometimes values are not separated correctly, some values might miss sometimes and so on.
Ok, it was just the old version of Blender I was running it on (2.40). I guess it didn’t run due to a bug in 2.40, that got fixed by 2.41 (i tested it with cvs from early April 2006).
However, it does seem to be quite slow. I’ll have a look to see if there are any bits that can be optimized.
When incrementing counters (such as vcount), doing:
foo += n
is the same as doing
foo = foo + n
Maybe to make it easier to add new types of atoms, the settings for different atoms should be implemented as a dictionary. Getting the necessary things could be as simple as doing a lookup.
As you are using many loops that perform a bit of math on every run, there doesn’t seem to be very much you could do to optimize the speed. That said, you are doing some inefficient stuff in these loops everytime you add some more data.
Dot notation (i.e. me.faces.[somefunction]) is inefficient. To optimize your script, get make references to these functions (to save python having to find them everytime). for example:
currently you have
while b < 360:
dz = radius*cos(a/90*pi)
dx = radius*sin(a/90*pi)*sin(b/90*pi)
dy = radius*sin(a/90*pi)*cos(b/90*pi)
b = b + db
me.verts.extend(dx, dy, dz)
vcount = vcount + 1
if vcount > 1:
me.faces.extend([me.verts[0],me.verts[vcount],me.verts[vcount-1]])
me.faces.extend([me.verts[0],me.verts[1],me.verts[vcount]])
this could be better written as (haven’t checked whether it actually works, but theoretrically it does)
mfcs = me.faces # me.faces
mvts = me.verts # me.verts
while b < 360:
dz = radius*cos(a/90*pi)
dx = radius*sin(a/90*pi)*sin(b/90*pi)
dy = radius*sin(a/90*pi)*cos(b/90*pi)
b += db
mvts.extend(dx, dy, dz)
vcount +=1
if vcount > 1:
mfcs.extend([mvts[0],mvts[vcount],mvts[vcount-1]])
mfcs.extend([mvts[0],mvts[1],mvts[vcount]])
Some people I have been working for have been tearing their hair out trying to create accurate mollecular models for a medical client in 3dsMax. Importing the pdb into Blender and then exporting as a .obj (with materials!) and importing to Max just made things a lot simpler.