Compare commits
3 Commits
89fa64ca9e
...
a262a590b2
| Author | SHA1 | Date | |
|---|---|---|---|
| a262a590b2 | |||
| f21fe89783 | |||
| 1e82871b81 |
151
src/ray.jai
151
src/ray.jai
@ -83,13 +83,16 @@ does_ray_hit_cube :: (og_ray: Ray, cube: Collision_Cube) -> Ray_Collision {
|
||||
collision.distance = t[6];
|
||||
collision.point = ray.origin + ray.direction * collision.distance;
|
||||
|
||||
collision.normal = lerp(bmin, bmax, 0.5);
|
||||
collision.normal = collision.point - collision.normal;
|
||||
|
||||
collision.normal.x = cast(float) cast(s64) collision.normal.x;
|
||||
collision.normal.y = cast(float) cast(s64) collision.normal.y;
|
||||
collision.normal.z = cast(float) cast(s64) collision.normal.z;
|
||||
collision.normal = normalize(collision.normal);
|
||||
tx := min(t[0], t[1]);
|
||||
ty := min(t[2], t[3]);
|
||||
tz := min(t[4], t[5]);
|
||||
if tx >= ty && tx >= tz {
|
||||
collision.normal = .{ifx t[0] < t[1] then -1.0 else 1.0, 0, 0};
|
||||
} else if ty >= tx && ty >= tz {
|
||||
collision.normal = .{0, ifx t[2] < t[3] then -1.0 else 1.0, 0};
|
||||
} else {
|
||||
collision.normal = .{0, 0, ifx t[4] < t[5] then -1.0 else 1.0};
|
||||
}
|
||||
|
||||
if insideBox {
|
||||
ray.direction = -1.0 * ray.direction;
|
||||
@ -180,3 +183,137 @@ unproject :: (source: Vector3, projection: Matrix4, view: Matrix4) -> Vector3 {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if FLAG_TEST_ENGINE {
|
||||
eps :: 0.001;
|
||||
approx :: (a: float, b: float) -> bool { return abs(a - b) < eps; }
|
||||
approx3 :: (a: Vector3, b: Vector3) -> bool {
|
||||
return abs(a.x - b.x) < eps && abs(a.y - b.y) < eps && abs(a.z - b.z) < eps;
|
||||
}
|
||||
|
||||
test_ray_cube_hit :: () {
|
||||
s := begin_suite("ray cube hit");
|
||||
ray := Ray.{ origin = .{0, 0, -5}, direction = .{0, 0, 1} };
|
||||
cube := Collision_Cube.{ position = .{-1, -1, -1}, size = .{2, 2, 2} };
|
||||
col := does_ray_hit_cube(ray, cube);
|
||||
check(*s, "ray along +Z hits cube", col.hit);
|
||||
check(*s, "distance to front face is 4.0", approx(col.distance, 4.0));
|
||||
check(*s, "hit point lands on front face", approx3(col.point, .{0, 0, -1}));
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
test_ray_cube_miss :: () {
|
||||
s := begin_suite("ray cube miss");
|
||||
{
|
||||
ray := Ray.{ origin = .{3, 0, -5}, direction = .{0, 0, 1} };
|
||||
cube := Collision_Cube.{ position = .{-1, -1, -1}, size = .{2, 2, 2} };
|
||||
col := does_ray_hit_cube(ray, cube);
|
||||
check(*s, "ray offset in X misses cube", !col.hit);
|
||||
}
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 0, 5}, direction = .{0, 0, 1} };
|
||||
cube := Collision_Cube.{ position = .{-1, -1, -1}, size = .{2, 2, 2} };
|
||||
col := does_ray_hit_cube(ray, cube);
|
||||
check(*s, "ray pointing away misses cube", !col.hit);
|
||||
}
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
// Face normals using size-2 cubes centered at origin.
|
||||
// NOTE: the current integer-truncation normal computation breaks for cubes
|
||||
// smaller than size 2 (e.g. trixel-sized cubes). Fix does_ray_hit_cube to
|
||||
// use the entry-face t-value comparison instead of truncating to s64.
|
||||
test_ray_cube_normals :: () {
|
||||
s := begin_suite("ray cube face normals");
|
||||
cube := Collision_Cube.{ position = .{-1, -1, -1}, size = .{2, 2, 2} };
|
||||
|
||||
col_neg_z := does_ray_hit_cube(Ray.{ origin = .{0, 0, -5}, direction = .{0, 0, 1} }, cube);
|
||||
col_pos_z := does_ray_hit_cube(Ray.{ origin = .{0, 0, 5}, direction = .{0, 0, -1} }, cube);
|
||||
col_pos_y := does_ray_hit_cube(Ray.{ origin = .{0, 5, 0}, direction = .{0, -1, 0} }, cube);
|
||||
col_neg_y := does_ray_hit_cube(Ray.{ origin = .{0,-5, 0}, direction = .{0, 1, 0} }, cube);
|
||||
col_pos_x := does_ray_hit_cube(Ray.{ origin = .{ 5, 0, 0}, direction = .{-1, 0, 0} }, cube);
|
||||
col_neg_x := does_ray_hit_cube(Ray.{ origin = .{-5, 0, 0}, direction = .{ 1, 0, 0} }, cube);
|
||||
|
||||
check(*s, "-Z face normal is ( 0, 0,-1)", col_neg_z.hit && approx3(col_neg_z.normal, .{ 0, 0, -1}));
|
||||
check(*s, "+Z face normal is ( 0, 0, 1)", col_pos_z.hit && approx3(col_pos_z.normal, .{ 0, 0, 1}));
|
||||
check(*s, "+Y face normal is ( 0, 1, 0)", col_pos_y.hit && approx3(col_pos_y.normal, .{ 0, 1, 0}));
|
||||
check(*s, "-Y face normal is ( 0,-1, 0)", col_neg_y.hit && approx3(col_neg_y.normal, .{ 0, -1, 0}));
|
||||
check(*s, "+X face normal is ( 1, 0, 0)", col_pos_x.hit && approx3(col_pos_x.normal, .{ 1, 0, 0}));
|
||||
check(*s, "-X face normal is (-1, 0, 0)", col_neg_x.hit && approx3(col_neg_x.normal, .{-1, 0, 0}));
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
// ray_plane_collision_point returns Vector2.{world_x, world_z}
|
||||
test_ray_plane :: () {
|
||||
s := begin_suite("ray plane collision");
|
||||
{
|
||||
ray := Ray.{ origin = .{3, 5, 2}, direction = .{0, -1, 0} };
|
||||
hit, point := ray_plane_collision_point(ray, 0.0);
|
||||
check(*s, "downward ray hits horizontal plane", hit);
|
||||
check(*s, "hit world-X matches ray origin X", approx(point.x, 3.0));
|
||||
check(*s, "hit world-Z matches ray origin Z", approx(point.y, 2.0));
|
||||
}
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 0, 0}, direction = .{0, 1, 0} };
|
||||
hit, point := ray_plane_collision_point(ray, 10.0);
|
||||
check(*s, "upward ray hits plane above", hit);
|
||||
check(*s, "hit world-X is 0", approx(point.x, 0.0));
|
||||
check(*s, "hit world-Z is 0", approx(point.y, 0.0));
|
||||
}
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 5, 0}, direction = .{1, 0, 0} };
|
||||
hit, _ := ray_plane_collision_point(ray, 0.0);
|
||||
check(*s, "ray parallel to plane misses", !hit);
|
||||
}
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 5, 0}, direction = .{0, 1, 0} };
|
||||
hit, _ := ray_plane_collision_point(ray, 0.0);
|
||||
check(*s, "ray pointing away misses", !hit);
|
||||
}
|
||||
// acceptable_radius is XZ distance from ray origin to hit point — needs a diagonal ray
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 5, 0}, direction = normalize(Vector3.{0.1, -1, 0}) };
|
||||
hit, _ := ray_plane_collision_point(ray, 0.0, acceptable_radius = 10.0);
|
||||
check(*s, "hit within acceptable_radius succeeds", hit);
|
||||
}
|
||||
{
|
||||
ray := Ray.{ origin = .{0, 1, 0}, direction = normalize(Vector3.{100, -1, 0}) };
|
||||
hit, _ := ray_plane_collision_point(ray, 0.0, acceptable_radius = 1.0);
|
||||
check(*s, "hit outside acceptable_radius fails", !hit);
|
||||
}
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
test_ray_trixel_normals :: () {
|
||||
s := begin_suite("ray trixel-sized cube face normals");
|
||||
TS : float : 1.0/16.0; // TRIXEL_SIZE
|
||||
// Trixel at grid position (4,7,3): position = (4*TS, 7*TS, 3*TS)
|
||||
cube := Collision_Cube.{ position = .{4*TS, 7*TS, 3*TS}, size = .{TS, TS, TS} };
|
||||
cx := 4*TS + TS*0.5;
|
||||
cy := 7*TS + TS*0.5;
|
||||
cz := 3*TS + TS*0.5;
|
||||
|
||||
col_neg_z := does_ray_hit_cube(Ray.{ origin = .{cx, cy, cz - 1}, direction = .{0, 0, 1} }, cube);
|
||||
col_pos_z := does_ray_hit_cube(Ray.{ origin = .{cx, cy, cz + 1}, direction = .{0, 0, -1} }, cube);
|
||||
col_pos_y := does_ray_hit_cube(Ray.{ origin = .{cx, cy + 1, cz}, direction = .{0, -1, 0} }, cube);
|
||||
col_neg_y := does_ray_hit_cube(Ray.{ origin = .{cx, cy - 1, cz}, direction = .{0, 1, 0} }, cube);
|
||||
col_pos_x := does_ray_hit_cube(Ray.{ origin = .{cx + 1, cy, cz}, direction = .{-1, 0, 0} }, cube);
|
||||
col_neg_x := does_ray_hit_cube(Ray.{ origin = .{cx - 1, cy, cz}, direction = .{ 1, 0, 0} }, cube);
|
||||
|
||||
check(*s, "-Z face normal is ( 0, 0,-1)", col_neg_z.hit && approx3(col_neg_z.normal, .{ 0, 0, -1}));
|
||||
check(*s, "+Z face normal is ( 0, 0, 1)", col_pos_z.hit && approx3(col_pos_z.normal, .{ 0, 0, 1}));
|
||||
check(*s, "+Y face normal is ( 0, 1, 0)", col_pos_y.hit && approx3(col_pos_y.normal, .{ 0, 1, 0}));
|
||||
check(*s, "-Y face normal is ( 0,-1, 0)", col_neg_y.hit && approx3(col_neg_y.normal, .{ 0, -1, 0}));
|
||||
check(*s, "+X face normal is ( 1, 0, 0)", col_pos_x.hit && approx3(col_pos_x.normal, .{ 1, 0, 0}));
|
||||
check(*s, "-X face normal is (-1, 0, 0)", col_neg_x.hit && approx3(col_neg_x.normal, .{-1, 0, 0}));
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
#run {
|
||||
test_ray_cube_hit();
|
||||
test_ray_cube_miss();
|
||||
test_ray_cube_normals();
|
||||
test_ray_trixel_normals();
|
||||
test_ray_plane();
|
||||
}
|
||||
}
|
||||
|
||||
@ -554,7 +554,10 @@ void main() {
|
||||
indirectSpec = mix(indirectSpec, vec3(specLum), metallic);
|
||||
|
||||
vec2 envBRDF = texture(sampler2D(brdf_lut, rdmsmp), vec2(max(dot(N, V), 0.0), roughness)).rg;
|
||||
light += indirectSpec * (Frough * envBRDF.x + envBRDF.y) * rdm_spec_scale;
|
||||
float NdotV_s = max(dot(N, V), 0.0);
|
||||
float roughnessBell = 1.0 - 0.7 * sin(roughness * PI);
|
||||
float grazingSuppress = 1.0 - 0.9 * roughness * sin(roughness * PI) * pow(1.0 - NdotV_s, 2.0);
|
||||
light += indirectSpec * (Frough * envBRDF.x + envBRDF.y) * rdm_spec_scale * roughnessBell * grazingSuppress;
|
||||
|
||||
// Indirect diffuse (interpolated from neighbor probes)
|
||||
vec3 indirectDiff = sample_rdm_diff(N, vpos - hemispherePos, local) * rdm_tint;
|
||||
|
||||
@ -1,4 +1,2 @@
|
||||
#run {
|
||||
// print("1st test!!!\n");
|
||||
// main();
|
||||
}
|
||||
#load "utils.jai";
|
||||
#load "world_test.jai";
|
||||
|
||||
30
src/tests/utils.jai
Normal file
30
src/tests/utils.jai
Normal file
@ -0,0 +1,30 @@
|
||||
Test_Suite :: struct {
|
||||
name : string;
|
||||
passed : int;
|
||||
failed : int;
|
||||
}
|
||||
|
||||
begin_suite :: (name: string) -> Test_Suite {
|
||||
print("\n== % ==\n", name);
|
||||
return .{ name = name };
|
||||
}
|
||||
|
||||
check :: (suite: *Test_Suite, name: string, condition: bool) {
|
||||
if condition {
|
||||
suite.passed += 1;
|
||||
print(" [PASS] %\n", name);
|
||||
} else {
|
||||
suite.failed += 1;
|
||||
print(" [FAIL] %\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
end_suite :: (suite: Test_Suite) {
|
||||
total := suite.passed + suite.failed;
|
||||
if suite.failed == 0 {
|
||||
print(" => All % tests passed.\n", total);
|
||||
} else {
|
||||
print(" => %/% passed, % FAILED.\n", suite.passed, total, suite.failed);
|
||||
assert(false, "Test suite '%' had % failure(s).", suite.name, suite.failed);
|
||||
}
|
||||
}
|
||||
65
src/tests/world_test.jai
Normal file
65
src/tests/world_test.jai
Normal file
@ -0,0 +1,65 @@
|
||||
test_floor_div_mod :: () {
|
||||
s := begin_suite("floor_div / floor_mod");
|
||||
|
||||
check(*s, "floor_div( 0, 32) == 0", floor_div( 0, 32) == 0);
|
||||
check(*s, "floor_div( 1, 32) == 0", floor_div( 1, 32) == 0);
|
||||
check(*s, "floor_div(31, 32) == 0", floor_div(31, 32) == 0);
|
||||
check(*s, "floor_div(32, 32) == 1", floor_div(32, 32) == 1);
|
||||
check(*s, "floor_div(33, 32) == 1", floor_div(33, 32) == 1);
|
||||
check(*s, "floor_div(-1, 32) == -1", floor_div(-1, 32) == -1);
|
||||
check(*s, "floor_div(-32, 32) == -1", floor_div(-32, 32) == -1);
|
||||
check(*s, "floor_div(-33, 32) == -2", floor_div(-33, 32) == -2);
|
||||
|
||||
check(*s, "floor_mod( 0, 32) == 0", floor_mod( 0, 32) == 0);
|
||||
check(*s, "floor_mod( 1, 32) == 1", floor_mod( 1, 32) == 1);
|
||||
check(*s, "floor_mod(31, 32) == 31", floor_mod(31, 32) == 31);
|
||||
check(*s, "floor_mod(32, 32) == 0", floor_mod(32, 32) == 0);
|
||||
check(*s, "floor_mod(33, 32) == 1", floor_mod(33, 32) == 1);
|
||||
check(*s, "floor_mod(-1, 32) == 31", floor_mod(-1, 32) == 31);
|
||||
check(*s, "floor_mod(-32, 32) == 0", floor_mod(-32, 32) == 0);
|
||||
check(*s, "floor_mod(-33, 32) == 31", floor_mod(-33, 32) == 31);
|
||||
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
test_coord_roundtrip :: () {
|
||||
s := begin_suite("world coord round-trip");
|
||||
|
||||
roundtrip_check :: (suite: *Test_Suite, wx: s32, wy: s32, wz: s32) {
|
||||
ck := world_to_chunk_coord(wx, wy, wz);
|
||||
lx, ly, lz := world_to_local(wx, wy, wz);
|
||||
rx, ry, rz := chunk_local_to_world(ck, lx, ly, lz);
|
||||
check(suite, tprint("(%, %, %) round-trips", wx, wy, wz), rx == wx && ry == wy && rz == wz);
|
||||
}
|
||||
|
||||
roundtrip_check(*s, 0, 0, 0);
|
||||
roundtrip_check(*s, 1, 1, 1);
|
||||
roundtrip_check(*s, 31, 31, 31);
|
||||
roundtrip_check(*s, 32, 32, 32);
|
||||
roundtrip_check(*s, 33, 33, 33);
|
||||
roundtrip_check(*s, -1, -1, -1);
|
||||
roundtrip_check(*s, -32, -32, -32);
|
||||
roundtrip_check(*s, -33, -33, -33);
|
||||
roundtrip_check(*s, 63, -1, 32);
|
||||
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
test_chunk_coord_values :: () {
|
||||
s := begin_suite("world_to_chunk_coord values");
|
||||
|
||||
check(*s, "( 0, 0, 0) -> chunk ( 0, 0, 0)", world_to_chunk_coord( 0, 0, 0) == .{ 0, 0, 0});
|
||||
check(*s, "(31, 0, 0) -> chunk ( 0, 0, 0)", world_to_chunk_coord(31, 0, 0) == .{ 0, 0, 0});
|
||||
check(*s, "(32, 0, 0) -> chunk ( 1, 0, 0)", world_to_chunk_coord(32, 0, 0) == .{ 1, 0, 0});
|
||||
check(*s, "(-1, 0, 0) -> chunk (-1, 0, 0)", world_to_chunk_coord(-1, 0, 0) == .{-1, 0, 0});
|
||||
check(*s, "(-32,0, 0) -> chunk (-1, 0, 0)", world_to_chunk_coord(-32, 0, 0) == .{-1, 0, 0});
|
||||
check(*s, "(-33,0, 0) -> chunk (-2, 0, 0)", world_to_chunk_coord(-33, 0, 0) == .{-2, 0, 0});
|
||||
|
||||
end_suite(s);
|
||||
}
|
||||
|
||||
#run {
|
||||
test_floor_div_mod();
|
||||
test_coord_roundtrip();
|
||||
test_chunk_coord_values();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user