Here's some information on getting the ground height from loaded direct X or 3DS object in DarkBASIC Classic. There is an attached file with a few examples that should help bring some of this information to light.
CONTENTS
* Introduction
* What will we actually be doing to get ground height?
* An insect has feelers - so let's follow it's lead
--------Example1.dba (DarkBASIC Classic Collision)
* Using multiple feelers to find vehicle tilt
* Using Slope
--------Example2.dba (DarkBASIC Classic Collision)
* Get Ground Height using Sparky's DLL
--------Example3.dba
* Finding a Vehicles Tilt using Sparky's DLL
--------Example4.dba
* A bit about Math to Calculate Ground Height
INTRODUCTION
DarkBASIC Classic has a useful object one can use to build a landscape/terrain. In DarkBASIC, this is referred to as a Matrix. There are a series of commands that can be applied to a matrix to change it's shape and it's textures so you can fairly quickly and easily start building your world. What really makes using a matrix nice is a handy command called GET GROUND HEIGHT(). With this command you can find the Y position (the height of the matrix's peaks or valleys) at any place on the matrix. This will help you position your objects or characters to be set, walk, or roll on top of the terrain no matter how bumpy it may be.
At some point, one wants to have more control over the design and look of their landscapes that they may not have when using only the built in matrix commands. They wish to build a landscape in a modeling program and load it into DarkBASIC Classic as an X file or a 3DS file. But when it comes to loading in and using a terrain that you have built in a 3D modeling program, there is no equivalent of the matrix GET GROUND HEIGHT() command. The question always comes up: "How do I get the ground height for my terrain like I can from a DBC Matrix?" Throughout the forums this has been asked and addressed many times. At the time of writing this, I had noticed this question resurfacing; especially in regards to tilting a vehicle according to the contours of the terrain. I've decided to address this issue yet again and hopefully provide a few options that aren't too complicated and easy to reproduce and understand. I'll rely on the reader to analyze the attached code to develop a better understanding of the methods I propose.
WHAT WILL WE ACTUALLY BE DOING TO GET THE GROUND HEIGHT?
Quite simply, we will use collision detection with an object and the landscape. There are mathematical ways to return the height at any given point, and I may touch on them, but for the bulk of this reference, we will concentrate on using collision detection to get our ground height.
We will talk about using some of DarkBASIC Classic's built in collision detection as well as using a custom DLL commonly referred to on these forums as Sparky's Collision DLL. Though it may seem straight forward enough, when it comes to using the built in collision commands, there aren't any that immediately lend themselves to returning a 3d point in space except for MAKE OBJECT COLLISION BOX or MAKE STATIC COLLISION BOX. The problem here is that a terrain is rarely only flat and/or square and returning the subtleties in height difference on an incline would be very difficult. Don't worry, we can use other collision commands and figure out the collision point ourselves.
AN INSECT HAS FEELERS - SO LET'S FOLLOW IT'S LEAD!
The first problem to tackle is how to set up the collision test. For the terrain, we will set it to polygon collision (SET OBJECT COLLISION TO POLYGONS) so that it is possible to detect collision anywhere on it. But for our character or main object that is strolling around on the terrain, we won't test it against the terrain, but rather, we will use a "feeler" object to test a point for collision before we move the main object to that position. The reason for this is to help prevent a jittering or a jumping up and down motion of the main object that can occur (and will become more clear as to why) as we explore this method. For our intents and purposes, we will make the feeler object out of a plain that is 1 unit wide and 60 units tall (make object plain feeler,1,60). This gives us a 30 unit spread above and below the terrain. This helps to make sure there is a very likely intersection with the terrain. We can make it smaller (and that can speed up the height test), but for now, 60 units will do fine. We will SET OBJECT COLLISION TO BOXES for the feeler object.
In order to get the height of the collision, we have to move the feeler up when there is collision and continue until there is no more collision, and down when there is no collision until there is collision. Remember, we have 30 units to play with on either end of the feeler, but since we are always trying to keep the feeler just above the terrain (raise it until there is no more collision), we only have to worry about the bottom 30 units. That is to say, once we have raised the feeler to a point where there is no more collision from a point of collision, we take the y position of the feeler and subtract 30 from it. That is the height of the terrain at that point. The accuracy is based on how much you move the feeler up each increment. If you move it up and down .1 units, then the accuracy will be within .1 units. If you move it up and down 10 units, then the accuracy will be within 10 units.
I mentioned a jumping or jittery effect that can occur with this kind of collision testing. Well, if we were moving the main object like the feeler, it would be trying to go up and down as it figured out the ground height using it's own collision. That is why we have a feeler object. Whenever we are about to move our main object to a new position, we send the feeler object out first, have it do it's up and down test to find the height of the terrain, and then we move the main object to that position at the proper height the feeler has found. The goal is not to have any testing going on exactly where the main object is at any given time, but to test where the main object will be moved and return that height before it even gets there. That's the whole premise of this method. And to keep the collision test to a minimum, there never is a test unless the main object is about to be moved.
Please review the code in Example1.dba to get a better understanding of this explanation.
USING MULTIPLE FEELERS TO FIND VEHICLE TILT
Just a reminder, I will use "object" or "vehicle" interchangably to mean the same thing: the main object you are trying to move or position on top of the landscape.
Once we are able to find the ground height, finding the the tilt of a vehicle is just a matter of finding the ground height at the front, back, left side, and right side of the vehicle. The different elevations form different angles and these are the angles at which to tilt the vehicle. These angles can be found by using the slope of the vehicle in both of two directions:
1. The vehicle's relative z direction
2. The vehicle's ralative x direction
If you're 1000 years old like me, you might have to dust the cobwebs out of your brain and try to remember what the slope is. If you're just starting to or recently studied this then you may have an advantage. If you've never heard of it, then this may not make much sense. And, in order for this to work properly, I suggest using a pivot object that the vehicle will be glued to. The pivot object will be positioned to where ever the vehicle is desired to be positioned. The use of a pivot object allows the main object to be tilted on different axes without the complications that can arise when an object's orientation is changed. The pivot object always retains the proper orientation so it can be positinoed without a problem. As the true orientation of the main obejct which is glued to the pivot object only changes relative to a zero degree orientation, the tilts don't get screwed up. That's all I'll say about that. For now, just trust that a pivot object can save some headaches. I actually broke one of my own rules in the example that goes with this. I used 1 of the feelers as the pivot object. It works out ok, but that means the object is positioned directly without waiting for the return value from the feeler - the object is positioned as the feeler is positioned. It'll be ok for this, but you may want to experiment later and set up an independent pivot object that is not a feeler. In any case, here we go.
Your main object, vehicle, character, whatever, has a z size (back to front) and an x size (right to left). The trick is to position feelers at these points where ever the vehicle/object is about to move. Once the feelers are in place, the heights of all of the feelers have to be tested and returned. From these heights, we can calculate the tilt of the object.
USING SLOPE
If the object is on an incline, the front will be higher than the back - i.e. the Y value in the front will be greater than the y value in the back. If we take the difference between these y values, and divide them by the z size, we have the slope of the vehicle along it's z axis. This slope has a relationship to a right triangle called tangent. You might be asking yourself "What right triangle?" A right triangle is formed between the way the object is tilted and it's position as if it were level with the ground. If we take the arctangent of the slope, then we get the angle this slope suggests. Now, the z axis can be rotated in one of two ways:
* around the x axis, if the motion is meant to be up and down
* around the y axis, if the motion is meant to be side to side
Well, we know we are dealing with the front and the back of the object so the only rotation that makes sense is up and down which is around the x axis. That means the front and back tilt is an X angle.
We apply the exact same process using the y heights on the right and left side of the object. This time we divide the difference by the x size. That means we will ultimately be rotating the x axis around the y axis or the z axis. Again, it doesn't make sense to rotate it around the y axis because that would be side to side. We are looking for an up and down rotation (tilt) so that has to be around the z axis. That means the right and left tilt is a Z angle.
We then xrotate and zrotate the object. I use CUREVEANGLE in the example to make a smooth transition from the objects previous angles to the the new tiled angles. That's it! Your vehicle should tilt according to the calculated slopes.
Please review the code in Example2.dba to get a better understanding of this explanation.
GET GROUND HEIGHT USING SPARKY'S DLL
For those of you with the enhancement pack to DB (version 1.20 has this included), you will be able to use DLLs and call the functions in them. There is an excellent collision DLL referred to as Sparky's Collision DLL. This DLL is ideal for getting the information we need to find the "ground height." There is a version for DBC and one for DBPro. We are, of course, interested in the DBC version. You can get a copy of it from here:
Sparky's DLL
The idea is simple: you set up your terrain for collision detection using the command set from the DLL. You then cast a ray from above the terrain to below the terrain at an X and Z position you specify. You then use the Y value that is returned from the intersection point and voila! You have your ground height.
FINDING A VEHICLE'S TILT WITH SPARKY'S DLL
Using the method of determining ground height can be applied to find the angles at which a vehicle may tilt on uneven terrain. Here is the outline:
1. Create a pivot object that will be positioned around the terrain
2. Create your vehicle object that will be glued to the pivot object. The vehicle can also be a limb to the pivot object but for now, let's just glue the vehicle to the pivot.
3. Cast a ray from the center position of the pivot down through the terrain and get the y intersect value
4. Cast a ray from the front of the vehicle down through the terrain and get y
5. Cast a ray from the back of the vehicle down through the terrain and get y
6. Cast a ray from the left of the vehicle down through the terrain and get y
7. Cast a ray from the right of the vehicle down through the terrain and get y
Just like in the previous example where we used feelers and found the slopes of the vehicle, we will do the same but using the DLL commands and ray casting to find collision points. Use the center ray cast to set the height. Use the arctangent of the difference between the back and front heights divided by the z length of the vehicle to figure out the X angle tilt. Use the arctangent of the difference between the left and right of the vehicle divided by the x width of the vehicle to figure out the Z angle tilt. Then xrotate and zrotate the vehicle (not the pivot object) according to those angles.
Please review Example3.dba for a demonstration of getting the ground height using Sparky's Collision DLL, and Example4.dba for a demonstration on how to get the vehicle tilt.
In both the DLL and DarkBASIC collision tilt examples, there is a generic function to calculate tilt that can be used in most situations where you want to calculate the tilt of an object on a terrain. This should get you up and running with your project fairly quickly.
A BIT ABOUT MATH TO CALCULATE GROUND HEIGHT
I hadn't planned on going much into this and may do so at a later date, but I think it's important to at least touch on a concept or two. I'll start with slope.
We've already seen that the tilt of a vehicle can be calculated using the slope of various points around our vehicle. We could use a similar method to get the ground height at any point on our terrain without using collision. If our terrain is made up of nice and neat sqaures where each "tile" is made up of 4 points, we can find the slope of any or all of the edges that make up the tile. If the tile is divided into two triangles (which would most likely be the case) we would have to know which edge belongs to which triangle and over which triangle we are trying to find the ground height. With that information, we can use the current position and find it's height using the angles we would have calculated using the slope, and some trig.
A more standard way, is to use the the normal of a face; a face being one of the triangles I mentioned make up a tile on our terrain. The normal is a vector that points perpendicular to the face. By using the dot product we can use the normal, the point we want to check, and a starting point to figure out the height.
Unless you have a parsing function, if you load a terrain into DarkBASIC Classic, you're not going to have an easy time finding the normals. Even if you use MAKE MEMBLOCK FROM MESH to convert your terrain to a memblock, the normals that are stored there are vertex normals and they are indexed to the vertices. This means the vertices can be in any order in the memblock and it can be tricky to try and figure out which face you are above. This is the same trouble you'd run into if you were trying the slope method height calculation.
To solve this, you could go through every face index and figure out an order for your vertices. From there you could average the vertex normals to get the face normal - or you could recalculate the face normal using the crossproduct, as the vertex normals may be an average of several faces surround the particular vertex and the average of them may not give you your face normal.
Or better yet, take matters into your own hands and sort the vertices from bottom to top or top to bottom of your terrain. From there calculate the face normals, and then use the dot product to calculate the height at any given point.
I'll stop there for now giving you a bit of meat to chew on.
Enjoy your day.