I'm writing a 3D world game which consists of a terrain map (generated by brownian motion algorithm) and a simple avatar. My goal is to make the avatar move smoothly between points of different height. Originally, I simply translate from start location to stop location, then set the height of this point based on the height value of a pair (x, z). Unfortunately the movement is completely off. I searched online I found several algorithms to create a smooth movement but I was not able to put them together. I just started to lean graphics for a month, so sorry for my dull knowledge.
My height map size is 512 * 512, and the spacing between each vertex is 150. Base on these values, I create a square around my destination as follows:
/*
* A B
* --------------
* | |
* | x |
* | |
* | |
* --------------
* C D
*/
// build square ABCD around stopLocation
int x = (int)stopLocation.X / 150;
int z = (int)stopLocation.Z / 150;
Vector3 A = new Vector3(x * 150, stage.surfaceHeight(x * 150, z * 150), z * 150);
Vector3 B = new Vector3((x + 1) * 150, stage.surfaceHeight((x + 1) * 150, z * 150), z * 150);
Vector3 C = new Vector3(x * 150, stage.surfaceHeight(x * 150, (z + 1) * 150), (z + 1) * 150);
Vector3 D = new Vector3((x + 1) * 150, stage.surfaceHeight((x + 1) * 150, (z + 1) * 150), (z + 1) * 150);
I use a simple algorithm to determine my destination plane:
public bool isInUpperPlane(Vector3 A, Vector3 B, Vector3 C, Vector3 D, Vector3 point) {
/*
^ y
|
|
|
| A B
----------------------------> x
/ /
/ x /
C /_________ / D
/
/
/
v z
*/
// consider A as the origin
if (Math.Abs(point.X - A.X) + Math.Abs(point.Z - A.Z) >= 150)
return true;
return false;
}
Next I construct a plane the 3 vertices (either ABC or BCD):
Plane destPlane;
if (isInUpperPlane(A, B, C, D, stopLocation)) {
destPlane = new Plane(A, B, C);
}
else {
destPlane = new Plane(B, C, D);
}
Then I project this my start location to this plane normal, and translate my avatar to this location:
Vector3 normal = destPlane.Normal;
Vector3 r = projectVectorToPlane(startLocation, normal);
Orientation *= Matrix.CreateTranslation(startLocation);
But doing this make my avatar stays still. It's very very wrong, but I really don't know how it actually works. The 3D world is just too confusing at this moment. Could anyone help me explain what's go wrong? An minimal working example would be greatly appreciated.
This is my original implementation, (not smooth following)
public void updateMovableObject() {
Vector3 startLocation = Translation;
Vector3 stopLocation = Translation;
Orientation *= Matrix.CreateTranslation(-1 * Translation); // move to origin
Orientation *= Matrix.CreateRotationY(yaw); // rotate
Orientation *= Matrix.CreateRotationX(pitch);
Orientation *= Matrix.CreateRotationZ(roll);
stopLocation += ((step * stepSize) * Forward); // move forward
// if collision, reset location and return
if (model.IsCollidable && collision(stopLocation)) {
Orientation *= Matrix.CreateTranslation(startLocation); // don't move
return;
}
// no collision test if move on terrain
if (stage.withinRange(this.Name, stopLocation)) {
Orientation *= Matrix.CreateTranslation(stopLocation); // move forward
}
else { // off terrain, reset location
Orientation *= Matrix.CreateTranslation(startLocation); // don't move
}
}
stage.setSurfaceHeight(this);
public void setSurfaceHeight(Object3D anObject3D) {
float terrainHeight = terrain.surfaceHeight((int)(anObject3D.Translation.X / spacing), (int)(anObject3D.Translation.Z / spacing));
anObject3D.Translation = new Vector3(anObject3D.Translation.X, terrainHeight, anObject3D.Translation.Z);
}
EDIT
public float surfaceHeight(int x, int z) {
if (x < 0 || x > 511 || z < 0 || z > 511)
return 0.0f; // index valid ?
return (float)terrainHeight[x, z];
}