Level hot reload working
This commit is contained in:
parent
bb6498a193
commit
8b8e4f9513
BIN
assets/blender/test_level_blend/NurbsPath.glb
(Stored with Git LFS)
BIN
assets/blender/test_level_blend/NurbsPath.glb
(Stored with Git LFS)
Binary file not shown.
BIN
assets/blender/test_level_blend/Plane.glb
(Stored with Git LFS)
BIN
assets/blender/test_level_blend/Plane.glb
(Stored with Git LFS)
Binary file not shown.
@ -1,45 +1,45 @@
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Bakery.glb"
|
||||
:pos (0.000000 0.000000 12.000000)
|
||||
:pos (1.000000 0.000000 -13.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/test_level_blend/Plane.glb"
|
||||
:pos (0.000000 -1.000000 0.000000)
|
||||
:pos (0.000000 -1.000000 -0.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (22.469545 22.469545 22.469545))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Fire_Station.glb"
|
||||
:pos (9.000000 0.000000 13.000000)
|
||||
:pos (10.500000 0.000000 -22.500000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-8.000000 0.000000 11.000000)
|
||||
:pos (-7.000000 0.000000 -12.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-16.000000 0.000000 11.000000)
|
||||
:pos (-16.000000 0.000000 -11.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-15.559284 1.609015 0.259853)
|
||||
:pos (-20.000000 -1.000000 -1.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Green_House.glb"
|
||||
:pos (14.000000 0.000000 -7.000000)
|
||||
:pos (14.000000 0.000000 7.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Hotel.glb"
|
||||
:pos (8.000000 0.000000 -18.000000)
|
||||
:pos (8.000000 0.000000 18.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/test_level_blend/NurbsPath.glb"
|
||||
:pos (0.000000 1.000000 0.000000)
|
||||
:pos (-4.000000 0.000000 12.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
|
BIN
assets/blender/testblend_blend/Bakery.glb
(Stored with Git LFS)
BIN
assets/blender/testblend_blend/Bakery.glb
(Stored with Git LFS)
Binary file not shown.
BIN
assets/blender/testblend_blend/Fire_Station.glb
(Stored with Git LFS)
BIN
assets/blender/testblend_blend/Fire_Station.glb
(Stored with Git LFS)
Binary file not shown.
BIN
assets/blender/testblend_blend/Gas_Station_Shop.glb
(Stored with Git LFS)
BIN
assets/blender/testblend_blend/Gas_Station_Shop.glb
(Stored with Git LFS)
Binary file not shown.
BIN
assets/blender/testblend_blend/Green_House.glb
(Stored with Git LFS)
BIN
assets/blender/testblend_blend/Green_House.glb
(Stored with Git LFS)
Binary file not shown.
BIN
assets/blender/testblend_blend/Hotel.glb
(Stored with Git LFS)
BIN
assets/blender/testblend_blend/Hotel.glb
(Stored with Git LFS)
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
import bpy
|
||||
import os
|
||||
import pathlib
|
||||
import contextlib
|
||||
|
||||
# Ensure blend file is saved
|
||||
blend_filepath = bpy.data.filepath
|
||||
@ -9,6 +10,26 @@ if not blend_filepath:
|
||||
|
||||
assert bpy.context.scene is not None
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temp_scene(name: str):
|
||||
original_scene = bpy.context.scene
|
||||
assert original_scene is not None
|
||||
temp_scene = bpy.data.scenes.new(name)
|
||||
assert bpy.context.window is not None
|
||||
|
||||
try:
|
||||
bpy.context.window.scene = temp_scene
|
||||
yield temp_scene
|
||||
finally:
|
||||
assert bpy.context.window is not None
|
||||
bpy.context.window.scene = original_scene
|
||||
print(f'removing scene {temp_scene}')
|
||||
bpy.data.scenes.remove(temp_scene)
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
export_scene = bpy.data.scenes.new('__export')
|
||||
|
||||
def clean_blend_path(blend_path: str) -> str:
|
||||
return os.path.join(os.path.dirname(blend_path), bpy.path.clean_name(os.path.basename(blend_path)))
|
||||
|
||||
@ -39,13 +60,18 @@ def export_object_as_glb(obj: bpy.types.Object, export_path: str):
|
||||
full_export_path = os.path.join(assets_path, export_path)
|
||||
pathlib.Path(os.path.dirname(full_export_path)).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if obj.instance_type == 'COLLECTION' and obj.instance_collection:
|
||||
with temp_scene('__export') as scn:
|
||||
scn.collection.children.link(obj.instance_collection)
|
||||
bpy.ops.export_scene.gltf(filepath=full_export_path, use_active_scene=True, export_apply=True)
|
||||
else:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
|
||||
assert bpy.context.view_layer is not None
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.export_scene.gltf(filepath=full_export_path, use_selection=True, export_apply=True)
|
||||
|
||||
print(f"Exported {obj.name} -> {export_path}")
|
||||
|
||||
# Collect all visible, non-hidden objects in the scene
|
||||
@ -70,7 +96,7 @@ for obj in visible_objects:
|
||||
instance_sexpr = (
|
||||
'(inst'
|
||||
f'\n\t:model "assets/{model_path}"'
|
||||
f'\n\t:pos ({loc.x:.6f} {loc.z:.6f} {loc.y:.6f})'
|
||||
f'\n\t:pos ({loc.x:.6f} {loc.z:.6f} {-loc.y:.6f})'
|
||||
f'\n\t:rot ({rot.x:.6f} {rot.y:.6f} {rot.z:.6f} {rot.w:.6f})'
|
||||
f'\n\t:scale ({scale.x:.6f} {scale.y:.6f} {scale.z:.6f}))'
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ package name
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:sync"
|
||||
import "libs:tracy"
|
||||
|
||||
// When enabled name globals will be initialized automatically
|
||||
NAME_STATIC_INIT :: #config(NAME_STATIC_INIT, true)
|
||||
@ -61,23 +62,36 @@ when NAME_STATIC_INIT {
|
||||
}
|
||||
|
||||
from_string :: proc(str: string) -> Name {
|
||||
sync.atomic_rw_mutex_guard(&global_container.lock)
|
||||
existing, ok := global_container.names_lookup[str]
|
||||
tracy.Zone()
|
||||
|
||||
existing: Name
|
||||
ok: bool
|
||||
{
|
||||
sync.atomic_rw_mutex_shared_guard(&global_container.lock)
|
||||
existing, ok = global_container.names_lookup[str]
|
||||
}
|
||||
if ok {
|
||||
return existing
|
||||
} else {
|
||||
new_str := strings.clone(
|
||||
sync.atomic_rw_mutex_guard(&global_container.lock)
|
||||
|
||||
new_str := strings.clone_to_cstring(
|
||||
str,
|
||||
mem.dynamic_arena_allocator(&global_container.names_allocator),
|
||||
)
|
||||
idx := u32(len(global_container.names_array))
|
||||
append(&global_container.names_array, new_str)
|
||||
append(&global_container.names_array, string(new_str))
|
||||
global_container.names_lookup[str] = Name(idx)
|
||||
return Name(idx)
|
||||
}
|
||||
}
|
||||
|
||||
from_cstring :: proc(str: cstring) -> Name {
|
||||
return from_string(string(str))
|
||||
}
|
||||
|
||||
to_string :: proc(name: Name) -> string {
|
||||
tracy.Zone()
|
||||
sync.atomic_rw_mutex_shared_guard(&global_container.lock)
|
||||
return global_container.names_array[name]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,13 @@ import "core:bytes"
|
||||
import "core:encoding/csv"
|
||||
import "core:io"
|
||||
import "core:log"
|
||||
import lg "core:math/linalg"
|
||||
import "core:strconv"
|
||||
import "game:debug"
|
||||
import "game:halfedge"
|
||||
import "game:physics/collision"
|
||||
import rl "libs:raylib"
|
||||
import rlgl "libs:raylib/rlgl"
|
||||
|
||||
CSV_Parse_Error :: enum {
|
||||
Ok,
|
||||
@ -69,7 +75,7 @@ parse_csv_2d :: proc(
|
||||
data: []byte,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
curve: Loaded_Curve_2D,
|
||||
curve: Curve_2D,
|
||||
error: CSV_Parse_Error,
|
||||
) {
|
||||
bytes_reader: bytes.Reader
|
||||
@ -125,8 +131,400 @@ parse_csv_2d :: proc(
|
||||
append(&tmp_result, [2]f32{f32(key), f32(val)})
|
||||
}
|
||||
|
||||
curve.points = make([][2]f32, len(tmp_result), allocator)
|
||||
copy(curve.points, tmp_result[:])
|
||||
curve = make([][2]f32, len(tmp_result), allocator)
|
||||
copy(curve, tmp_result[:])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
parse_convex :: proc(bytes: []byte, allocator := context.allocator) -> (Loaded_Convex, bool) {
|
||||
Parse_Ctx :: struct {
|
||||
bytes: []byte,
|
||||
it: int,
|
||||
line: int,
|
||||
}
|
||||
|
||||
advance :: proc(ctx: ^Parse_Ctx, by: int = 1) -> bool {
|
||||
ctx.it = min(ctx.it + by, len(ctx.bytes) + 1)
|
||||
return ctx.it < len(ctx.bytes)
|
||||
}
|
||||
|
||||
is_whitespace :: proc(b: byte) -> bool {
|
||||
return b == ' ' || b == '\t' || b == '\r' || b == '\n'
|
||||
}
|
||||
|
||||
skip_line :: proc(ctx: ^Parse_Ctx) {
|
||||
for ctx.it < len(ctx.bytes) && ctx.bytes[ctx.it] != '\n' {
|
||||
advance(ctx) or_break
|
||||
}
|
||||
advance(ctx)
|
||||
ctx.line += 1
|
||||
}
|
||||
|
||||
skip_whitespase :: proc(ctx: ^Parse_Ctx) {
|
||||
switch ctx.bytes[ctx.it] {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
if ctx.bytes[ctx.it] == '\n' {
|
||||
ctx.line += 1
|
||||
}
|
||||
advance(ctx) or_break
|
||||
case '#':
|
||||
skip_line(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
Edge :: [2]u16
|
||||
edges_map := make_map(map[Edge]halfedge.Edge_Index, context.temp_allocator)
|
||||
|
||||
edges := make_dynamic_array([dynamic]halfedge.Half_Edge, context.temp_allocator)
|
||||
vertices := make_dynamic_array([dynamic]halfedge.Vertex, context.temp_allocator)
|
||||
faces := make_dynamic_array([dynamic]halfedge.Face, context.temp_allocator)
|
||||
min_pos, max_pos: rl.Vector3 = max(f32), min(f32)
|
||||
|
||||
// Parse obj file directly into halfedge data structure
|
||||
{
|
||||
ctx := Parse_Ctx {
|
||||
bytes = bytes,
|
||||
line = 1,
|
||||
}
|
||||
|
||||
for ctx.it < len(ctx.bytes) {
|
||||
skip_whitespase(&ctx)
|
||||
switch ctx.bytes[ctx.it] {
|
||||
case 'v':
|
||||
// vertex
|
||||
advance(&ctx) or_break
|
||||
|
||||
vertex: rl.Vector3
|
||||
|
||||
coord_idx := 0
|
||||
for ctx.bytes[ctx.it] != '\n' && ctx.bytes[ctx.it] != '\r' {
|
||||
skip_whitespase(&ctx)
|
||||
s := string(ctx.bytes[ctx.it:])
|
||||
coord_val, nr, ok := strconv.parse_f32_prefix(s)
|
||||
if !ok {
|
||||
log.errorf(
|
||||
"failed to parse float %v %s at line %d",
|
||||
coord_idx,
|
||||
ctx.bytes[ctx.it:][:12],
|
||||
ctx.line,
|
||||
)
|
||||
return {}, false
|
||||
}
|
||||
advance(&ctx, nr) or_break
|
||||
|
||||
vertex[coord_idx] = coord_val
|
||||
coord_idx += 1
|
||||
}
|
||||
append(&vertices, halfedge.Vertex{pos = vertex, edge = -1})
|
||||
min_pos = lg.min(vertex, min_pos)
|
||||
max_pos = lg.max(vertex, max_pos)
|
||||
|
||||
if ctx.bytes[ctx.it] == '\r' {
|
||||
advance(&ctx)
|
||||
}
|
||||
advance(&ctx)
|
||||
ctx.line += 1
|
||||
case 'f':
|
||||
advance(&ctx) or_break
|
||||
|
||||
MAX_FACE_VERTS :: 10
|
||||
|
||||
indices_buf: [MAX_FACE_VERTS]u16
|
||||
index_count := 0
|
||||
|
||||
for ctx.bytes[ctx.it] != '\n' && ctx.bytes[ctx.it] != '\r' {
|
||||
skip_whitespase(&ctx)
|
||||
index_f, nr, ok := strconv.parse_f32_prefix(string(ctx.bytes[ctx.it:]))
|
||||
if !ok {
|
||||
log.errorf("failed to parse index at line %d", ctx.line)
|
||||
return {}, false
|
||||
}
|
||||
advance(&ctx, nr) or_break
|
||||
index := u16(index_f) - 1
|
||||
indices_buf[index_count] = u16(index)
|
||||
index_count += 1
|
||||
}
|
||||
if ctx.bytes[ctx.it] == '\r' {
|
||||
advance(&ctx)
|
||||
}
|
||||
advance(&ctx)
|
||||
ctx.line += 1
|
||||
|
||||
assert(index_count >= 3)
|
||||
indices := indices_buf[:index_count]
|
||||
|
||||
append(&faces, halfedge.Face{})
|
||||
face_idx := len(faces) - 1
|
||||
face := &faces[face_idx]
|
||||
|
||||
first_edge_idx := len(edges)
|
||||
|
||||
face.edge = halfedge.Edge_Index(first_edge_idx)
|
||||
|
||||
plane: collision.Plane
|
||||
{
|
||||
i1, i2, i3 := indices[0], indices[1], indices[2]
|
||||
v1, v2, v3 := vertices[i1].pos, vertices[i2].pos, vertices[i3].pos
|
||||
|
||||
plane = collision.plane_from_point_normal(
|
||||
v1,
|
||||
lg.normalize0(lg.cross(v2 - v1, v3 - v1)),
|
||||
)
|
||||
}
|
||||
face.normal = plane.normal
|
||||
|
||||
for index in indices[3:] {
|
||||
assert(
|
||||
abs(collision.signed_distance_plane(vertices[index].pos, plane)) < 0.01,
|
||||
"mesh has non planar faces",
|
||||
)
|
||||
}
|
||||
|
||||
first_vert_pos := vertices[indices[0]].pos
|
||||
|
||||
for i in 0 ..< len(indices) {
|
||||
edge_idx := halfedge.Edge_Index(first_edge_idx + i)
|
||||
prev_edge_relative := i == 0 ? len(indices) - 1 : i - 1
|
||||
next_edge_relative := (i + 1) % len(indices)
|
||||
i1, i2 := indices[i], indices[next_edge_relative]
|
||||
v1, v2 := &vertices[i1], &vertices[i2]
|
||||
|
||||
assert(
|
||||
lg.dot(
|
||||
lg.cross(v1.pos - first_vert_pos, v2.pos - first_vert_pos),
|
||||
plane.normal,
|
||||
) >=
|
||||
0,
|
||||
"non convex face or non ccw winding",
|
||||
)
|
||||
|
||||
if v1.edge == -1 {
|
||||
v1.edge = edge_idx
|
||||
}
|
||||
|
||||
edge := halfedge.Half_Edge {
|
||||
origin = halfedge.Vertex_Index(i1),
|
||||
face = halfedge.Face_Index(face_idx),
|
||||
twin = -1,
|
||||
next = halfedge.Edge_Index(first_edge_idx + next_edge_relative),
|
||||
prev = halfedge.Edge_Index(first_edge_idx + prev_edge_relative),
|
||||
}
|
||||
|
||||
stable_index := [2]u16{min(i1, i2), max(i1, i2)}
|
||||
if stable_index in edges_map {
|
||||
edge.twin = edges_map[stable_index]
|
||||
twin_edge := &edges[edge.twin]
|
||||
assert(twin_edge.twin == -1, "edge has more than two faces attached")
|
||||
twin_edge.twin = edge_idx
|
||||
} else {
|
||||
edges_map[stable_index] = edge_idx
|
||||
}
|
||||
|
||||
append(&edges, edge)
|
||||
}
|
||||
case:
|
||||
skip_line(&ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
center := (max_pos + min_pos) * 0.5
|
||||
extent := (max_pos - min_pos) * 0.5
|
||||
|
||||
center_of_mass: rl.Vector3
|
||||
|
||||
final_vertices := make([]halfedge.Vertex, len(vertices), allocator)
|
||||
final_edges := make([]halfedge.Half_Edge, len(edges), allocator)
|
||||
final_faces := make([]halfedge.Face, len(faces), allocator)
|
||||
copy(final_vertices, vertices[:])
|
||||
copy(final_edges, edges[:])
|
||||
copy(final_faces, faces[:])
|
||||
|
||||
mesh := halfedge.Half_Edge_Mesh {
|
||||
vertices = final_vertices,
|
||||
edges = final_edges,
|
||||
faces = final_faces,
|
||||
center = center,
|
||||
extent = extent,
|
||||
}
|
||||
|
||||
// Center of mass calculation
|
||||
total_volume := f32(0.0)
|
||||
{
|
||||
tri_idx := 0
|
||||
for face_idx in 0 ..< len(faces) {
|
||||
face := faces[face_idx]
|
||||
// for all triangles
|
||||
it := halfedge.iterator_face_edges(mesh, halfedge.Face_Index(face_idx))
|
||||
i := 0
|
||||
tri: [3]rl.Vector3
|
||||
for edge in halfedge.iterate_next_edge(&it) {
|
||||
switch i {
|
||||
case 0 ..< 3:
|
||||
tri[i] = mesh.vertices[edge.origin].pos
|
||||
case:
|
||||
tri[1] = tri[2]
|
||||
tri[2] = mesh.vertices[edge.origin].pos
|
||||
}
|
||||
|
||||
if i >= 2 {
|
||||
plane := collision.plane_from_point_normal(tri[0], -face.normal)
|
||||
|
||||
h := max(0, collision.signed_distance_plane(center, plane))
|
||||
tri_area :=
|
||||
lg.dot(lg.cross(tri[1] - tri[0], tri[2] - tri[0]), face.normal) * 0.5
|
||||
tetra_volume := 1.0 / 3.0 * tri_area * h
|
||||
total_volume += tetra_volume
|
||||
|
||||
tetra_centroid := (tri[0] + tri[1] + tri[2] + center) * 0.25
|
||||
center_of_mass += tetra_volume * tetra_centroid
|
||||
|
||||
tri_idx += 1
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(total_volume > 0, "degenerate convex hull")
|
||||
center_of_mass /= total_volume
|
||||
|
||||
inertia_tensor: lg.Matrix3f32
|
||||
// Find inertia tensor
|
||||
{
|
||||
tri_idx := 0
|
||||
for face_idx in 0 ..< len(faces) {
|
||||
// for all triangles
|
||||
it := halfedge.iterator_face_edges(mesh, halfedge.Face_Index(face_idx))
|
||||
i := 0
|
||||
tri: [3]rl.Vector3
|
||||
for edge in halfedge.iterate_next_edge(&it) {
|
||||
switch i {
|
||||
case 0 ..< 3:
|
||||
tri[i] = mesh.vertices[edge.origin].pos
|
||||
case:
|
||||
tri[1] = tri[2]
|
||||
tri[2] = mesh.vertices[edge.origin].pos
|
||||
}
|
||||
|
||||
if i >= 2 {
|
||||
tet := Tetrahedron {
|
||||
p = {tri[0], tri[1], tri[2], center_of_mass},
|
||||
}
|
||||
|
||||
inertia_tensor += tetrahedron_inertia_tensor(tet, center_of_mass)
|
||||
|
||||
tri_idx += 1
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
inertia_tensor = inertia_tensor
|
||||
|
||||
return Loaded_Convex {
|
||||
mesh = mesh,
|
||||
center_of_mass = center_of_mass,
|
||||
inertia_tensor = inertia_tensor,
|
||||
total_volume = total_volume,
|
||||
},
|
||||
true
|
||||
}
|
||||
|
||||
// TODO: move convex stuff out of assets.odin
|
||||
Tetrahedron :: struct {
|
||||
p: [4]rl.Vector3,
|
||||
}
|
||||
|
||||
tetrahedron_volume :: #force_inline proc(tet: Tetrahedron) -> f32 {
|
||||
return(
|
||||
1.0 /
|
||||
6.0 *
|
||||
abs(lg.dot(lg.cross(tet.p[1] - tet.p[0], tet.p[2] - tet.p[0]), tet.p[3] - tet.p[0])) \
|
||||
)
|
||||
}
|
||||
|
||||
square :: #force_inline proc(val: f32) -> f32 {
|
||||
return val * val
|
||||
}
|
||||
|
||||
tetrahedron_inertia_tensor :: proc(tet: Tetrahedron, o: rl.Vector3) -> lg.Matrix3f32 {
|
||||
p1, p2, p3, p4 := tet.p[0] - o, tet.p[1] - o, tet.p[2] - o, tet.p[3] - o
|
||||
// Jacobian determinant is 6*Volume
|
||||
det_j := abs(6.0 * tetrahedron_volume(tet))
|
||||
|
||||
moment_of_inertia_term :: proc(p1, p2, p3, p4: rl.Vector3, axis: int) -> f32 {
|
||||
return(
|
||||
square(p1[axis]) +
|
||||
p1[axis] * p2[axis] +
|
||||
square(p2[axis]) +
|
||||
p1[axis] * p3[axis] +
|
||||
p2[axis] * p3[axis] +
|
||||
square(p3[axis]) +
|
||||
p1[axis] * p4[axis] +
|
||||
p2[axis] * p4[axis] +
|
||||
p3[axis] * p4[axis] +
|
||||
square(p4[axis]) \
|
||||
)
|
||||
}
|
||||
|
||||
product_of_inertia_term :: proc(p1, p2, p3, p4: rl.Vector3, axis1, axis2: int) -> f32 {
|
||||
return(
|
||||
2.0 * p1[axis1] * p1[axis2] +
|
||||
p2[axis1] * p1[axis2] +
|
||||
p3[axis1] * p1[axis2] +
|
||||
p4[axis1] * p1[axis2] +
|
||||
p1[axis1] * p2[axis2] +
|
||||
2.0 * p2[axis1] * p2[axis2] +
|
||||
p3[axis1] * p2[axis2] +
|
||||
p4[axis1] * p2[axis2] +
|
||||
p1[axis1] * p3[axis2] +
|
||||
p2[axis1] * p3[axis2] +
|
||||
2.0 * p3[axis1] * p3[axis2] +
|
||||
p4[axis1] * p3[axis2] +
|
||||
p1[axis1] * p4[axis2] +
|
||||
p2[axis1] * p4[axis2] +
|
||||
p3[axis1] * p4[axis2] +
|
||||
2.0 * p4[axis1] * p4[axis2] \
|
||||
)
|
||||
}
|
||||
|
||||
MOMENT_OF_INERTIA_DENOM :: 1.0 / 60.0
|
||||
PRODUCT_OF_INERTIA_DENOM :: 1.0 / 120.0
|
||||
|
||||
x_term := moment_of_inertia_term(p1, p2, p3, p4, 0)
|
||||
y_term := moment_of_inertia_term(p1, p2, p3, p4, 1)
|
||||
z_term := moment_of_inertia_term(p1, p2, p3, p4, 2)
|
||||
|
||||
// Moments of intertia with respect to XYZ
|
||||
// Integral(y^2 + z^2)
|
||||
a := det_j * (y_term + z_term) * MOMENT_OF_INERTIA_DENOM
|
||||
// Integral(x^2 + z^2)
|
||||
b := det_j * (x_term + z_term) * MOMENT_OF_INERTIA_DENOM
|
||||
// Integral(x^2 + y^2)
|
||||
c := det_j * (x_term + y_term) * MOMENT_OF_INERTIA_DENOM
|
||||
|
||||
// Products of inertia
|
||||
a_ := product_of_inertia_term(p1, p2, p3, p4, axis1 = 1, axis2 = 2) * PRODUCT_OF_INERTIA_DENOM
|
||||
b_ := product_of_inertia_term(p1, p2, p3, p4, axis1 = 0, axis2 = 2) * PRODUCT_OF_INERTIA_DENOM
|
||||
c_ := product_of_inertia_term(p1, p2, p3, p4, axis1 = 0, axis2 = 1) * PRODUCT_OF_INERTIA_DENOM
|
||||
|
||||
return {a, -b_, -c_, -b_, b, -a_, -c_, -a_, c}
|
||||
}
|
||||
|
||||
debug_draw_tetrahedron_wires :: proc(tri: [3]rl.Vector3, p: rl.Vector3, color: rl.Color) {
|
||||
rlgl.Begin(rlgl.LINES)
|
||||
defer rlgl.End()
|
||||
|
||||
debug.rlgl_color(color)
|
||||
|
||||
debug.rlgl_vertex3v2(tri[0], tri[1])
|
||||
debug.rlgl_vertex3v2(tri[1], tri[2])
|
||||
debug.rlgl_vertex3v2(tri[2], tri[0])
|
||||
debug.rlgl_vertex3v2(tri[0], p)
|
||||
debug.rlgl_vertex3v2(tri[1], p)
|
||||
debug.rlgl_vertex3v2(tri[2], p)
|
||||
}
|
||||
|
145
game/assets/watcher.odin
Normal file
145
game/assets/watcher.odin
Normal file
@ -0,0 +1,145 @@
|
||||
package assets
|
||||
|
||||
import "base:runtime"
|
||||
import "common:name"
|
||||
import "core:log"
|
||||
import "core:sync/chan"
|
||||
import "core:thread"
|
||||
import "libs:physfs"
|
||||
|
||||
ASSET_WATCHER_OPS_BUFFER :: 256
|
||||
|
||||
// Add asset to watch list
|
||||
Asset_Watcher_Op_Add :: struct {
|
||||
type: Asset_Type,
|
||||
path: name.Name,
|
||||
modtime: physfs.sint64,
|
||||
}
|
||||
// Remove asset from watch list
|
||||
Asset_Watcher_Op_Remove :: struct {
|
||||
type: Asset_Type,
|
||||
path: name.Name,
|
||||
}
|
||||
|
||||
Asset_Watcher_Op :: union #no_nil {
|
||||
Asset_Watcher_Op_Add,
|
||||
Asset_Watcher_Op_Remove,
|
||||
}
|
||||
|
||||
Watcher_Asset :: struct {
|
||||
type: Asset_Type,
|
||||
path: name.Name,
|
||||
modtime: physfs.sint64,
|
||||
}
|
||||
|
||||
Asset_Modtime_Watcher :: struct {
|
||||
ops: chan.Chan(Asset_Watcher_Op),
|
||||
modified_assets: chan.Chan(Watcher_Asset),
|
||||
loaded_assets: [dynamic]Watcher_Asset,
|
||||
thread: ^thread.Thread,
|
||||
}
|
||||
|
||||
modtime_watcher_init :: proc(watcher: ^Asset_Modtime_Watcher, allocator := context.allocator) {
|
||||
err: runtime.Allocator_Error
|
||||
watcher.ops, err = chan.create_buffered(
|
||||
chan.Chan(Asset_Watcher_Op),
|
||||
ASSET_WATCHER_OPS_BUFFER,
|
||||
allocator,
|
||||
)
|
||||
assert(err == nil)
|
||||
watcher.modified_assets, err = chan.create_buffered(
|
||||
chan.Chan(Watcher_Asset),
|
||||
ASSET_WATCHER_OPS_BUFFER,
|
||||
allocator,
|
||||
)
|
||||
watcher.loaded_assets = make_dynamic_array([dynamic]Watcher_Asset, allocator)
|
||||
|
||||
watcher.thread = thread.create(modtime_watcher_thread_proc)
|
||||
watcher.thread.data = watcher
|
||||
watcher_context := runtime.default_context()
|
||||
watcher_context.logger = context.logger
|
||||
watcher_context.allocator = context.allocator
|
||||
watcher.thread.init_context = watcher_context
|
||||
thread.start(watcher.thread)
|
||||
}
|
||||
|
||||
modtime_watcher_deinit :: proc(watcher: ^Asset_Modtime_Watcher) {
|
||||
if !chan.is_closed(&watcher.ops) {
|
||||
chan.close(&watcher.ops)
|
||||
thread.join(watcher.thread)
|
||||
watcher.thread = nil
|
||||
}
|
||||
|
||||
chan.destroy(&watcher.ops)
|
||||
chan.close(&watcher.modified_assets)
|
||||
chan.destroy(&watcher.modified_assets)
|
||||
delete(watcher.loaded_assets)
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
modtime_watcher_thread_proc :: proc(t: ^thread.Thread) {
|
||||
watcher := cast(^Asset_Modtime_Watcher)t.data
|
||||
|
||||
log.debugf("watcher thread")
|
||||
|
||||
for !chan.is_closed(&watcher.ops) {
|
||||
for recv_op in chan.try_recv(watcher.ops) {
|
||||
switch op in recv_op {
|
||||
case Asset_Watcher_Op_Add:
|
||||
log.debugf("add [{}] {}", op.type, name.to_string(op.path))
|
||||
append(
|
||||
&watcher.loaded_assets,
|
||||
Watcher_Asset{type = op.type, path = op.path, modtime = op.modtime},
|
||||
)
|
||||
case Asset_Watcher_Op_Remove:
|
||||
log.debugf("remove [{}] {}", op.type, name.to_string(op.path))
|
||||
i := 0
|
||||
for i < len(watcher.loaded_assets) {
|
||||
if op.path == watcher.loaded_assets[i].path &&
|
||||
op.type == watcher.loaded_assets[i].type {
|
||||
unordered_remove(&watcher.loaded_assets, i)
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &asset in watcher.loaded_assets {
|
||||
modtime := physfs.getLastModTime(name.to_cstring(asset.path))
|
||||
|
||||
if asset.modtime != modtime {
|
||||
log.debugf("change [{}] {}", asset.type, name.to_string(asset.path))
|
||||
ok := chan.send(
|
||||
watcher.modified_assets,
|
||||
Watcher_Asset{type = asset.type, path = asset.path, modtime = modtime},
|
||||
)
|
||||
assert(ok)
|
||||
}
|
||||
asset.modtime = modtime
|
||||
}
|
||||
|
||||
// To avoid busy loop just in case
|
||||
thread.yield()
|
||||
}
|
||||
}
|
||||
|
||||
modtime_watcher_add_asset :: proc(
|
||||
watcher: ^Asset_Modtime_Watcher,
|
||||
type: Asset_Type,
|
||||
path: name.Name,
|
||||
modtime: physfs.sint64,
|
||||
) -> bool {
|
||||
return chan.send(
|
||||
watcher.ops,
|
||||
Asset_Watcher_Op_Add{type = type, path = path, modtime = modtime},
|
||||
)
|
||||
}
|
||||
|
||||
modtime_watcher_remove_asset :: proc(
|
||||
watcher: ^Asset_Modtime_Watcher,
|
||||
type: Asset_Type,
|
||||
path: name.Name,
|
||||
) -> bool {
|
||||
return chan.send(watcher.ops, Asset_Watcher_Op_Remove{type = type, path = path})
|
||||
}
|
@ -63,7 +63,8 @@ scene_destroy :: proc(world: ^World, scene: ^Scene) {
|
||||
}
|
||||
|
||||
immediate_scene :: proc(world: ^World, scene: ^Scene, path: cstring) {
|
||||
path_name := name.from_string(string(path))
|
||||
tracy.Zone()
|
||||
path_name := name.from_cstring(path)
|
||||
|
||||
desc, reloaded := assets.get_scene_desc(&g_mem.assetman, path_name)
|
||||
|
||||
@ -71,15 +72,16 @@ immediate_scene :: proc(world: ^World, scene: ^Scene, path: cstring) {
|
||||
scene_destroy(world, scene)
|
||||
|
||||
scene.scene_desc_path = path_name
|
||||
scene.level_geoms = make([]physics.Level_Geom_Handle, len(desc.instances))
|
||||
|
||||
sim_state := physics.get_sim_state(&world.physics_scene)
|
||||
for inst in desc.instances {
|
||||
physics.add_level_geom(
|
||||
for inst, i in desc.instances {
|
||||
scene.level_geoms[i] = physics.add_level_geom(
|
||||
sim_state,
|
||||
physics.Level_Geom_Config {
|
||||
position = inst.pos,
|
||||
rotation = inst.rot,
|
||||
source = physics.Level_Geometry_Asset(name.to_string(inst.model)),
|
||||
source = physics.Level_Geometry_Asset(inst.model),
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -87,6 +89,7 @@ immediate_scene :: proc(world: ^World, scene: ^Scene, path: cstring) {
|
||||
}
|
||||
|
||||
scene_copy :: proc(dst, src: ^Scene) {
|
||||
tracy.Zone()
|
||||
dst.scene_desc_path = src.scene_desc_path
|
||||
if len(dst.level_geoms) != len(src.level_geoms) {
|
||||
delete(dst.level_geoms)
|
||||
@ -101,7 +104,7 @@ scene_draw :: proc(scene: ^Scene) {
|
||||
render.draw_model(
|
||||
assets.get_model(&g_mem.assetman, name.to_cstring(geo.model)),
|
||||
{},
|
||||
auto_cast linalg.matrix4_from_trs(geo.pos, geo.rot, geo.scale),
|
||||
auto_cast linalg.matrix4_from_trs(geo.pos, geo.rot, 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1437,6 +1440,8 @@ game_update :: proc() -> bool {
|
||||
tracy.Zone()
|
||||
defer tracy.FrameMark()
|
||||
|
||||
assets.assetman_tick(&g_mem.assetman)
|
||||
|
||||
update()
|
||||
draw()
|
||||
|
||||
@ -1474,7 +1479,6 @@ game_init :: proc() {
|
||||
init_physifs_raylib_callbacks()
|
||||
|
||||
assets.assetman_init(&g_mem.assetman)
|
||||
assets.init(&g_mem.assetman)
|
||||
|
||||
editor_state_init(&g_mem.es, 100)
|
||||
runtime_world_init(&g_mem.runtime_world, DEV_BUILD ? 100 : 2)
|
||||
|
@ -265,8 +265,7 @@ query_separation_edges :: proc(
|
||||
a_edge = -1
|
||||
b_edge = -1
|
||||
|
||||
temp := runtime.default_temp_allocator_temp_begin()
|
||||
defer runtime.default_temp_allocator_temp_end(temp)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
checked_pairs: bit_array.Bit_Array
|
||||
bit_array.init(&checked_pairs, len(a.edges) * len(b.edges), 0, context.temp_allocator)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package physics
|
||||
|
||||
import "base:runtime"
|
||||
import "bvh"
|
||||
import "common:name"
|
||||
import "core:fmt"
|
||||
@ -9,6 +10,7 @@ import lg "core:math/linalg"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
import "game:assets"
|
||||
import "game:debug"
|
||||
import he "game:halfedge"
|
||||
import "game:ui"
|
||||
@ -61,7 +63,14 @@ init_debug_state :: proc(debug_state: ^Debug_State) {
|
||||
draw_debug_scene :: proc(scene: ^Scene, debug_state: ^Debug_State) {
|
||||
tracy.Zone()
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
sim_state := get_sim_state(scene)
|
||||
sim_cache: Sim_Cache
|
||||
sim_cache.level_geom_asset_bvh = make_map(
|
||||
map[Level_Geom_Handle]assets.Loaded_BVH,
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
// Static_TLAS
|
||||
if true && sim_state.static_tlas.built {
|
||||
@ -73,6 +82,7 @@ draw_debug_scene :: proc(scene: ^Scene, debug_state: ^Debug_State) {
|
||||
|
||||
it := bvh.iterator(&sim_state.static_tlas.bvh_tree)
|
||||
|
||||
if false {
|
||||
for node in bvh.iterator_next(&it) {
|
||||
if bvh.is_leaf_node(node) {
|
||||
prim_start := node.child_or_prim_start
|
||||
@ -80,8 +90,11 @@ draw_debug_scene :: proc(scene: ^Scene, debug_state: ^Debug_State) {
|
||||
for level_geom_idx in prim_start ..< prim_start + node.prim_len {
|
||||
level_geom_handle := index_to_level_geom(int(level_geom_idx))
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
blas := get_level_geom_blas(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
blas, vertices, indices := get_level_geom_data(
|
||||
sim_state,
|
||||
&sim_cache,
|
||||
level_geom_handle,
|
||||
)
|
||||
|
||||
bvh.debug_draw_bvh_bounds_mesh(
|
||||
&blas,
|
||||
@ -93,6 +106,7 @@ draw_debug_scene :: proc(scene: ^Scene, debug_state: ^Debug_State) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic TLAS
|
||||
if false && sim_state.dynamic_tlas.built {
|
||||
|
@ -300,6 +300,7 @@ body_get_convex_shapes_world :: proc(
|
||||
|
||||
level_geom_get_convex_shape :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
level_geom_handle: Level_Geom_Handle,
|
||||
tri_idx: int,
|
||||
allocator := context.temp_allocator,
|
||||
@ -307,7 +308,7 @@ level_geom_get_convex_shape :: proc(
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
_, vertices, indices := get_level_geom_data(sim_state, sim_cache, level_geom_handle)
|
||||
return collision.double_sided_triangle_to_convex(
|
||||
get_transformed_triangle(vertices, indices, tri_idx, level_geom.x, level_geom.q),
|
||||
allocator,
|
||||
@ -353,7 +354,7 @@ rotate_extent :: proc(extent: Vec3, q: Quat) -> Vec3 {
|
||||
return result
|
||||
}
|
||||
|
||||
aabb_transform :: proc(local_aabb: AABB, x: Vec3, q: Quat) -> (aabb: AABB) {
|
||||
aabb_transform_phys :: proc(local_aabb: AABB, x: Vec3, q: Quat) -> (aabb: AABB) {
|
||||
aabb.center = lg.quaternion_mul_vector3(q, local_aabb.center) + x
|
||||
|
||||
aabb.extent = rotate_extent(local_aabb.extent, q)
|
||||
@ -361,6 +362,34 @@ aabb_transform :: proc(local_aabb: AABB, x: Vec3, q: Quat) -> (aabb: AABB) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: consolidate formats, please
|
||||
aabb_transform_bvh :: proc(local_aabb: bvh.AABB, x: Vec3, q: Quat) -> (aabb: bvh.AABB) {
|
||||
phys_aabb := bvh_aabb_to_phys_aabb(local_aabb)
|
||||
phys_aabb = aabb_transform(phys_aabb, x, q)
|
||||
|
||||
aabb.min = phys_aabb.center - phys_aabb.extent
|
||||
aabb.max = phys_aabb.center + phys_aabb.extent
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
aabb_transform :: proc {
|
||||
aabb_transform_phys,
|
||||
aabb_transform_bvh,
|
||||
}
|
||||
|
||||
aabb_inv_transform :: proc(local_aabb: bvh.AABB, x: Vec3, q: Quat) -> (aabb: bvh.AABB) {
|
||||
phys_aabb := bvh_aabb_to_phys_aabb(local_aabb)
|
||||
inv_q := lg.quaternion_inverse(q)
|
||||
phys_aabb.center = lg.quaternion_mul_vector3(inv_q, phys_aabb.center - x)
|
||||
phys_aabb.extent = rotate_extent(phys_aabb.extent, inv_q)
|
||||
|
||||
aabb.min = phys_aabb.center - phys_aabb.extent
|
||||
aabb.max = phys_aabb.center + phys_aabb.extent
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
input_shape_get_aabb :: proc(shapes: []Input_Shape) -> (aabb: AABB) {
|
||||
min, max: Vec3 = max(f32), min(f32)
|
||||
|
||||
|
@ -4,7 +4,6 @@ import "bvh"
|
||||
import "collision"
|
||||
import "common:name"
|
||||
import lg "core:math/linalg"
|
||||
import "core:strings"
|
||||
import "game:assets"
|
||||
import "game:container/spanpool"
|
||||
import "libs:tracy"
|
||||
@ -476,9 +475,6 @@ Suspension_Constraint_Ptr :: #soa^#soa[]Suspension_Constraint
|
||||
Engine_Ptr :: ^Engine
|
||||
Level_Geom_Ptr :: ^Level_Geom
|
||||
|
||||
_invalid_body: #soa[1]Body
|
||||
_invalid_body_slice := _invalid_body[:]
|
||||
|
||||
_invalid_suspension_constraint: #soa[1]Suspension_Constraint
|
||||
_invalid_suspension_constraint_slice := _invalid_suspension_constraint[:]
|
||||
|
||||
@ -507,8 +503,12 @@ flip_sim_state :: proc(scene: ^Scene) {
|
||||
|
||||
/// Returns pointer to soa slice. NEVER STORE IT
|
||||
get_body :: proc(sim_state: ^Sim_State, handle: Body_Handle) -> Body_Ptr {
|
||||
@(static) _invalid_body: #soa[1]Body
|
||||
@(static) _invalid_body_slice: #soa[]Body
|
||||
|
||||
index := int(handle) - 1
|
||||
if index < 0 || index >= len(sim_state.bodies_slice) {
|
||||
_invalid_body_slice = _invalid_body[:]
|
||||
_invalid_body_slice[0] = {
|
||||
alive = true,
|
||||
q = lg.QUATERNIONF32_IDENTITY,
|
||||
@ -602,7 +602,7 @@ Engine_Config :: struct {
|
||||
axle: Drive_Axle_Config,
|
||||
}
|
||||
|
||||
Level_Geometry_Asset :: distinct string
|
||||
Level_Geometry_Asset :: distinct name.Name
|
||||
|
||||
Level_Geometry_Mesh :: struct {
|
||||
vertices: []Vec3,
|
||||
@ -949,8 +949,10 @@ get_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) -> Leve
|
||||
|
||||
get_level_geom_data :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
handle: Level_Geom_Handle,
|
||||
) -> (
|
||||
blas: bvh.BVH,
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
) {
|
||||
@ -958,17 +960,28 @@ get_level_geom_data :: proc(
|
||||
|
||||
switch s in level_geom.source {
|
||||
case Level_Geom_Source_Local:
|
||||
blas.nodes = spanpool.resolve_slice(&sim_state.blas_nodes_pool, s.blas.nodes)
|
||||
blas.primitives = spanpool.resolve_slice(
|
||||
&sim_state.blas_primitives_pool,
|
||||
s.blas.primitives,
|
||||
)
|
||||
blas.nodes_used = i32(len(blas.nodes))
|
||||
vertices = spanpool.resolve_slice(&sim_state.geometry_vertices_pool, s.geometry.vertices)
|
||||
indices = spanpool.resolve_slice(&sim_state.geometry_indices_pool, s.geometry.indices)
|
||||
case Level_Geom_Source_Asset:
|
||||
loaded_bvh, reloaded := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(name.to_string(s.assetpath)),
|
||||
)
|
||||
loaded_bvh, ok := sim_cache.level_geom_asset_bvh[handle]
|
||||
|
||||
if !ok {
|
||||
reloaded: bool
|
||||
loaded_bvh, reloaded = assets.get_bvh(sim_state.scene.assetman, s.assetpath)
|
||||
if reloaded {
|
||||
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb)
|
||||
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
|
||||
}
|
||||
// sim_cache.level_geom_asset_bvh[handle] = loaded_bvh
|
||||
}
|
||||
|
||||
blas = loaded_bvh.bvh
|
||||
vertices = loaded_bvh.vertices
|
||||
indices = loaded_bvh.indices
|
||||
}
|
||||
@ -976,28 +989,6 @@ get_level_geom_data :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
get_level_geom_blas :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) -> (bvh: bvh.BVH) {
|
||||
level_geom := get_level_geom(sim_state, handle)
|
||||
switch s in level_geom.source {
|
||||
case Level_Geom_Source_Local:
|
||||
bvh.nodes = spanpool.resolve_slice(&sim_state.blas_nodes_pool, s.blas.nodes)
|
||||
bvh.primitives = spanpool.resolve_slice(&sim_state.blas_primitives_pool, s.blas.primitives)
|
||||
bvh.nodes_used = i32(len(bvh.nodes))
|
||||
case Level_Geom_Source_Asset:
|
||||
loaded_bvh, reloaded := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(name.to_string(s.assetpath)),
|
||||
)
|
||||
if reloaded {
|
||||
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb)
|
||||
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
|
||||
}
|
||||
|
||||
bvh = loaded_bvh.bvh
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
update_level_geom_from_config :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
level_geom: Level_Geom_Ptr,
|
||||
@ -1063,13 +1054,10 @@ add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Leve
|
||||
level_geom.source = source
|
||||
case Level_Geometry_Asset:
|
||||
level_geom.source = Level_Geom_Source_Asset {
|
||||
assetpath = name.from_string(string(s)),
|
||||
assetpath = name.Name(s),
|
||||
}
|
||||
|
||||
bvh, _ := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(string(s)),
|
||||
)
|
||||
bvh, _ := assets.get_bvh(sim_state.scene.assetman, name.Name(s))
|
||||
level_geom.aabb = AABB {
|
||||
center = (bvh.aabb.max + bvh.aabb.min) * 0.5,
|
||||
extent = (bvh.aabb.max - bvh.aabb.min) * 0.5,
|
||||
@ -1104,6 +1092,11 @@ remove_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) {
|
||||
spanpool.free(&sim_state.blas_primitives_pool, s.blas.primitives)
|
||||
case Level_Geom_Source_Asset:
|
||||
}
|
||||
|
||||
level_geom.alive = false
|
||||
level_geom.next_plus_one = sim_state.first_free_level_geom_plus_one
|
||||
sim_state.first_free_level_geom_plus_one = i32(handle)
|
||||
sim_state.num_level_geoms -= 1
|
||||
}
|
||||
|
||||
_get_first_free_body :: proc(sim_state: ^Sim_State) -> i32 {
|
||||
|
@ -11,6 +11,7 @@ import "core:math"
|
||||
import lg "core:math/linalg"
|
||||
import "core:math/rand"
|
||||
import "core:slice"
|
||||
import "game:assets"
|
||||
import "game:debug"
|
||||
import he "game:halfedge"
|
||||
import "libs:tracy"
|
||||
@ -174,8 +175,10 @@ dynamic_tlas_destroy :: proc(dyn_tlas: ^Dynamic_TLAS) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
raycasts_level :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
tlas: ^Static_TLAS,
|
||||
origin, dir: Vec3,
|
||||
distance := max(f32),
|
||||
@ -201,32 +204,31 @@ raycasts_level :: proc(
|
||||
int(tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]),
|
||||
)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
blas := get_level_geom_blas(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
blas, vertices, indices := get_level_geom_data(sim_state, sim_cache, level_geom_handle)
|
||||
|
||||
// TODO: transform ray into blas space and back
|
||||
|
||||
blas_it := bvh.iterator_intersect_leaf_ray(&blas, ray, distance)
|
||||
inv_q := lg.quaternion_inverse(level_geom.q)
|
||||
local_ray := ray
|
||||
local_ray.origin = lg.quaternion_mul_vector3(inv_q, ray.origin - level_geom.x)
|
||||
local_ray.dir = lg.quaternion_mul_vector3(inv_q, ray.dir)
|
||||
local_ray.dir_inv = 1.0 / local_ray.dir
|
||||
|
||||
blas_it := bvh.iterator_intersect_leaf_ray(&blas, local_ray, distance)
|
||||
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&blas_it) {
|
||||
for k in 0 ..< blas_leaf_node.prim_len {
|
||||
tri_idx := int(blas.primitives[blas_leaf_node.child_or_prim_start + k])
|
||||
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
tri_idx,
|
||||
level_geom.x,
|
||||
level_geom.q,
|
||||
)
|
||||
tri := get_triangle(vertices, indices, tri_idx)
|
||||
|
||||
hit_t, tmp_normal, _, ok := collision.intersect_ray_triangle(
|
||||
{origin, origin + dir},
|
||||
{local_ray.origin, local_ray.origin + local_ray.dir},
|
||||
tri,
|
||||
)
|
||||
|
||||
if ok && (!hit || hit_t < t) {
|
||||
t = hit_t
|
||||
normal = tmp_normal
|
||||
normal = lg.quaternion_mul_vector3(level_geom.q, tmp_normal)
|
||||
hit = true
|
||||
}
|
||||
}
|
||||
@ -294,6 +296,7 @@ raycast_bodies :: proc(
|
||||
|
||||
raycast :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
static_tlas: ^Static_TLAS,
|
||||
dyn_tlas: ^Dynamic_TLAS,
|
||||
origin, dir: Vec3,
|
||||
@ -305,7 +308,7 @@ raycast :: proc(
|
||||
) {
|
||||
t = distance
|
||||
|
||||
t1, normal1, hit1 := raycasts_level(sim_state, static_tlas, origin, dir, t)
|
||||
t1, normal1, hit1 := raycasts_level(sim_state, sim_cache, static_tlas, origin, dir, t)
|
||||
t2, normal2, hit2 := raycast_bodies(sim_state, dyn_tlas, origin, dir, t)
|
||||
|
||||
hit = hit1 || hit2
|
||||
@ -329,6 +332,14 @@ raycast :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
// Cache used during simulation to avoid complex computations
|
||||
Sim_Cache :: struct {
|
||||
// Looking up bvh can be expensive because assetman touches the filesystem each time. We assume that during simulation it cannot change
|
||||
// so it's safe to cache
|
||||
level_geom_asset_bvh: map[Level_Geom_Handle]assets.Loaded_BVH,
|
||||
}
|
||||
|
||||
|
||||
get_triangle :: proc(vertices: []Vec3, indices: []u16, tri_idx: int) -> (tri: [3]Vec3) {
|
||||
i1, i2, i3 := indices[tri_idx * 3 + 0], indices[tri_idx * 3 + 1], indices[tri_idx * 3 + 2]
|
||||
tri[0], tri[1], tri[2] = vertices[i1], vertices[i2], vertices[i3]
|
||||
@ -361,6 +372,7 @@ get_triangle_aabb :: proc(tri: [3]Vec3) -> (aabb: bvh.AABB) {
|
||||
|
||||
remove_invalid_contacts :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
static_tlas: Static_TLAS,
|
||||
dyn_tlas: Dynamic_TLAS,
|
||||
) {
|
||||
@ -394,7 +406,11 @@ remove_invalid_contacts :: proc(
|
||||
|
||||
if !should_remove {
|
||||
tri_idx := int(contact.local_tri_idx)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
_, vertices, indices := get_level_geom_data(
|
||||
sim_state,
|
||||
sim_cache,
|
||||
level_geom_handle,
|
||||
)
|
||||
should_remove |= tri_idx * 3 >= len(indices)
|
||||
|
||||
if !should_remove {
|
||||
@ -453,6 +469,7 @@ remove_invalid_contacts :: proc(
|
||||
// TODO: free intermediate temp allocs
|
||||
find_new_contacts :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
static_tlas: ^Static_TLAS,
|
||||
dyn_tlas: ^Dynamic_TLAS,
|
||||
) {
|
||||
@ -559,10 +576,16 @@ find_new_contacts :: proc(
|
||||
)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
if level_geom.alive {
|
||||
blas := get_level_geom_blas(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
blas, vertices, indices := get_level_geom_data(
|
||||
sim_state,
|
||||
sim_cache,
|
||||
level_geom_handle,
|
||||
)
|
||||
|
||||
blas_it := bvh.iterator_intersect_leaf_aabb(&blas, body_aabb)
|
||||
blas_it := bvh.iterator_intersect_leaf_aabb(
|
||||
&blas,
|
||||
aabb_inv_transform(body_aabb, level_geom.x, level_geom.q),
|
||||
)
|
||||
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&blas_it) {
|
||||
for k in 0 ..< blas_leaf_node.prim_len {
|
||||
tri_idx := int(
|
||||
@ -653,9 +676,16 @@ simulate :: proc(
|
||||
prune_immediate(scene)
|
||||
|
||||
copy_sim_state(get_next_sim_state(scene), get_sim_state(scene))
|
||||
|
||||
sim_state := get_next_sim_state(scene)
|
||||
|
||||
// runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
sim_cache: Sim_Cache
|
||||
sim_cache.level_geom_asset_bvh = make_map(
|
||||
map[Level_Geom_Handle]assets.Loaded_BVH,
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
num_steps := 0
|
||||
switch step_mode {
|
||||
case .Accumulated_Time:
|
||||
@ -666,11 +696,11 @@ simulate :: proc(
|
||||
state.accumulated_time -= config.timestep
|
||||
|
||||
if num_steps < MAX_STEPS {
|
||||
simulate_step(scene, sim_state, config)
|
||||
simulate_step(scene, sim_state, &sim_cache, config)
|
||||
}
|
||||
}
|
||||
case .Single:
|
||||
simulate_step(scene, get_next_sim_state(scene), config)
|
||||
simulate_step(scene, get_next_sim_state(scene), &sim_cache, config)
|
||||
num_steps += 1
|
||||
}
|
||||
|
||||
@ -718,7 +748,7 @@ Contact :: struct {
|
||||
applied_normal_correction: [4]f32,
|
||||
}
|
||||
|
||||
update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
|
||||
update_contacts :: proc(sim_state: ^Sim_State, sim_cache: ^Sim_Cache, static_tlas: ^Static_TLAS) {
|
||||
tracy.Zone()
|
||||
|
||||
temp := runtime.default_temp_allocator_temp_begin()
|
||||
@ -824,7 +854,7 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
|
||||
case .Body_vs_Level:
|
||||
level_geom_handle := Level_Geom_Handle(contact.b)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
_, vertices, indices := get_level_geom_data(sim_state, sim_cache, level_geom_handle)
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
@ -1232,6 +1262,7 @@ calculate_ground_vel :: proc(
|
||||
|
||||
pgs_solve_suspension :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
static_tlas: ^Static_TLAS,
|
||||
dyn_tlas: ^Dynamic_TLAS,
|
||||
config: Solver_Config,
|
||||
@ -1249,6 +1280,7 @@ pgs_solve_suspension :: proc(
|
||||
dir := body_local_to_world_vec(body, v.rel_dir)
|
||||
v.hit_t, v.hit_normal, v.hit = raycast(
|
||||
sim_state,
|
||||
sim_cache,
|
||||
static_tlas,
|
||||
dyn_tlas,
|
||||
wheel_world_pos,
|
||||
@ -1438,6 +1470,7 @@ pgs_solve_suspension :: proc(
|
||||
|
||||
pgs_substep :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
static_tlas: ^Static_TLAS,
|
||||
dyn_tlas: ^Dynamic_TLAS,
|
||||
config: Solver_Config,
|
||||
@ -1559,7 +1592,7 @@ pgs_substep :: proc(
|
||||
apply_bias := true
|
||||
pgs_solve_contacts(sim_state, config, dt, inv_dt, apply_bias)
|
||||
pgs_solve_engines(sim_state, config, dt, inv_dt)
|
||||
pgs_solve_suspension(sim_state, static_tlas, dyn_tlas, config, dt, inv_dt)
|
||||
pgs_solve_suspension(sim_state, sim_cache, static_tlas, dyn_tlas, config, dt, inv_dt)
|
||||
|
||||
for i in 0 ..< len(sim_state.bodies_slice) {
|
||||
body := &sim_state.bodies_slice[i]
|
||||
@ -1600,7 +1633,12 @@ pgs_substep :: proc(
|
||||
// pgs_solve_suspension(sim_state, config, dt, inv_dt, apply_bias)
|
||||
}
|
||||
|
||||
simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Config) {
|
||||
simulate_step :: proc(
|
||||
scene: ^Scene,
|
||||
sim_state: ^Sim_State,
|
||||
sim_cache: ^Sim_Cache,
|
||||
config: Solver_Config,
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
substeps := config.substreps_minus_one + 1
|
||||
@ -1614,9 +1652,9 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
build_static_tlas(sim_state, &sim_state.static_tlas)
|
||||
build_dynamic_tlas(sim_state, config, &sim_state.dynamic_tlas)
|
||||
|
||||
remove_invalid_contacts(sim_state, sim_state.static_tlas, sim_state.dynamic_tlas)
|
||||
find_new_contacts(sim_state, &sim_state.static_tlas, &sim_state.dynamic_tlas)
|
||||
update_contacts(sim_state, &sim_state.static_tlas)
|
||||
remove_invalid_contacts(sim_state, sim_cache, sim_state.static_tlas, sim_state.dynamic_tlas)
|
||||
find_new_contacts(sim_state, sim_cache, &sim_state.static_tlas, &sim_state.dynamic_tlas)
|
||||
update_contacts(sim_state, sim_cache, &sim_state.static_tlas)
|
||||
|
||||
Solver :: enum {
|
||||
XPBD,
|
||||
@ -1634,6 +1672,7 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
for _ in 0 ..< substeps {
|
||||
pgs_substep(
|
||||
sim_state,
|
||||
sim_cache,
|
||||
&sim_state.static_tlas,
|
||||
&sim_state.dynamic_tlas,
|
||||
config,
|
||||
|
@ -2,12 +2,9 @@ package raylib
|
||||
|
||||
import "core:c"
|
||||
|
||||
_ :: c
|
||||
|
||||
RAYGUI_ENABLED :: #config(RAYGUI_ENABLED, false)
|
||||
RAYGUI_SHARED :: #config(RAYGUI_SHARED, false)
|
||||
RAYGUI_WASM_LIB :: #config(RAYGUI_WASM_LIB, "wasm/libraygui.a")
|
||||
|
||||
when RAYGUI_ENABLED {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import lib {
|
||||
"windows/rayguidll.lib" when RAYGUI_SHARED else "windows/raygui.lib",
|
||||
@ -26,6 +23,10 @@ when RAYGUI_ENABLED {
|
||||
"macos/libraygui.dylib" when RAYGUI_SHARED else "macos/libraygui.a",
|
||||
}
|
||||
}
|
||||
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||
foreign import lib {
|
||||
RAYGUI_WASM_LIB,
|
||||
}
|
||||
} else {
|
||||
foreign import lib "system:raygui"
|
||||
}
|
||||
@ -563,4 +564,3 @@ when RAYGUI_ENABLED {
|
||||
ICON_254 = 254,
|
||||
ICON_255 = 255,
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ Camera3D :: struct {
|
||||
target: Vector3, // Camera target it looks-at
|
||||
up: Vector3, // Camera up vector (rotation over its axis)
|
||||
fovy: f32, // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic
|
||||
projection: CameraProjection, // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
|
||||
projection: CameraProjection, // Camera projection: `.PERSPECTIVE` or `.ORTHOGRAPHIC`
|
||||
}
|
||||
|
||||
Camera :: Camera3D // Camera type fallback, defaults to Camera3D
|
||||
@ -329,6 +329,7 @@ BoneInfo :: struct {
|
||||
// Model type
|
||||
Model :: struct #align(align_of(uintptr)) {
|
||||
transform: Matrix, // Local transform matrix
|
||||
|
||||
meshCount: c.int, // Number of meshes
|
||||
materialCount: c.int, // Number of materials
|
||||
meshes: [^]Mesh, // Meshes array
|
||||
@ -384,6 +385,7 @@ Wave :: struct {
|
||||
AudioStream :: struct {
|
||||
buffer: rawptr, // Pointer to internal data used by the audio system
|
||||
processor: rawptr, // Pointer to internal data processor, useful for audio effects
|
||||
|
||||
sampleRate: c.uint, // Frequency (samples per second)
|
||||
sampleSize: c.uint, // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
|
||||
channels: c.uint, // Number of channels (1-mono, 2-stereo)
|
||||
@ -401,6 +403,7 @@ Music :: struct {
|
||||
using stream: AudioStream, // Audio stream
|
||||
frameCount: c.uint, // Total number of frames (considering channels)
|
||||
looping: bool, // Music looping enable
|
||||
|
||||
ctxType: c.int, // Type of music context (audio filetype)
|
||||
ctxData: rawptr, // Audio context data, depends on type
|
||||
}
|
||||
@ -825,6 +828,17 @@ Gesture :: enum c.uint {
|
||||
}
|
||||
Gestures :: distinct bit_set[Gesture; c.uint]
|
||||
|
||||
// Camera speed values
|
||||
CAMERA_MOVE_SPEED :: 5.4
|
||||
CAMERA_ROTATION_SPEED :: 0.03
|
||||
CAMERA_PAN_SPEED :: 0.2
|
||||
|
||||
// Camera mouse movement sensitivity
|
||||
CAMERA_MOUSE_MOVE_SENSITIVITY :: 0.003
|
||||
|
||||
// Camera orbital speed in CAMERA_ORBITAL mode
|
||||
CAMERA_ORBITAL_SPEED :: 0.5
|
||||
|
||||
// Camera system modes
|
||||
CameraMode :: enum c.int {
|
||||
CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing)
|
||||
@ -1163,6 +1177,23 @@ foreign lib {
|
||||
UpdateCamera :: proc(camera: ^Camera, mode: CameraMode) --- // Set camera mode (multiple camera modes available)
|
||||
UpdateCameraPro :: proc(camera: ^Camera, movement: Vector3, rotation: Vector3, zoom: f32) --- // Update camera movement/rotation
|
||||
|
||||
GetCameraForward :: proc(camera: ^Camera) -> Vector3 --- // returns the camera's forward vector (normalized)
|
||||
GetCameraUp :: proc(camera: ^Camera) -> Vector3 --- // returns the camera's up vector (normalized) - might not be perpendicular to forward vector
|
||||
GetCameraRight :: proc(camera: ^Camera) -> Vector3 --- // returns the camera's right vector (normalized)
|
||||
|
||||
// Camera Movement/Rotation. Angle is provided in radians
|
||||
|
||||
CameraMoveForward :: proc(camera: ^Camera, distance: f32, moveInWorldPlane: bool) --- // move the camera in its forward direction
|
||||
CameraMoveUp :: proc(camera: ^Camera, distance: f32) --- // move camera in its up direction
|
||||
CameraMoveRight :: proc(camera: ^Camera, distance: f32, moveInWorldPlane: bool) --- // move camera in it's current right direction
|
||||
CameraMoveToTarget :: proc(camera: ^Camera, delta: f32) --- // moves the camera position closer/farther to/from the camera target
|
||||
CameraYaw :: proc(camera: ^Camera, angle: f32, rotateAroundTarget: bool) --- // rotates the camera around its up vector (left and right)
|
||||
CameraPitch :: proc(camera: ^Camera, angle: f32, lockView: bool, rotateAroundTarget: bool, rotateUp: bool) --- // rotates the camera around its right vector (up and down)
|
||||
CameraRoll :: proc(camera: ^Camera, angle: f32) --- // rotates the camera around its forward vector (left and right)
|
||||
|
||||
GetCameraViewMatrix :: proc(camera: ^Camera) -> Matrix --- // returns the camera view matrix
|
||||
GetCameraProjectionMatrix :: proc(camera: ^Camera, aspect: f32) -> Matrix --- // returns the camera projection matrix
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Basic Shapes Drawing Functions (Module: shapes)
|
||||
//------------------------------------------------------------------------------------
|
||||
@ -1205,7 +1236,7 @@ foreign lib {
|
||||
DrawRectangleLines :: proc(posX, posY: c.int, width, height: c.int, color: Color) --- // Draw rectangle outline
|
||||
DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: f32, color: Color) --- // Draw rectangle outline with extended parameters
|
||||
DrawRectangleRounded :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle with rounded edges
|
||||
DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle lines with rounded edges
|
||||
DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle lines with rounded edges
|
||||
DrawRectangleRoundedLinesEx :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle with rounded edges outline
|
||||
DrawTriangle :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!)
|
||||
DrawTriangleLines :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw triangle outline (vertex in counter-clockwise order!)
|
||||
@ -1247,6 +1278,7 @@ foreign lib {
|
||||
GetCollisionRec :: proc(rec1, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision
|
||||
|
||||
|
||||
|
||||
// Image loading functions
|
||||
// NOTE: These functions do not require GPU access
|
||||
|
||||
@ -1390,6 +1422,8 @@ foreign lib {
|
||||
GetPixelDataSize :: proc(width, height: c.int, format: PixelFormat) -> c.int --- // Get pixel data size in bytes for certain format
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Font Loading and Text Drawing Functions (Module: text)
|
||||
//------------------------------------------------------------------------------------
|
||||
@ -1673,8 +1707,8 @@ IsGestureDetected :: proc "c" (gesture: Gesture) -> bool {
|
||||
|
||||
// Text formatting with variables (sprintf style)
|
||||
TextFormat :: proc(text: cstring, args: ..any) -> cstring {
|
||||
@(static) buffers: [MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH]byte
|
||||
@(static) index: u32
|
||||
@static buffers: [MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH]byte
|
||||
@static index: u32
|
||||
|
||||
buffer := buffers[index][:]
|
||||
mem.zero_slice(buffer)
|
||||
@ -1715,17 +1749,9 @@ MemAllocator :: proc "contextless" () -> mem.Allocator {
|
||||
return mem.Allocator{MemAllocatorProc, nil}
|
||||
}
|
||||
|
||||
MemAllocatorProc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: mem.Allocator_Mode,
|
||||
MemAllocatorProc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr,
|
||||
old_size: int,
|
||||
location := #caller_location,
|
||||
) -> (
|
||||
data: []byte,
|
||||
err: mem.Allocator_Error,
|
||||
) {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
ptr := MemAlloc(c.uint(size))
|
||||
|
@ -523,7 +523,7 @@ Vector3Unproject :: proc "c" (source: Vector3, projection: Matrix, view: Matrix)
|
||||
|
||||
quat: Quaternion
|
||||
quat.x = source.x
|
||||
quat.y = source.z
|
||||
quat.y = source.y
|
||||
quat.z = source.z
|
||||
quat.w = 1
|
||||
|
||||
@ -831,8 +831,8 @@ fmaxf :: proc "contextless" (x, y: f32) -> f32 {
|
||||
return x
|
||||
}
|
||||
|
||||
if math.signbit(x) != math.signbit(y) {
|
||||
return y if math.signbit(x) else x
|
||||
if math.sign_bit(x) != math.sign_bit(y) {
|
||||
return y if math.sign_bit(x) else x
|
||||
}
|
||||
|
||||
return y if x < y else x
|
||||
|
@ -107,8 +107,8 @@
|
||||
|
||||
package rlgl
|
||||
|
||||
import rl "../."
|
||||
import "core:c"
|
||||
import rl "../."
|
||||
|
||||
VERSION :: "5.0"
|
||||
|
||||
@ -259,6 +259,7 @@ VertexBufferIndexType :: c.ushort when GRAPHICS_API_OPENGL_ES2 else c.uint
|
||||
// Dynamic vertex buffers (position + texcoords + colors + indices arrays)
|
||||
VertexBuffer :: struct {
|
||||
elementCount: c.int, // Number of elements in the buffer (QUADS)
|
||||
|
||||
vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
|
||||
texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
|
||||
colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
|
||||
@ -283,6 +284,7 @@ RenderBatch :: struct {
|
||||
bufferCount: c.int, // Number of vertex buffers (multi-buffering support)
|
||||
currentBuffer: c.int, // Current buffer tracking in case of multi-buffering
|
||||
vertexBuffer: [^]VertexBuffer, // Dynamic buffer(s) for vertex data
|
||||
|
||||
draws: [^]DrawCall, // Draw calls array, depends on textureId
|
||||
drawCounter: c.int, // Draw calls counter
|
||||
currentDepth: f32, // Current depth value for next draw
|
||||
@ -362,7 +364,9 @@ foreign lib {
|
||||
Frustum :: proc(left, right, bottom, top, znear, zfar: f64) ---
|
||||
Ortho :: proc(left, right, bottom, top, znear, zfar: f64) ---
|
||||
Viewport :: proc(x, y, width, height: c.int) --- // Set the viewport area
|
||||
SetClipPlanes :: proc(nearPlane, farPlane: f64) --- // Set clip planes
|
||||
SetClipPlanes :: proc(near, far: f64) --- // Set clip planes distances
|
||||
GetCullDistanceNear :: proc() -> f64 --- // Get cull plane distance near
|
||||
GetCullDistanceFar :: proc() -> f64 --- // Get cull plane distance far
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Functions Declaration - Vertex level operations
|
||||
@ -491,7 +495,7 @@ foreign lib {
|
||||
UpdateVertexBufferElements :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update vertex buffer elements with new data
|
||||
UnloadVertexArray :: proc(vaoId: c.uint) ---
|
||||
UnloadVertexBuffer :: proc(vboId: c.uint) ---
|
||||
SetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) ---
|
||||
SetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, offset: c.int) ---
|
||||
SetVertexAttributeDivisor :: proc(index: c.uint, divisor: c.int) ---
|
||||
SetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value
|
||||
DrawVertexArray :: proc(offset: c.int, count: c.int) ---
|
||||
@ -512,7 +516,7 @@ foreign lib {
|
||||
ReadScreenPixels :: proc(width, height: c.int) -> [^]byte --- // Read screen pixel data (color buffer)
|
||||
|
||||
// Framebuffer management (fbo)
|
||||
LoadFramebuffer :: proc(width, height: c.int) -> c.uint --- // Load an empty framebuffer
|
||||
LoadFramebuffer :: proc() -> c.uint --- // Load an empty framebuffer
|
||||
FramebufferAttach :: proc(fboId, texId: c.uint, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer
|
||||
FramebufferComplete :: proc(id: c.uint) -> bool --- // Verify framebuffer is complete
|
||||
UnloadFramebuffer :: proc(id: c.uint) --- // Delete framebuffer from GPU
|
||||
|
BIN
src_assets/test_level.blend
(Stored with Git LFS)
BIN
src_assets/test_level.blend
(Stored with Git LFS)
Binary file not shown.
BIN
src_assets/test_level.blend1
(Stored with Git LFS)
BIN
src_assets/test_level.blend1
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user