You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/_posts/2019-12-12-data-formats.md
+20-1
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,8 @@ title: Data Formats
5
5
6
6
The original game features vast levels that were destructible and "live": there was often something moving underground, gates opening and closing, seasons changing, making the world feel very natural. But how could all of this be even stored in memory of a machine from the last century? 128Mb of RAM was supposed to be enough for the whole system running the game.
7
7
8
+
For the technical breakdown of the formats please refer to the [wiki page](https://github.com/kvark/vange-rs/wiki). This blog post focuses on intuitive description that allows reasoning about the data.
9
+
8
10
## Level
9
11
10
12
Level data is stored and maintained on a per-texel as a multi-layer height map with metadata. Every point of potentially 4096x32768 resolution of a level has its own 1-byte height and 1-byte metadata. For areas that are double-layered, two horizontally adjacent cells are merged to represent the 2x1 segment of the map. They encode the following values:
@@ -63,4 +65,21 @@ NameID RiverBier
63
65
Finally, this is a line from `wrlds.dat`:
64
66
```
65
67
Necross thechain/necross/world.ini
66
-
```
68
+
```
69
+
70
+
## Palette
71
+
72
+
The game has various color tables for objects and levels, depending on the current season (yes, the game had dynamic season change in 1998). Palettes are stored as 256 triples of bytes, each representing a color component that for some reason is divided by 4:
Copy file name to clipboardExpand all lines: docs/_posts/2019-12-17-collision-model.md
+21-14
Original file line number
Diff line number
Diff line change
@@ -19,12 +19,14 @@ These parameters are updated regularly with regards to the forces, impulses, tim
19
19
20
20
### Impulses
21
21
22
-
First, we consider all collisions and pushes from another objects as well as the terrain, in order to have a list of impulses that affect the body. Collision vector is computed from the current velocities and the point of contact:
22
+
First, we consider all collisions and pushes from another objects as well as the terrain, in order to have a list of impulses that affect the body. The collision vector is computed as a projection of the current velocity at the collision point, projected onto the direction of the collision:
The collision vector is scaled based on some constants that determine the collision power. In order to evaluate the impulse, we then compute the local collision matrix:
29
+
In order to evaluate the impulse, we then compute the local collision matrix:
Here, `r` is the point of contact, and `ji` is an inverted Jacobian matrix that is adjusted for volume and scale. These come from the physics part of the model data as described in the [Data Formats]({{site.baseurl}}/{% post_url 2019-12-12-data-formats %}). My understanding is that the matrix represents an approximation of the shape of an object, and the fact that it may respond differently to collisions coming from different directions.
45
+
Here, `r` is the point of contact, and `ji` is an inverted Jacobian matrix that is adjusted for volume and scale. These come from the physics part of the model data as described in the [Data Formats]({{site.baseurl}}/{% post_url 2019-12-12-data-formats %}). My understanding is that the matrix represents an approximation of the distribution of mass in an object, which makes it react respond differently to collisions coming from different directions.
44
46
45
47
Once the local collision matrix is computed, the raw impulse can be derived as:
Forces are also tracked separately for translation and rotation. Whenever a force vector affects the body at a particular point, we compute the linear and angular components as follows:
60
+
{: #force-equation }
58
61
```rust
59
-
fnapply_force(vector, point) {
62
+
fnapply_force(point, vector) {
60
63
linear_force+=vector;
61
64
angular_force+=cross(point, vector);
62
65
}
63
66
```
64
67
65
68
First force that is always present is gravity. It's applied at point `(0, 0, z_offset_of_mass_center)`, which comes from the model parameters.
66
69
67
-
Interestingly, collisions also affect forces, or more specifically - the spring force. It corresponds to some in-game machinery of a car that puts pressure in all directions and can be activated for a jump. Spring force is applied at `collision_point` with `collision_vector`.
70
+
Interestingly, collisions also affect forces, or more specifically - the spring force. It corresponds to some in-game machinery of a car that puts pressure in all directions and can be activated for a jump. Spring force is applied [as the force equation](#force-equation)at `collision_point` with `collision_vector`.
68
71
69
-
Note: this part that may need to take the time delta into account in order to have smooth physics simulation with variable frame rate.
70
-
71
-
Finally, local effects like vortexes may also contribute to the forces.
72
+
Finally, local effects like vortexes or artifact abilities may also contribute to the forces.
72
73
73
74
Before the forces can translate into the velocity change, we need to make sure they are converted into the local space. The application is done as follows:
So technically a force works the same way as an impulse integrated over time, as if the `collision_vector` is given (as opposed to being computed based on the velocities), which makes the whole model rather elegant in my eyes. In `vange-rs`, both paths go through a "raw impulse" representation that is common between forces and impulses:
80
+
So technically a force works the same way as an impulse integrated over time, as if the `collision_vector` is given (as opposed to being computed), which makes the whole model rather elegant in my eyes. In `vange-rs`, both paths go through a "raw impulse" representation that is common between forces and impulses:
80
81
- for forces, they are pre-multiplied by `time_delta`
81
82
- for impulses, the angular component comes from the `cross(point, vector)`
82
83
- multiplication by `jacobian_inv` is done only once at the end of the simulation step
@@ -120,18 +121,24 @@ At the end of the step (after the position and orientation are updated), the vel
120
121
121
122
Remember the collision shapes we described in the [Data Formats]({{site.baseurl}}/{% post_url 2019-12-12-data-formats %})? These simplified quad-based mesh approximations get intersected with the terrain at each step. How? By just rasterizing them on the terrain and sampling the heights (and metadata) at each intersection point.
122
123
123
-
For each collision shape quad, we find the average in the penetration depth (along the Z axis) as well as the point of contact. Then we simply generate an impulse at that point with the vector pointing downwards, following the regular impulse equations. This picture shows the averaged contact points and vectors:
124
+
For each collision shape quad, we find the average in the penetration depth (along the Z axis) as well as the point of contact. Then we simply generate an impulse at that point with the vector pointing downwards, following the regular [impulse equations](#impulse-equation). This picture shows the averaged contact points and vectors:
Note: more precisely, the pixels are split into groups for "soft" contacts and "hard" constants, and these averaged contact points are used differently for some logic, like the horizontal wall collisions.
128
129
129
130
### Controls
130
131
131
-
User-controlled car is also affected by the traction, which is computed separately for each wheel. The basic logic is similar to a regular impulse computed at the wheel position (based on the current velocities), but with the collision vector projected onto the rudder vector (steering direction):
132
+
User-controlled car is also affected by the traction, which is computed separately for each wheel. Each wheel generates an impulse at its position along the rudder vector, following the same [impulse equation](#impulse-equation):
0 commit comments