Refactor asset loading to remove duplication, add loading of tyre params from csv
This commit is contained in:
parent
a27a5ccde6
commit
bdd7bbb9b1
19
assets/tyre_lateral.csv
Normal file
19
assets/tyre_lateral.csv
Normal file
@ -0,0 +1,19 @@
|
||||
a
|
||||
1.5
|
||||
0
|
||||
1100
|
||||
1100
|
||||
10
|
||||
0
|
||||
0
|
||||
-2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
|
15
assets/tyre_longitudinal.csv
Normal file
15
assets/tyre_longitudinal.csv
Normal file
@ -0,0 +1,15 @@
|
||||
b
|
||||
1.5
|
||||
-2.0
|
||||
1100
|
||||
0
|
||||
300
|
||||
0
|
||||
0
|
||||
0
|
||||
-2
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
|
@ -16,16 +16,6 @@ import "vendor:raylib/rlgl"
|
||||
|
||||
_ :: math
|
||||
|
||||
Loaded_Texture :: struct {
|
||||
texture: rl.Texture2D,
|
||||
modtime: c.long,
|
||||
}
|
||||
|
||||
Loaded_Model :: struct {
|
||||
model: rl.Model,
|
||||
modtime: c.long,
|
||||
}
|
||||
|
||||
Loaded_BVH :: struct {
|
||||
// AABB of all bvhs
|
||||
aabb: bvh.AABB,
|
||||
@ -53,39 +43,193 @@ destroy_loaded_bvh :: proc(loaded_bvh: Loaded_BVH) {
|
||||
delete(loaded_bvh.bvhs)
|
||||
}
|
||||
|
||||
Curve_2D :: [][2]f32
|
||||
|
||||
Asset_Manager :: struct {
|
||||
textures: map[cstring]Loaded_Texture,
|
||||
models: map[cstring]Loaded_Model,
|
||||
textures: Asset_Cache(rl.Texture2D),
|
||||
models: Asset_Cache(rl.Model),
|
||||
curves_1d: Asset_Cache([]f32),
|
||||
curves_2d: Asset_Cache(Curve_2D),
|
||||
bvhs: map[cstring]Loaded_BVH,
|
||||
curves: map[cstring]Loaded_Curve_2D,
|
||||
}
|
||||
|
||||
Asset_Cache_Entry :: struct($E: typeid) {
|
||||
value: E,
|
||||
modtime: i64,
|
||||
}
|
||||
|
||||
Asset_Cache_Loader :: struct($E: typeid) {
|
||||
load: proc(path: cstring) -> (E, bool),
|
||||
unload: proc(value: E),
|
||||
}
|
||||
|
||||
Asset_Cache :: struct($E: typeid) {
|
||||
cache: map[cstring]Asset_Cache_Entry(E),
|
||||
loader: Asset_Cache_Loader(E),
|
||||
}
|
||||
|
||||
MODEL_LOADER :: Asset_Cache_Loader(rl.Model) {
|
||||
load = proc(path: cstring) -> (rl.Model, bool) {
|
||||
model := rl.LoadModel(path)
|
||||
return model, rl.IsModelValid(model)
|
||||
},
|
||||
unload = proc(model: rl.Model) {
|
||||
rl.UnloadModel(model)
|
||||
},
|
||||
}
|
||||
|
||||
TEXTURE_LOADER :: Asset_Cache_Loader(rl.Texture2D) {
|
||||
load = proc(path: cstring) -> (rl.Texture2D, bool) {
|
||||
texture := rl.LoadTexture(path)
|
||||
return texture, rl.IsTextureValid(texture)
|
||||
},
|
||||
unload = proc(texture: rl.Texture2D) {
|
||||
rl.UnloadTexture(texture)
|
||||
},
|
||||
}
|
||||
|
||||
CURVE_1D_CSV_LOADER :: Asset_Cache_Loader([]f32) {
|
||||
load = proc(path: cstring) -> ([]f32, bool) {
|
||||
data, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
||||
if err != nil {
|
||||
log.errorf("Failed to read curve: %s, %v", path, err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
values, err2 := parse_csv_1d(data)
|
||||
|
||||
if err2 != nil {
|
||||
log.errorf("Failed to parse curve: %s, %v", path, err2)
|
||||
}
|
||||
|
||||
return values, true
|
||||
},
|
||||
unload = proc(values: []f32) {
|
||||
delete(values)
|
||||
},
|
||||
}
|
||||
|
||||
CURVE_2D_CSV_LOADER :: Asset_Cache_Loader(Curve_2D) {
|
||||
load = proc(path: cstring) -> (Curve_2D, bool) {
|
||||
data, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
||||
if err != nil {
|
||||
log.errorf("Failed to read curve: %s, %v", path, err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
curve, err2 := parse_csv_2d(data)
|
||||
|
||||
if err2 != nil {
|
||||
log.errorf("Failed to parse curve: %s, %v", path, err2)
|
||||
}
|
||||
|
||||
return curve.points, true
|
||||
},
|
||||
unload = proc(curve: Curve_2D) {
|
||||
delete(curve)
|
||||
},
|
||||
}
|
||||
|
||||
assetman_init :: proc(assetman: ^Asset_Manager) {
|
||||
assetman.models = {
|
||||
loader = MODEL_LOADER,
|
||||
}
|
||||
assetman.textures = {
|
||||
loader = TEXTURE_LOADER,
|
||||
}
|
||||
assetman.curves_1d = {
|
||||
loader = CURVE_1D_CSV_LOADER,
|
||||
}
|
||||
assetman.curves_2d = {
|
||||
loader = CURVE_2D_CSV_LOADER,
|
||||
}
|
||||
}
|
||||
|
||||
Asset_Cache_Result :: enum {
|
||||
Cached,
|
||||
Loaded,
|
||||
Reloaded,
|
||||
Error,
|
||||
}
|
||||
|
||||
assetcache_fetch_or_load :: proc(
|
||||
ac: ^$T/Asset_Cache($E),
|
||||
path: cstring,
|
||||
) -> (
|
||||
value: E,
|
||||
modtime: i64,
|
||||
result: Asset_Cache_Result,
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
existing, has_existing := ac.cache[path]
|
||||
|
||||
if has_existing {
|
||||
new_modtime := physfs.getLastModTime(path)
|
||||
|
||||
if existing.modtime == new_modtime {
|
||||
result = .Cached
|
||||
return existing.value, new_modtime, result
|
||||
} else {
|
||||
// Try to load the new version
|
||||
|
||||
new_value, ok := ac.loader.load(path)
|
||||
|
||||
if ok {
|
||||
result = .Reloaded
|
||||
ac.loader.unload(existing.value)
|
||||
ac.cache[path] = {
|
||||
value = new_value,
|
||||
modtime = new_modtime,
|
||||
}
|
||||
|
||||
log.debugf("reloaded asset: %s", path)
|
||||
|
||||
return new_value, new_modtime, result
|
||||
} else {
|
||||
log.warnf("failed to reload asset after modification %s", path)
|
||||
result = .Cached
|
||||
|
||||
return existing.value, existing.modtime, result
|
||||
}
|
||||
}
|
||||
} else {
|
||||
modtime = physfs.getLastModTime(path)
|
||||
ok: bool
|
||||
value, ok = ac.loader.load(path)
|
||||
|
||||
if ok {
|
||||
ac.cache[path] = {
|
||||
value = value,
|
||||
modtime = modtime,
|
||||
}
|
||||
result = .Loaded
|
||||
|
||||
log.debugf("loaded asset: %s", path)
|
||||
|
||||
return value, modtime, result
|
||||
} else {
|
||||
log.errorf("failed to load asset %s", path)
|
||||
result = .Error
|
||||
return {}, 0, .Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assetcache_destroy :: proc(ac: ^$T/Asset_Cache($E)) {
|
||||
for _, v in ac.cache {
|
||||
ac.loader.unload(v.value)
|
||||
}
|
||||
delete(ac.cache)
|
||||
}
|
||||
|
||||
get_texture :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Texture2D {
|
||||
tracy.Zone()
|
||||
|
||||
modtime := physfs.getLastModTime(path)
|
||||
texture, _, _ := assetcache_fetch_or_load(&assetman.textures, path)
|
||||
|
||||
existing, ok := assetman.textures[path]
|
||||
if ok && existing.modtime == modtime {
|
||||
return existing.texture
|
||||
}
|
||||
|
||||
if ok {
|
||||
rl.UnloadTexture(existing.texture)
|
||||
delete_key(&assetman.textures, path)
|
||||
log.infof("deleted texture %s. New textures len: %d", path, len(assetman.textures))
|
||||
}
|
||||
|
||||
loaded := rl.LoadTexture(path)
|
||||
if rl.IsTextureValid(loaded) {
|
||||
assetman.textures[path] = {
|
||||
texture = loaded,
|
||||
modtime = modtime,
|
||||
}
|
||||
return loaded
|
||||
} else {
|
||||
return rl.Texture2D{}
|
||||
}
|
||||
return texture
|
||||
}
|
||||
|
||||
get_model_ex :: proc(
|
||||
@ -99,31 +243,11 @@ get_model_ex :: proc(
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
new_modtime := physfs.getLastModTime(path)
|
||||
result: Asset_Cache_Result
|
||||
model, modtime, result = assetcache_fetch_or_load(&assetman.models, path)
|
||||
reloaded = result == .Reloaded || ref_modtime != modtime
|
||||
|
||||
existing, ok := assetman.models[path]
|
||||
if ok && existing.modtime == new_modtime {
|
||||
return existing.model,
|
||||
existing.modtime,
|
||||
ref_modtime == 0 ? false : existing.modtime != ref_modtime
|
||||
}
|
||||
|
||||
if ok {
|
||||
rl.UnloadModel(existing.model)
|
||||
delete_key(&assetman.textures, path)
|
||||
log.infof("deleted model %s. New models len: %d", path, len(assetman.textures))
|
||||
}
|
||||
|
||||
loaded := rl.LoadModel(path)
|
||||
if rl.IsModelValid(loaded) {
|
||||
assetman.models[path] = {
|
||||
model = loaded,
|
||||
modtime = new_modtime,
|
||||
}
|
||||
return loaded, new_modtime, true
|
||||
} else {
|
||||
return rl.Model{}, 0, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
get_model :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Model {
|
||||
@ -182,22 +306,14 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
|
||||
return assetman.bvhs[path]
|
||||
}
|
||||
|
||||
// TODO: cache result
|
||||
// Reads a two column comma separated csv file as a curve
|
||||
get_curve_2d :: proc(assetman: ^Asset_Manager, path: string) -> (curve: Loaded_Curve_2D) {
|
||||
data, err := physfs.read_entire_file(path, context.temp_allocator)
|
||||
if err != nil {
|
||||
log.errorf("Failed to read curve: %s, %v", path, err)
|
||||
get_curve_1d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: []f32) {
|
||||
curve, _, _ = assetcache_fetch_or_load(&assetman.curves_1d, path)
|
||||
return
|
||||
}
|
||||
|
||||
err2: Curve_Parse_Error
|
||||
curve, err2 = parse_curve_2d(data, context.temp_allocator)
|
||||
|
||||
if err2 != nil {
|
||||
log.errorf("Failed to parse curve: %s, %v", path, err2)
|
||||
}
|
||||
|
||||
// Reads a two column comma separated csv file as a curve
|
||||
get_curve_2d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: Curve_2D) {
|
||||
curve, _, _ = assetcache_fetch_or_load(&assetman.curves_2d, path)
|
||||
return
|
||||
}
|
||||
|
||||
@ -584,16 +700,13 @@ debug_draw_tetrahedron_wires :: proc(tri: [3]rl.Vector3, p: rl.Vector3, color: r
|
||||
shutdown :: proc(assetman: ^Asset_Manager) {
|
||||
tracy.Zone()
|
||||
|
||||
for _, texture in assetman.textures {
|
||||
rl.UnloadTexture(texture.texture)
|
||||
}
|
||||
for _, model in assetman.models {
|
||||
rl.UnloadModel(model.model)
|
||||
}
|
||||
assetcache_destroy(&assetman.textures)
|
||||
assetcache_destroy(&assetman.models)
|
||||
assetcache_destroy(&assetman.curves_1d)
|
||||
assetcache_destroy(&assetman.curves_2d)
|
||||
|
||||
for _, loaded_bvh in assetman.bvhs {
|
||||
destroy_loaded_bvh(loaded_bvh)
|
||||
}
|
||||
delete(assetman.textures)
|
||||
delete(assetman.models)
|
||||
delete(assetman.bvhs)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ rpm,torque
|
||||
|
||||
@(test)
|
||||
test_curve_parsing :: proc(t: ^testing.T) {
|
||||
curve, err := parse_curve_2d(transmute([]u8)test_csv, context.temp_allocator)
|
||||
curve, err := parse_csv_2d(transmute([]u8)test_csv, context.temp_allocator)
|
||||
|
||||
testing.expect_value(t, err, nil)
|
||||
testing.expect_value(t, len(curve.points), 3)
|
||||
@ -34,9 +34,9 @@ test_curve_parsing :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_curve_parsing_error :: proc(t: ^testing.T) {
|
||||
curve, err := parse_curve_2d(transmute([]u8)test_invalid_csv, context.temp_allocator)
|
||||
curve, err := parse_csv_2d(transmute([]u8)test_invalid_csv, context.temp_allocator)
|
||||
defer free_all(context.temp_allocator)
|
||||
|
||||
testing.expect_value(t, err, Curve_Parse_Error.ExpectedNumber)
|
||||
testing.expect_value(t, err, CSV_Parse_Error.CSV_Parse_Error)
|
||||
testing.expect_value(t, len(curve.points), 0)
|
||||
}
|
||||
|
@ -6,18 +6,71 @@ import "core:io"
|
||||
import "core:log"
|
||||
import "core:strconv"
|
||||
|
||||
Curve_Parse_Error :: enum {
|
||||
CSV_Parse_Error :: enum {
|
||||
Ok,
|
||||
TooManyColumns,
|
||||
ExpectedNumber,
|
||||
}
|
||||
|
||||
parse_curve_2d :: proc(
|
||||
parse_csv_1d :: proc(
|
||||
data: []byte,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
values: []f32,
|
||||
error: CSV_Parse_Error,
|
||||
) {
|
||||
bytes_reader: bytes.Reader
|
||||
bytes.reader_init(&bytes_reader, data)
|
||||
bytes_stream := bytes.reader_to_stream(&bytes_reader)
|
||||
|
||||
csv_reader: csv.Reader
|
||||
csv.reader_init(&csv_reader, bytes_stream, context.temp_allocator)
|
||||
defer csv.reader_destroy(&csv_reader)
|
||||
|
||||
tmp_result := make([dynamic]f32, context.temp_allocator)
|
||||
|
||||
skipped_header := false
|
||||
for {
|
||||
row, err := csv.read(&csv_reader, context.temp_allocator)
|
||||
if err != nil {
|
||||
if err != io.Error.EOF {
|
||||
log.warnf("Failed to read curve %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if len(row) != 1 {
|
||||
log.warnf("expected 1 columns, got %v", len(row))
|
||||
error = .TooManyColumns
|
||||
break
|
||||
}
|
||||
|
||||
val, ok := strconv.parse_f64(row[0])
|
||||
if !ok {
|
||||
if skipped_header {
|
||||
log.warnf("Expected numbers, got %s", row[1])
|
||||
error = .ExpectedNumber
|
||||
break
|
||||
}
|
||||
skipped_header = true
|
||||
continue
|
||||
}
|
||||
|
||||
append(&tmp_result, f32(val))
|
||||
}
|
||||
|
||||
values = make([]f32, len(tmp_result), allocator)
|
||||
copy(values, tmp_result[:])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
parse_csv_2d :: proc(
|
||||
data: []byte,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
curve: Loaded_Curve_2D,
|
||||
error: Curve_Parse_Error,
|
||||
error: CSV_Parse_Error,
|
||||
) {
|
||||
bytes_reader: bytes.Reader
|
||||
bytes.reader_init(&bytes_reader, data)
|
||||
|
@ -345,6 +345,13 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
wheel_back_z := f32(-1.05)
|
||||
wheel_mass := f32(14)
|
||||
|
||||
pacejka_long := physics.slice_to_pacejka94_long(
|
||||
assets.get_curve_1d(&g_mem.assetman, "assets/tyre_longitudinal.csv"),
|
||||
)
|
||||
pacejka_lat := physics.slice_to_pacejka94_lat(
|
||||
assets.get_curve_1d(&g_mem.assetman, "assets/tyre_lateral.csv"),
|
||||
)
|
||||
|
||||
wheel_fl := physics.immediate_suspension_constraint(
|
||||
&world.physics_scene,
|
||||
&runtime_world.solver_state,
|
||||
@ -358,6 +365,8 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
damping = damping,
|
||||
body = runtime_world.car_handle,
|
||||
mass = wheel_mass,
|
||||
pacejka_lat = pacejka_lat,
|
||||
pacejka_long = pacejka_long,
|
||||
},
|
||||
)
|
||||
wheel_fr := physics.immediate_suspension_constraint(
|
||||
@ -373,6 +382,8 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
damping = damping,
|
||||
body = runtime_world.car_handle,
|
||||
mass = wheel_mass,
|
||||
pacejka_lat = pacejka_lat,
|
||||
pacejka_long = pacejka_long,
|
||||
},
|
||||
)
|
||||
wheel_rl := physics.immediate_suspension_constraint(
|
||||
@ -388,6 +399,8 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
damping = damping,
|
||||
body = runtime_world.car_handle,
|
||||
mass = wheel_mass,
|
||||
pacejka_lat = pacejka_lat,
|
||||
pacejka_long = pacejka_long,
|
||||
},
|
||||
)
|
||||
wheel_rr := physics.immediate_suspension_constraint(
|
||||
@ -403,6 +416,8 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
damping = damping,
|
||||
body = runtime_world.car_handle,
|
||||
mass = wheel_mass,
|
||||
pacejka_lat = pacejka_lat,
|
||||
pacejka_long = pacejka_long,
|
||||
},
|
||||
)
|
||||
|
||||
@ -411,10 +426,13 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
|
||||
&runtime_world.solver_state,
|
||||
#hash("engine", "fnv32a"),
|
||||
physics.Engine_Config {
|
||||
rpm_torque_curve = assets.get_curve_2d(&g_mem.assetman, "assets/ae86_rpm_torque.csv").points,
|
||||
rpm_torque_curve = assets.get_curve_2d(
|
||||
&g_mem.assetman,
|
||||
"assets/ae86_rpm_torque.csv",
|
||||
),
|
||||
lowest_rpm = 1100,
|
||||
inertia = 0.264,
|
||||
internal_friction = 0.01,
|
||||
internal_friction = 0.00,
|
||||
gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861},
|
||||
axle = physics.Drive_Axle_Config {
|
||||
wheels = {wheel_rl, wheel_rr},
|
||||
@ -1080,6 +1098,7 @@ game_init :: proc() {
|
||||
g_mem^ = Game_Memory{}
|
||||
|
||||
init_physifs_raylib_callbacks()
|
||||
assets.assetman_init(&g_mem.assetman)
|
||||
|
||||
physics.init_physics_scene(&g_mem.runtime_world.world.physics_scene, 100)
|
||||
|
||||
|
@ -9,7 +9,7 @@ Pacejka94_Lateral_Params :: [18]f32
|
||||
PACEJKA94_LONGITUDINAL_PEAK_X :: 6.24
|
||||
PACEJKA94_LONGITUDINAL_PARAMS :: Pacejka94_Longitudinal_Params {
|
||||
1.5,
|
||||
0,
|
||||
-2.0,
|
||||
1100,
|
||||
0,
|
||||
300,
|
||||
@ -91,3 +91,17 @@ pacejka_94_lateral :: proc(
|
||||
F := D * math.sin(C * math.atan(Bx1 - E * (Bx1 - math.atan(Bx1)))) + V
|
||||
return F / (f_z * 1000)
|
||||
}
|
||||
|
||||
slice_to_pacejka94_long :: proc(values: []f32) -> (result: Pacejka94_Longitudinal_Params) {
|
||||
assert(len(values) == len(Pacejka94_Longitudinal_Params))
|
||||
|
||||
copy(result[:], values)
|
||||
return result
|
||||
}
|
||||
|
||||
slice_to_pacejka94_lat :: proc(values: []f32) -> (result: Pacejka94_Lateral_Params) {
|
||||
assert(len(values) == len(Pacejka94_Lateral_Params))
|
||||
|
||||
copy(result[:], values)
|
||||
return result
|
||||
}
|
||||
|
@ -146,6 +146,8 @@ Suspension_Constraint :: struct {
|
||||
natural_frequency: f32,
|
||||
// How much to damp velocity of the spring
|
||||
damping: f32,
|
||||
pacejka_long: Pacejka94_Longitudinal_Params,
|
||||
pacejka_lat: Pacejka94_Lateral_Params,
|
||||
|
||||
// Runtime state
|
||||
|
||||
@ -358,6 +360,8 @@ Suspension_Constraint_Config :: struct {
|
||||
damping: f32,
|
||||
radius: f32,
|
||||
mass: f32,
|
||||
pacejka_long: Pacejka94_Longitudinal_Params,
|
||||
pacejka_lat: Pacejka94_Lateral_Params,
|
||||
}
|
||||
|
||||
Drive_Axle_Config :: struct {
|
||||
@ -448,6 +452,8 @@ update_suspension_constraint_from_config :: proc(
|
||||
constraint.damping = config.damping
|
||||
constraint.radius = config.radius
|
||||
constraint.inv_inertia = 1.0 / (0.5 * config.mass * config.radius * config.radius)
|
||||
constraint.pacejka_long = config.pacejka_long
|
||||
constraint.pacejka_lat = config.pacejka_lat
|
||||
}
|
||||
|
||||
add_engine_curve :: proc(sim_state: ^Sim_State, curve: [][2]f32) -> Engine_Curve_Handle {
|
||||
|
@ -716,7 +716,7 @@ pgs_solve_suspension :: proc(
|
||||
dir,
|
||||
v.rest,
|
||||
)
|
||||
log.debugf("hit_t: %v, hit: %v", v.hit_t, v.hit)
|
||||
// log.debugf("hit_t: %v, hit: %v", v.hit_t, v.hit)
|
||||
v.hit_point = wheel_world_pos + dir * v.hit_t
|
||||
|
||||
forward := wheel_get_forward_vec(body, v)
|
||||
@ -791,9 +791,9 @@ pgs_solve_suspension :: proc(
|
||||
slip_ratio :=
|
||||
ground_vel == 0 ? 0 : clamp(wheel_spin_vel / ground_vel - 1, -1, 1) * 100.0
|
||||
slip_angle :=
|
||||
lg.angle_between(forward, body_vel_at_contact_patch) * math.DEG_PER_RAD
|
||||
-lg.angle_between(forward, body_vel_at_contact_patch) * math.DEG_PER_RAD
|
||||
|
||||
MAX_SLIP_LEN :: f32(2.0)
|
||||
MAX_SLIP_LEN :: f32(1.0)
|
||||
|
||||
slip_vec := Vec2 {
|
||||
slip_angle / PACEJKA94_LATERAL_PEAK_X / MAX_SLIP_LEN,
|
||||
@ -809,7 +809,7 @@ pgs_solve_suspension :: proc(
|
||||
long_friction :=
|
||||
abs(
|
||||
pacejka_94_longitudinal(
|
||||
PACEJKA94_LONGITUDINAL_PARAMS,
|
||||
v.pacejka_long,
|
||||
slip_ratio,
|
||||
max(abs(v.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
),
|
||||
@ -818,7 +818,7 @@ pgs_solve_suspension :: proc(
|
||||
lat_friction :=
|
||||
abs(
|
||||
pacejka_94_lateral(
|
||||
PACEJKA94_LATERAL_PARAMS,
|
||||
v.pacejka_lat,
|
||||
slip_angle,
|
||||
max(abs(v.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
0.0,
|
||||
|
@ -29,10 +29,11 @@ main :: proc() {
|
||||
os.stderr = logh
|
||||
}
|
||||
|
||||
logger := logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
|
||||
logger :=
|
||||
logh_err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
|
||||
context.logger = logger
|
||||
|
||||
game.game_init_window()
|
||||
game.game_init_window(os.args)
|
||||
game.game_init()
|
||||
|
||||
window_open := true
|
||||
@ -74,3 +75,4 @@ NvOptimusEnablement: u32 = 1
|
||||
|
||||
@(export)
|
||||
AmdPowerXpressRequestHighPerformance: i32 = 1
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 59,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -11,7 +11,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 60,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -26,7 +26,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 61,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@ -35,7 +35,7 @@
|
||||
"text": [
|
||||
"<>:5: SyntaxWarning: invalid escape sequence '\\g'\n",
|
||||
"<>:5: SyntaxWarning: invalid escape sequence '\\g'\n",
|
||||
"/tmp/ipykernel_97985/2069913084.py:5: SyntaxWarning: invalid escape sequence '\\g'\n",
|
||||
"/tmp/ipykernel_100064/2069913084.py:5: SyntaxWarning: invalid escape sequence '\\g'\n",
|
||||
" camber_angle = Symbol('\\gamma')\n"
|
||||
]
|
||||
}
|
||||
@ -81,7 +81,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 62,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -106,12 +106,13 @@
|
||||
" b11 = 0.0,\n",
|
||||
" b12 = 0.0,\n",
|
||||
" b13 = 0.0,\n",
|
||||
" _Fz=float(1.0)\n",
|
||||
" ):\n",
|
||||
" params = [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13]\n",
|
||||
" result = pacejka94_longitudinal().subs({sym: val for sym, val in zip(b, params)})\n",
|
||||
" result = result.subs({Fz: 1})\n",
|
||||
" result = result.subs({Fz: _Fz})\n",
|
||||
"\n",
|
||||
" return plot(result, (x, 0, 100))\n",
|
||||
" return plot(result, (x, -100, 100))\n",
|
||||
"\n",
|
||||
"def pacejka_lateral_interactive(\n",
|
||||
" a0 = float(0.0),\n",
|
||||
@ -143,7 +144,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": 63,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -155,13 +156,13 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 64,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "ad99275593cc4cc0a0d9bbd2418231cf",
|
||||
"model_id": "dc81af6a67754c5d8127cc89eb70368b",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
@ -175,7 +176,7 @@
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "57e3277511e346aabc5d38e222bc8eee",
|
||||
"model_id": "e09f497a55324a8ba3e35f5624741bce",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
@ -190,7 +191,7 @@
|
||||
"source": [
|
||||
"long_params = [\n",
|
||||
" 1.5,\n",
|
||||
" 0.0,\n",
|
||||
" -1.0,\n",
|
||||
" 1100.0,\n",
|
||||
" 0.0,\n",
|
||||
" 300.0,\n",
|
||||
@ -208,7 +209,7 @@
|
||||
"w = interactive(pacejka_longitudinal_interactive, kwargs={f'b{i}': long_params[i] for i in range(len(long_params))})\n",
|
||||
"\n",
|
||||
"copy_long_btn = widgets.Button(description=\"Copy Params\")\n",
|
||||
"copy_long_btn.on_click(lambda _: copy_to_clipboard(',\\n'.join(str(p.value) for p in w.children[:-1])))\n",
|
||||
"copy_long_btn.on_click(lambda _: copy_to_clipboard(',\\n'.join(str(p.value) for p in w.children[:-2])))\n",
|
||||
"\n",
|
||||
"display(w)\n",
|
||||
"display(copy_long_btn)"
|
||||
@ -218,16 +219,23 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 65,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "ad99275593cc4cc0a0d9bbd2418231cf",
|
||||
"model_id": "60b3a140f56547828b45f3038c9c039c",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"interactive(children=(FloatSlider(value=1.5, description='b0', max=4.5, min=-1.5), FloatSlider(value=0.0, desc…"
|
||||
"interactive(children=(FloatSlider(value=0.0, description='a0', max=1.0), FloatSlider(value=0.0, description='a…"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
@ -236,7 +244,7 @@
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "3c5b82c12f154727a32aed50b1f8fc59",
|
||||
"model_id": "01b9a4caf90c43caadf998126c9f5c51",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
@ -272,11 +280,11 @@
|
||||
"\n",
|
||||
"widget_lat = interactive(pacejka_lateral_interactive, kwargs={f'a{i}': lat_params[i] for i in range(len(lat_params))})\n",
|
||||
"\n",
|
||||
"copy_long_btn = widgets.Button(description=\"Copy Params\")\n",
|
||||
"copy_long_btn.on_click(lambda _: copy_to_clipboard(',\\n'.join(str(p.value) for p in w.children[:-2])))\n",
|
||||
"copy_lat_btn = widgets.Button(description=\"Copy Params\")\n",
|
||||
"copy_lat_btn.on_click(lambda _: copy_to_clipboard(',\\n'.join(str(p.value) for p in widget_lat.children[:-2])))\n",
|
||||
"\n",
|
||||
"display(w)\n",
|
||||
"display(copy_long_btn)"
|
||||
"display(widget_lat)\n",
|
||||
"display(copy_lat_btn)"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user