Skip to content

Commit

Permalink
Merge pull request #12 from VirxEC/rocketsim
Browse files Browse the repository at this point in the history
Incorporate accuracy improvements from RocketSim
  • Loading branch information
VirxEC authored May 9, 2024
2 parents 7651c6f + d0e3f29 commit 8ad396b
Show file tree
Hide file tree
Showing 45 changed files with 1,264 additions and 380 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
.vscode/
/.vscode
/target
/venv
ball_prediction.json
Cargo.lock
*.svg
perf*
*.dump
*.json
/collision_meshes
18 changes: 14 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ name = "rl_ball_sym"
version = "4.0.0"
authors = ["VirxEC"]
edition = "2021"
description = "Rust implementation of ball path prediction for Rocket League; Inspired by Samuel (Chip) P. Mish's C++ utils called RLUtilities"
description = "rl_ball_sym is a Rust implementation of Rocket League's ball physics"
readme = "README.md"
repository = "https://github.com/VirxEC/rl_ball_sym"
license = "MIT"
keywords = ["rocket-league", "rlbot", "physics", "simulation"]
categories = ["science", "simulation", "mathematics"]
include = ["src/", "assets/"]
rust-version = "1.65"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
all = "warn"

[dev-dependencies]
rand = "0.8"
once_cell = "1.17.1"
criterion = { version = "0.5.0", features = ["html_reports"] }
serde_json = "1.0.107"
colored = "2.0.4"
rocketsim_rs = { version = "0.28.0", features = ["glam"] }

[dependencies]
byteorder = "1"
glam = "0.25.0"
glam = "0.27.0"
radsort = "0.1"
combo_vec = { version = "0.7.1", default_features = false }
combo_vec = { version = "0.7.1", default-features = false }
include-flate = { version = "0.3.0", optional = true }
serde = { version = "1.0.188", features = ["derive"], optional = true }

Expand Down Expand Up @@ -55,3 +61,7 @@ lto = true
name = "benchmarks"
path = "benches/benchmarks.rs"
harness = false

[[example]]
name = "accuracy"
required-features = ["serde"]
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@

Rust implementation of Rocket League's ball physics;
Inspired by Samuel P. Mish's C++ utils called [RLUtilities](https://github.com/samuelpmish/RLUtilities)
with accuracy improvements from [RocketSim](https://github.com/ZealanL/RocketSim)
and other miscellaneous performance improvements.

This crate also contains fixes to discovered errors stemming from the original repo.

## Running

Make sure you have Rust/Cargo installed, then just run `cargo test --release` in the terminal.
Expand Down Expand Up @@ -38,11 +37,11 @@ Numbers _will_ vary depending on your system.
+ `load_hoops`: Loads 15732 triangles, executes in around `1.30ms`
+ `load_dropshot`: Loads 3616 triangles, executes in around `300µs`
+ `load_standard_throwback`: Loads 9272 triangles, executes in around `805µs`
+ `get_ball_prediction_struct_for_time`: standard + 8 seconds, executes in around `130µs`
+ `get_ball_prediction`: standard + 6 seconds, executes in around `90µs`
+ `get_ball_prediction`: Hoops + 6 seconds, executes in around `120µs`
+ `get_ball_prediction`: Dropshot + 6 seconds, executes in around `110µs`
+ `get_ball_prediction`: standard + Throwback Stadium + 6 seconds, executes in around `100µs`
+ `get_ball_prediction_struct_for_time`: standard + 8 seconds, executes in around `220µs`
+ `get_ball_prediction`: standard + 6 seconds, executes in around `165µs`
+ `get_ball_prediction`: Hoops + 6 seconds, executes in around `160µs`
+ `get_ball_prediction`: Dropshot + 6 seconds, executes in around `140µs`
+ `get_ball_prediction`: standard + Throwback Stadium + 6 seconds, executes in around `175µs`

## Features

Expand Down
84 changes: 84 additions & 0 deletions analysis/accuracy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import json
from pathlib import Path

import matplotlib.pyplot as plt

# run `cargo r --example accuracy --features=serde` first!

with open(Path(__file__).parent / "accuracy.json") as f:
data = json.load(f)

rocketsim = [[], [], [], []]
rl_ball_sym = [[], [], [], []]

for ball in data["rocketsim"]:
rocketsim[0].append(ball["time"])
rocketsim[1].append(ball["location"])
rocketsim[2].append(ball["velocity"])
rocketsim[3].append(ball["angular_velocity"])

for ball in data["rl_ball_sym"]:
rl_ball_sym[0].append(ball["time"])
rl_ball_sym[1].append(ball["location"])
rl_ball_sym[2].append(ball["velocity"])
rl_ball_sym[3].append(ball["angular_velocity"])

start_time = 0 * 120
# time_limit = 250
time_limit = len(rocketsim[0])

# start_time = 7 * 120
# time_limit = 8 * 120

location_diff = []

for i in range(start_time, time_limit):
location_diff.append(
(
(rocketsim[1][i][0] - rl_ball_sym[1][i][0]) ** 2
+ (rocketsim[1][i][1] - rl_ball_sym[1][i][1]) ** 2
+ (rocketsim[1][i][2] - rl_ball_sym[1][i][2]) ** 2
)
** 0.5
)

if location_diff[-1] > 0:
print(rl_ball_sym[0][i])

velocity_diff = []

for i in range(start_time, time_limit):
velocity_diff.append(
(
(rocketsim[2][i][0] - rl_ball_sym[2][i][0]) ** 2
+ (rocketsim[2][i][1] - rl_ball_sym[2][i][1]) ** 2
+ (rocketsim[2][i][2] - rl_ball_sym[2][i][2]) ** 2
)
** 0.5
)

angular_velocity_diff = []

for i in range(start_time, time_limit):
angular_velocity_diff.append(
(
(rocketsim[3][i][0] - rl_ball_sym[3][i][0]) ** 2
+ (rocketsim[3][i][1] - rl_ball_sym[3][i][1]) ** 2
+ (rocketsim[3][i][2] - rl_ball_sym[3][i][2]) ** 2
)
** 0.5
)

ax = plt.figure().add_subplot()

ax.plot(rocketsim[0][start_time:time_limit], location_diff, label="location")
ax.plot(rocketsim[0][start_time:time_limit], velocity_diff, label="velocity")
ax.plot(rocketsim[0][start_time:time_limit], angular_velocity_diff, label="angular velocity")

ax.legend()
ax.set_title("Time vs Difference")
ax.set_xlabel("Time")
ax.set_ylabel("Magnitude of difference")

plt.show()

31 changes: 31 additions & 0 deletions analysis/angular_velocity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import json
import matplotlib.pyplot as plt

with open("accuracy.json") as f:
data = json.load(f)

rocketsim = [[], [], []]
rl_ball_sym = [[], [], []]

for ball in data["rocketsim"]:
rocketsim[0].append(ball["angular_velocity"][0])
rocketsim[1].append(ball["angular_velocity"][1])
rocketsim[2].append(ball["angular_velocity"][2])

for ball in data["rl_ball_sym"]:
rl_ball_sym[0].append(ball["angular_velocity"][0])
rl_ball_sym[1].append(ball["angular_velocity"][1])
rl_ball_sym[2].append(ball["angular_velocity"][2])

ax = plt.figure().add_subplot(projection='3d')

ax.plot(rocketsim[0], rocketsim[1], rocketsim[2], label="rocketsim")
ax.plot(rl_ball_sym[0], rl_ball_sym[1], rl_ball_sym[2], label="rl_ball_sym")

ax.legend()
ax.set_title("Angular Velocity")
ax.set_xlabel("X")
ax.set_ylabel("Z")
ax.set_zlabel("Y")

plt.show()
31 changes: 31 additions & 0 deletions analysis/location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import json
import matplotlib.pyplot as plt

with open("accuracy.json") as f:
data = json.load(f)

rocketsim = [[], [], []]
rl_ball_sym = [[], [], []]

for ball in data["rocketsim"]:
rocketsim[0].append(ball["location"][0])
rocketsim[1].append(ball["location"][1])
rocketsim[2].append(ball["location"][2])

for ball in data["rl_ball_sym"]:
rl_ball_sym[0].append(ball["location"][0])
rl_ball_sym[1].append(ball["location"][1])
rl_ball_sym[2].append(ball["location"][2])

ax = plt.figure().add_subplot(projection='3d')

ax.plot(rocketsim[0], rocketsim[1], rocketsim[2], label="rocketsim")
ax.plot(rl_ball_sym[0], rl_ball_sym[1], rl_ball_sym[2], label="rl_ball_sym")

ax.legend()
ax.set_title("Location")
ax.set_xlabel("X")
ax.set_ylabel("Z")
ax.set_zlabel("Y")

plt.show()
31 changes: 31 additions & 0 deletions analysis/velocity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import json
import matplotlib.pyplot as plt

with open("accuracy.json") as f:
data = json.load(f)

rocketsim = [[], [], []]
rl_ball_sym = [[], [], []]

for ball in data["rocketsim"]:
rocketsim[0].append(ball["velocity"][0])
rocketsim[1].append(ball["velocity"][1])
rocketsim[2].append(ball["velocity"][2])

for ball in data["rl_ball_sym"]:
rl_ball_sym[0].append(ball["velocity"][0])
rl_ball_sym[1].append(ball["velocity"][1])
rl_ball_sym[2].append(ball["velocity"][2])

ax = plt.figure().add_subplot(projection='3d')

ax.plot(rocketsim[0], rocketsim[1], rocketsim[2], label="rocketsim")
ax.plot(rl_ball_sym[0], rl_ball_sym[1], rl_ball_sym[2], label="rl_ball_sym")

ax.legend()
ax.set_title("Velocity")
ax.set_xlabel("X")
ax.set_ylabel("Z")
ax.set_zlabel("Y")

plt.show()
Binary file modified assets/hoops/hoops_corner_ids.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_corner_vertices.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_net_ids.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_net_vertices.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_ramps_0_ids.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_ramps_0_vertices.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_ramps_1_ids.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_ramps_1_vertices.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_rim_ids.bin
Binary file not shown.
Binary file modified assets/hoops/hoops_rim_vertices.bin
Binary file not shown.
Binary file modified assets/standard/standard_corner_vertices.bin
Binary file not shown.
Binary file modified assets/standard/standard_goal_ids.bin
Binary file not shown.
Binary file modified assets/standard/standard_goal_vertices.bin
Binary file not shown.
Binary file modified assets/standard/standard_ramps_0_ids.bin
Binary file not shown.
Binary file modified assets/standard/standard_ramps_0_vertices.bin
Binary file not shown.
Binary file modified assets/standard/standard_ramps_1_ids.bin
Binary file not shown.
Binary file modified assets/standard/standard_ramps_1_vertices.bin
Binary file not shown.
8 changes: 4 additions & 4 deletions benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn get_ball_prediction_struct_benchmark(c: &mut Criterion) {
ball.velocity = BALL_VEL;

c.bench_function("get_ball_prediction/standard", |b| {
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)))
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)));
});
}

Expand All @@ -46,7 +46,7 @@ fn get_ball_prediction_struct_hoops_benchmark(c: &mut Criterion) {
ball.velocity = BALL_VEL;

c.bench_function("get_ball_prediction/hoops", |b| {
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)))
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)));
});
}

Expand All @@ -55,7 +55,7 @@ fn get_ball_prediction_struct_dropshot(c: &mut Criterion) {
ball.velocity = BALL_VEL;

c.bench_function("get_ball_prediction/dropshot", |b| {
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)))
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)));
});
}

Expand All @@ -64,7 +64,7 @@ fn get_ball_prediction_struct_throwback(c: &mut Criterion) {
ball.velocity = BALL_VEL;

c.bench_function("get_ball_prediction/throwback", |b| {
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)))
b.iter(|| ball.get_ball_prediction_struct(black_box(&game)));
});
}

Expand Down
80 changes: 80 additions & 0 deletions examples/accuracy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use byteorder::{LittleEndian, ReadBytesExt};
use colored::Colorize;
use rl_ball_sym::{load_standard, Ball};
use std::{fs, io};

fn read_balls(file_name: &str, mut ball: Ball) -> io::Result<Vec<Ball>> {
let mut file = fs::File::open(file_name)?;
let num_balls = file.read_u16::<LittleEndian>()? as usize;

let mut balls = Vec::with_capacity(num_balls);

for _ in 0..num_balls {
ball.time = file.read_f32::<LittleEndian>()?;
ball.location.x = file.read_f32::<LittleEndian>()?;
ball.location.y = file.read_f32::<LittleEndian>()?;
ball.location.z = file.read_f32::<LittleEndian>()?;
ball.velocity.x = file.read_f32::<LittleEndian>()?;
ball.velocity.y = file.read_f32::<LittleEndian>()?;
ball.velocity.z = file.read_f32::<LittleEndian>()?;
ball.angular_velocity.x = file.read_f32::<LittleEndian>()?;
ball.angular_velocity.y = file.read_f32::<LittleEndian>()?;
ball.angular_velocity.z = file.read_f32::<LittleEndian>()?;

balls.push(ball);
}

Ok(balls)
}

#[derive(serde::Serialize)]
struct BallDump {
rocketsim: Vec<Ball>,
rl_ball_sym: Vec<Ball>,
}

fn main() -> io::Result<()> {
let (game, mut ball) = load_standard();

let rocketsim = read_balls("examples/ball.dump", ball)?;

println!("Starting config:");
println!("Location: {}", rocketsim[0].location);
println!("Velocity: {}", rocketsim[0].velocity);
println!("Angular Velocity: {}", rocketsim[0].angular_velocity);

println!();

let mut rl_ball_sym = Vec::with_capacity(rocketsim.len());
rl_ball_sym.push(rocketsim[0]);

ball = rocketsim[0];
for cball in rocketsim.iter().copied().take(5) {
print_error(ball, cball);

println!("{}", "[START OF TICK]".green());
ball = cball;
ball.step(&game, 1. / 120.);
rl_ball_sym.push(ball);
println!("{}", "[END OF TICK]".red());
}

fs::write(
"analysis/accuracy.json",
serde_json::to_string(&BallDump { rocketsim, rl_ball_sym }).unwrap(),
)?;

Ok(())
}

fn print_error(ball: Ball, cball: Ball) {
println!("{} | {}", ball.location / 50., cball.location / 50.);
println!("{} | {}", ball.velocity / 50., cball.velocity / 50.);
println!(
"Error: {}, {}, {}",
ball.location.distance(cball.location),
ball.velocity.distance(cball.velocity),
ball.angular_velocity.distance(cball.angular_velocity)
);
println!("Vel error: {}", ball.velocity - cball.velocity);
}
Loading

0 comments on commit 8ad396b

Please sign in to comment.