Thank you a lot Larry!
This tutorial is just perfect, simple and easy to understand!
Finally, I understand the things that I didn't before!
Thank you again!
Jointed objects are a little tricky, and Schwaa's VHOT and Axels tutorial, excellent as it is, will only take you so far. So this is my attempt to explain what I have learned about the subject. Hopefully between what Schwaa has to say and what I have to say, you'll be successful in building your own jointed object models in whatever 3D modeling tool you use.
Basic Concepts
- A jointed object has only three part types: the base object, the sub-object, and the axle object.
- The base object is the part of the jointed object which does not move.
- The sub-object is the part of the jointed object which does move.
- The axle object tells how the sub-object moves with respect to the base object.
- The axle object is always two points with a line between them.
- There are only two types of axle objects: rotation and translation.
- You can have multiple levels of sub-objects. That is, a base object can have multiple sub-objects attached to it via axle objects, and it can have sub-objects which have their own sub-objects attached to them via their own associated axle objects! You might want to think of these as sub-sub-objects. I can't stop you. The most sub-object levels I have ever used successfully is three: base object, sub-object, sub-sub-object, and sub-sub-sub-object. I think that theoretically 5 might be possible, but I have never seen it done.
- The names associated with these objects define which is what. [**** IMPORTANT ***]
- Positioning the parts of a jointed object such that they are acceptable to bsp.exe can be very tricky!!
- The more levels of sub-objects that a jointed object has, the trickier it is to build a model acceptable to bsp.exe.
Naming Jointed Object Parts
- For the base object, only the first two characters of the name are significant. For example, if you name your base object, Object, the "real" name as used by bsp.exe in creating a bin file is just Ob. The rest of the characters in the name are ignored. More about this in a minute.
- If your jointed object has only a base object and one or more sub-objects attached to it, then the name you give to your base object can be whatever you want. However, if your jointed object has more than one level of sub-objects, I strongly recommend that you name it Aa. More about that later.
- Axle object names follow one of two specific patterns: @Xjjobnnnn or @Zjjobnnnn:
- the @ sign indicates that the name is a special name; all axles, sub-objects, and vhots must begin with @, and only axles, sub-objects, and vhots.
- @X means rotating axle
- @Z means translating axle
- jj is one less than the joint number you want to refer to it by in DromEd (00=Joint 1, 01 = Joint 2, 02 = Joint 3, etc.)
- ob is the two character name of the base for the joint
- nnnn is the degrees of movement that you want bsp.exe to check for interferences in a special code (0000 = 0°, 1250 = 45°, 2500 = 90°, 5000 = 180°, 7500 = 270°, 9999 = 359.964°). [I recommend that you don't have bsp.exe do any checking and always use 0000.]
So, if you ran across an object in a model with the name @X03Aa2500, you would know right away that it is a rotating axle which would be referred to in DromEd as joint 4, whose base object was named with Aa as the first two characters of its name, and that only a 90° movement interference check is desired. Similarly @Z00Ob0000 is a translating axle referred to by Joint 1 in DromEd, with base object named Ob-something, and no interference check is desired. Simple, right?- sub-object names all must be of the form: @Sjjob
- @S indicates that this is a sub-object
- jj is the joint designator for the associated axle object that it moves according to
- ob is the sub-object name reference. Generally only two characters are used as only the first two characters are significant for bsp.exe, but I suppose you could use a longer name.
So, if you ran across an object in a model with the name @s01bb, you would know that it was a sub-object which moved according to the axle object with 01 as the joint designator and that it was referred to as bb if referenced by any other objects (say by the joint for a sub-sub-object)
With me so far? Let's look at a couple of examples. The first is a shipping crate with a movable lid and on the lid is a moveable hasp.
This shipping crate has a base object aa (the bottom and sides of the crate) which has an associated axle object @x01aa0000 (the hinges for the lid). This axle is associated with Joint 2 in DromEd, and is a rotation axle with no interference checking done.
There are two sub-objects for the shipping crate. One, the lid of the crate, is a sub-object to aa, named @s01bb. You know that it moves with respect to Joint 2, and since Joint 2 (@x01aa0000) is associated with aa, you know that the lid hinges off of aa. The second sub-object is the hasp of the lid's fastener system. It hinges with respect to the lid, and not with respect to the bottom of the crate. We know this by looking at its name and the name of its axle: @s00cc and @x00bb0000. You know that the hasp uses the Joint 1 axle object, and looking for that we see @x00bb0000. This name says that this axle is associated with bb (the lid) and not aa (the crate bottom). So @s00cc is a sub-sub-object.
Diagrammatically the setup is:
base --axle------------> sub-object --axle----------> sub-sub-object
aa -@x01aa0000 ---> @s01bb -@x00bb0000-> @s00cc
There are a couple of interesting points that this example illustrates:
- The joint assignments are arbitrary. The first joint off the base does not have to be 00. It can be any legitimate joint number, 00 through 05 (Joint 1 through Joint 6). In this case the lid hinges off of Joint 2 and the hasp on the lid hinges off of Joint 1.
- While the sub-object's name clearly associates it with a axle object, and the axle object's name clearly associates it with the base object, it is a best practice to name such linked object parts alphabetically. aa->bb->cc. When we come to multiple sub-objects off of a single base, you will see that you skip a name to indicate that both sub-objects are off of the same base: aa->bb and aa->dd. cc is skipped since that would mean that cc was off of bb and not aa. Get it? More later on this later. But it is important to getting multiple joints to work.
Here's another example. This is a railroad semaphore signal. The head turns and the flags attached to the head raise and lower.
As discussed above, the Dd designator is skipped for this model since Bb has two independent sub-objects, Cc and Ee. Use of Dd instead of Ee for the second flag would indicate that it was a sub to Cc, which it isn't. Make sense? All I can say is that this alphabetical naming seems necessary to get multiple jointed objects to work. If you have just one joint, you can name the base object and sub-object whatever you want. That seems to work just fine. But with multiple sub-objects I strongly suggest you stick with Aa, Bb, Cc Dd, etc. with appropriate name skipping. Not to do so seems to cause troubles.
Another point, about axles this time. While axles are invisible in-game, they are not insubstantial. That is, they can change the physics box about your object. The points at the ends of the lines count towards that calculation, so if you want the axles not to make your objects bigger, keep them inside of the solid portions of your object. In the semaphore above, this was not done. You can easily see that the axle X00Aa0000 sticks down below the base of the semaphore at the bottom and above the head of the semaphore at the top. While that makes things easy during construction, it is a good practice to move those points to be inside instead before generating your bin.
This is a cell door, with a translating bolt instead of the normal rotating handle. When you pick the lock, the bolt visibly withdraws indicating the door is unlocked.
You know the bolt moves linearly because of the name of its axle @z00aa0000. Joints on doors for handles (and bolts) have to use Joint 1. That's what the StdDoor script is looking for to move the sub-object about the joint.
Another thing to note is that a door is a special case. It doesn't need a joint for the door to pivot about. The hinging of the door is taken care of by the set-up on the door (Door>Rotating properties) and the StdDoor script.
Jointed Object Modeling Best Practices
- Sub-objects cannot intersect base objects or other sub-objects. Give plenty of visible separation between them, and then a little bit more. Fortunately in most situations this extra spacing is totally invisible in-game. You think it would be obvious, but it's not. So spare yourself some grief and space thing out more than you think is necessary. If bsp.exe rejects it, then you've got something intersecting something it shouldn't or bsp.exe thinks something is / will intersect something it shouldn't. Which leads to my next bit of practical advice:
- Test things out one joint at a time. If you want to have a 6 jointed object (why? why? why?) build the base object and the first sub-object with its associated axle object, and immediately see if you can get it to pass bsp.exe. Then progressively add one sub-object and axle object at a time until you get all of the joints working.
- If bsp.exe chokes on the jointed object, try renaming things so that the model is just a normal model and not a jointed one. Then try bsp.exe again. The problem may not be with the joint(s) but elsewhere in your modeling. If bsp.exe is OK with your model without the special joint names, then you know that it thinks something is intersecting if things move as you have designed them. Go back and read best practice 1. I really meant it. Sometimes a small movement in the axle left, right, forward, back, up, down may be all it needs to make bsp happy. Sometimes it can take hours of playing around with moving the problematic sub-object and/or axle object. Sorry. That's just the way it is. Following best practice 2, will help you focus in on just what part of your model is causing the issues.
- I believe that axle rotations follow the "right-hand rule"
so theoretically if you construct the axle line from point A to point B, that should be interpreted as a vector from A to B, and if you line up your right thumb to that vector, you should know what the positive rotational movement is by looking at how your fingers curl on that hand. But I always forget to do that, only to find when I import the bin into the game that my hinge rotates opposite of what I want. So I have to go back and flip my axle 180 degrees and try again.- Don't be too ambitious at first, Do a few single jointed objects successfully before you try multiple joints.
More on using jointed objects in DromEd to come ...
Edit: One more thing that I have learned: Always define your joints in order, starting with Joint 1 (axle 00) and continuing in sequence to Joint 2, Joint 3, Joint 4, Joint 5, and Joint 6. Do not skip joints in your joint definitions. No gaps. DromEd's Tweq > Joints doesn't like it when you skip joints. If you have a three jointed model, make the joints Joint 1, 2, and 3. If you have a one jointed model, always make that joint numbered as Joint 1. If you deviate from this, your joints may not work properly or at all. And you could end up endlessly trying to fix the geometry of the model when all that is wrong is that you named your axles wrong. It makes no never mind which axle is which, but you must have a 00 axle (joint 1) and each subsequent axle must be in sequence up to 05.
Last edited by LarryG; 10th Aug 2015 at 22:54. Reason: New information about Joint numbers and Tweq > Joints
Thank you a lot Larry!
This tutorial is just perfect, simple and easy to understand!
Finally, I understand the things that I didn't before!
Thank you again!
I forgot to include this object in the tutorial. It's a container, a chest. There are a couple of things that make this example noteworthy.
First, the lock is on the lid. You can look at any LGS chest, and the lock is on the base, and figure that out from there. Having the lock on the lid makes this a little special.
Secondly, this illustrates that you don't have to name the base Aa. As long as you stay alphabetical, you'll be alright.
Thirdly, this chest would not pass bsp.exe no matter how I adjusted things. In the end I had to take the handles off. The lid does not come any place close to the handles, but bsp saw it differently. This demonstrates that you can follow all the rules, and it can sometimes not matter. I think bsp puts a cube around each object and sub-object and looks for intersections there. And a cube around the lid would hit the handles. Well, maybe not exactly that, as the lid fits over the base, but something like that. Whatever. Sigh.
This is what the chest ended up like in the game:
Last edited by LarryG; 7th Sep 2013 at 10:35.
That's excellent Larry. Thank you so much for doing that. . . . I am going to print and plaque mount this.
I have although a question: Is the @X/Zjjobnnnn's longer important?
That's a great and comprehensive tutorial Larry - thanks for that.
The checks that BSP does seem to be highly annoying. Is there not any command line option to turn them off?
Thers also a tutorial by Targa
When making my furniture objects I had huge problems with some of them, getting errors(means no conversion) or my joints got deleted. What I experienced was that you can also use negative values for counterclockwise movements like -2500 and also stuff like -99-99 can be used. Targa says in his tut that also 10000 is possible
I don't understand the question. All axles have to be named in that format.
Thanks for pointing out Targa's tutorial. Since I don't use 3dsMax, I never paid any attention to it. I believe that there is some misinformation there about how to set up the the joints in DromEd once you have a successful jointed model. But I want to take the time to reread and think about exactly what Targa is saying.
As to the axle's degrees of rotation specification in the name, all I can say is that you get less bsp errors with 0000 than with any other specification. There is no reason to ever specify anything else. It's the degrees of rotation that you set up in DromEd which matters. Having bsp check anything more than it has to for interferences is just asking for trouble. IMO.
I wish! but not that I've found.
You're right (as usual). I meant "one less than the joint number," now corrected.
Sorry, so, I wanted to say:
About the "Axle Object": is the distance between it two points important?
Ah. A very different question than I had thought you were asking!
Well, the long and the short of it (ahem) is that I don't know for certain. I have never found it so, but then I have never tried for a really short (.01 or less) or really long (100.00 or more) axle. Certainly I have had bsp choke on axles where if I moved an endpoint slightly it would not. Was that because the length changed or because the angle changed? I suspect the latter, but it could have been at least partially due to the length.
I would say that angle and placement of the axle are more important as long as you don't go to extremes in length.
Ok
So, I havn't to worry about so?
Thank you!
Worry? No. But you should expect difficulties. You are working with jointed objects and bsp can be extremely fussy. It is an exercise in patience.
In my experiments, one time among two, BSP has deleted my joints.
And I never known really why. I thought it was because of the Axle Obj.
So according to your answer, it isn't the problem
Are you positive? Here's a pump from Shock 2, imported into Blender:
One of the pistons quite clearly intersects the base. It does so even if you unparent it and zero out the transforms, although in a different spot. So how was pumps.bin (and others like it) originally cooked in the first place if intersection is a no-no?
If the handles of a chest cause bsp to choke they can be made into another object and placed in the same location as the chest is that the handles appear to be part of the chest. In LS5 the mermaid was actually three objects placed together... why? I don't know but it didn't cause any problems.
The SS2 pump model has holes in it that the pistons go into.
Pumper is a problematic bin. You may have noticed that when you convert it to 3ds that the 2nd arms on each pump arm assembly are detached from where they belong, off to the side, and not in the correct assembled position? My guess is that bintoe does not position the primary arms correctly with respect to the base either. Did you look at the range of motion specified in the axes, @x03pu0001 and @x00pu0101? There is also the possibility that those primary arms are as the modeler positioned them originally and, given the very small range of motions specified, that bsp did not detect that intersection. I suppose that bsp might actually have made a mistake in our favor in this case. But would you want to count on it with a new model? There are enough problems getting bsp to pass on a jointed model, do you really want to press your luck by intentionally creating an intersection that can be so easily avoided? Hey, do what you want. If you find out after building 20 or so jointed models that you have a method to have the base and sub objects intersect which consistently works, post it. I would love to find out how. Meanwhile, I'll continue to try and build my models as clean as I can.
Sorry, I wouldn't know what happens in this case. I'm importing the bins directly into Blender, and the subobjects are arranged (correctly) automatically. Interestingly, if you unparent everything and reset the transforms on everything, there still are intersections, as in the Shock pump.
The compiled bin file has no notion of axes (it specifies a 3x3 matrix and a position vector). Here's what can be gathered from it with regard to ranges (the units are radians, of course):
I'm trying to write directly to bin, bypassing bsp and whatnot. So far I've had no luck with multi-subobject models (only one of the dumped subobjects shows up in the editor currently). If I make any progress, I'll certainly post it.name: pumper
motion: none
min: 0.0
max: 0.0
name: @s01oo
motion: rotation
min: -0.0
max: 0.0
name: @s00nn
motion: rotation
min: -0.0
max: 1.0471975803375244
name: @s04uu
motion: rotation
min: -0.0
max: 0.0
name: @s03qq
motion: rotation
min: -0.0
max: 1.0471975803375244
I've been wondering about the purpose of bsp in the pipeline. Apparently it builds BSP trees from meshes, but it's not clear how the resulting trees are used. They are not stored anywhere in the bins. This needs more researching.
Thanks LarryG, you gave me the motivation I needed to turn my custom lever into a jointed object instead of taking the easy way out. It wasn't that hard once I had figured out the basics (i.e. using a group of meshes as a subobject is a bad idea and thank the bearded one for "join solids" ). Now I just need to tweak stuff.
I don't think it is stored inside the bin in any way.
I was working on a new turret model (tu_s.bin), and was having some problems converting it to a bin using bsp.exe. So to at least get the model ingame to check if it's modeled correctly I simply removed all vhots and renamed all the objects to don't feature any of the jointed object information.
So I just replaced the tu_s.bin and the model was still moving properly (only the muzzle was bugged since the vhot was missing).
My guess is that dromed/shocked does generate the map and all specific stuff which is needed for it to work when you import a new model into the editor. After that it just checks for object names discarding all jointed information.
As for your importer. The axle objects don't get imported (bintoe imports them, so it must be possible). Vhots get imported, but don't receive a name.
Using bintoe, it creates a small box at one position which is called @h00bottom, your bin importer creates a vhot at the same position, without a name.
I hope all this information will help you in your development, looking forward to it