Level hot reload working

This commit is contained in:
sergeypdev 2025-07-13 23:09:32 +04:00
parent bb6498a193
commit 8b8e4f9513
26 changed files with 3207 additions and 2702 deletions

Binary file not shown.

BIN
assets/blender/test_level_blend/Plane.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/blender/testblend_blend/Hotel.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -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}))'
)

View File

@ -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

View File

@ -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
View 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})
}

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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,

View File

@ -2,21 +2,18 @@ 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 {
when ODIN_OS == .Windows {
foreign import lib {
"windows/rayguidll.lib" when RAYGUI_SHARED else "windows/raygui.lib",
}
} else when ODIN_OS == .Linux {
} else when ODIN_OS == .Linux {
foreign import lib {
"linux/libraygui.so" when RAYGUI_SHARED else "linux/libraygui.a",
}
} else when ODIN_OS == .Darwin {
} else when ODIN_OS == .Darwin {
when ODIN_ARCH == .arm64 {
foreign import lib {
"macos-arm64/libraygui.dylib" when RAYGUI_SHARED else "macos-arm64/libraygui.a",
@ -26,41 +23,45 @@ when RAYGUI_ENABLED {
"macos/libraygui.dylib" when RAYGUI_SHARED else "macos/libraygui.a",
}
}
} else {
foreign import lib "system:raygui"
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
foreign import lib {
RAYGUI_WASM_LIB,
}
} else {
foreign import lib "system:raygui"
}
RAYGUI_VERSION :: "4.0"
RAYGUI_VERSION :: "4.0"
// Gui control state
GuiState :: enum c.int {
// Gui control state
GuiState :: enum c.int {
STATE_NORMAL = 0,
STATE_FOCUSED,
STATE_PRESSED,
STATE_DISABLED,
}
}
// Gui control text alignment
GuiTextAlignment :: enum c.int {
// Gui control text alignment
GuiTextAlignment :: enum c.int {
TEXT_ALIGN_LEFT = 0,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_RIGHT,
}
}
GuiTextAlignmentVertical :: enum c.int {
GuiTextAlignmentVertical :: enum c.int {
TEXT_ALIGN_TOP = 0,
TEXT_ALIGN_MIDDLE,
TEXT_ALIGN_BOTTOM,
}
}
GuiTextWrapMode :: enum c.int {
GuiTextWrapMode :: enum c.int {
TEXT_WRAP_NONE = 0,
TEXT_WRAP_CHAR,
TEXT_WRAP_WORD,
}
}
// Gui controls
GuiControl :: enum c.int {
// Gui controls
GuiControl :: enum c.int {
// Default -> populates to all controls when set
DEFAULT = 0,
// Basic controls
@ -79,11 +80,11 @@ when RAYGUI_ENABLED {
COLORPICKER,
SCROLLBAR,
STATUSBAR,
}
}
// Gui base properties for every control
// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
GuiControlProperty :: enum c.int {
// Gui base properties for every control
// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
GuiControlProperty :: enum c.int {
BORDER_COLOR_NORMAL = 0,
BASE_COLOR_NORMAL,
TEXT_COLOR_NORMAL,
@ -100,15 +101,15 @@ when RAYGUI_ENABLED {
TEXT_PADDING,
TEXT_ALIGNMENT,
RESERVED,
}
}
// Gui extended properties depend on control
// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
//----------------------------------------------------------------------------------
// Gui extended properties depend on control
// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
//----------------------------------------------------------------------------------
// DEFAULT extended properties
// NOTE: Those properties are common to all controls or global
GuiDefaultProperty :: enum c.int {
// DEFAULT extended properties
// NOTE: Those properties are common to all controls or global
GuiDefaultProperty :: enum c.int {
TEXT_SIZE = 16, // Text size (glyphs max height)
TEXT_SPACING, // Text spacing between glyphs
LINE_COLOR, // Line control color
@ -116,99 +117,99 @@ when RAYGUI_ENABLED {
TEXT_LINE_SPACING, // Text spacing between lines
TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding)
TEXT_WRAP_MODE, // Text wrap-mode inside text bounds
}
}
// Label
//GuiLabelProperty :: enum c.int { }
// Label
//GuiLabelProperty :: enum c.int { }
// Button/Spinner
//GuiButtonProperty :: enum c.int { }
// Button/Spinner
//GuiButtonProperty :: enum c.int { }
// Toggle/ToggleGroup
GuiToggleProperty :: enum c.int {
// Toggle/ToggleGroup
GuiToggleProperty :: enum c.int {
GROUP_PADDING = 16, // ToggleGroup separation between toggles
}
}
// Slider/SliderBar
GuiSliderProperty :: enum c.int {
// Slider/SliderBar
GuiSliderProperty :: enum c.int {
SLIDER_WIDTH = 16, // Slider size of internal bar
SLIDER_PADDING, // Slider/SliderBar internal bar padding
}
}
// ProgressBar
GuiProgressBarProperty :: enum c.int {
// ProgressBar
GuiProgressBarProperty :: enum c.int {
PROGRESS_PADDING = 16, // ProgressBar internal padding
}
}
// ScrollBar
GuiScrollBarProperty :: enum c.int {
// ScrollBar
GuiScrollBarProperty :: enum c.int {
ARROWS_SIZE = 16,
ARROWS_VISIBLE,
SCROLL_SLIDER_PADDING, // (SLIDERBAR, SLIDER_PADDING)
SCROLL_SLIDER_SIZE,
SCROLL_PADDING,
SCROLL_SPEED,
}
}
// CheckBox
GuiCheckBoxProperty :: enum c.int {
// CheckBox
GuiCheckBoxProperty :: enum c.int {
CHECK_PADDING = 16, // CheckBox internal check padding
}
}
// ComboBox
GuiComboBoxProperty :: enum c.int {
// ComboBox
GuiComboBoxProperty :: enum c.int {
COMBO_BUTTON_WIDTH = 16, // ComboBox right button width
COMBO_BUTTON_SPACING, // ComboBox button separation
}
}
// DropdownBox
GuiDropdownBoxProperty :: enum c.int {
// DropdownBox
GuiDropdownBoxProperty :: enum c.int {
ARROW_PADDING = 16, // DropdownBox arrow separation from border and items
DROPDOWN_ITEMS_SPACING, // DropdownBox items separation
}
}
// TextBox/TextBoxMulti/ValueBox/Spinner
GuiTextBoxProperty :: enum c.int {
// TextBox/TextBoxMulti/ValueBox/Spinner
GuiTextBoxProperty :: enum c.int {
TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable
}
}
// Spinner
GuiSpinnerProperty :: enum c.int {
// Spinner
GuiSpinnerProperty :: enum c.int {
SPIN_BUTTON_WIDTH = 16, // Spinner left/right buttons width
SPIN_BUTTON_SPACING, // Spinner buttons separation
}
}
// ListView
GuiListViewProperty :: enum c.int {
// ListView
GuiListViewProperty :: enum c.int {
LIST_ITEMS_HEIGHT = 16, // ListView items height
LIST_ITEMS_SPACING, // ListView items separation
SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
SCROLLBAR_SIDE, // ListView scrollbar side (0-left, 1-right)
}
}
// ColorPicker
GuiColorPickerProperty :: enum c.int {
// ColorPicker
GuiColorPickerProperty :: enum c.int {
COLOR_SELECTOR_SIZE = 16,
HUEBAR_WIDTH, // ColorPicker right hue bar width
HUEBAR_PADDING, // ColorPicker right hue bar separation from panel
HUEBAR_SELECTOR_HEIGHT, // ColorPicker right hue bar selector height
HUEBAR_SELECTOR_OVERFLOW, // ColorPicker right hue bar selector overflow
}
}
SCROLLBAR_LEFT_SIDE :: 0
SCROLLBAR_RIGHT_SIDE :: 1
SCROLLBAR_LEFT_SIDE :: 0
SCROLLBAR_RIGHT_SIDE :: 1
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@(default_calling_convention="c")
foreign lib {
@(default_calling_convention="c")
foreign lib {
// WASM does not have foreign variable declarations.
when ODIN_ARCH != .wasm32 && ODIN_ARCH != .wasm64p32 {
@(link_name="raylib_version") version: cstring
@ -300,12 +301,12 @@ when RAYGUI_ENABLED {
GuiColorPickerHSV :: proc(bounds: Rectangle, text: cstring, colorHsv: ^Vector3) -> c.int --- // Color Picker control that avoids conversion to RGB on each call (multiple color controls)
GuiColorPanelHSV :: proc(bounds: Rectangle, text: cstring, colorHsv: ^Vector3) -> c.int --- // Color Panel control that returns HSV color value, used by GuiColorPickerHSV()
//----------------------------------------------------------------------------------------------------------
}
}
//----------------------------------------------------------------------------------
// Icons enumeration
//----------------------------------------------------------------------------------
GuiIconName :: enum c.int {
//----------------------------------------------------------------------------------
// Icons enumeration
//----------------------------------------------------------------------------------
GuiIconName :: enum c.int {
ICON_NONE = 0,
ICON_FOLDER_FILE_OPEN = 1,
ICON_FILE_SAVE_CLASSIC = 2,
@ -562,5 +563,4 @@ when RAYGUI_ENABLED {
ICON_253 = 253,
ICON_254 = 254,
ICON_255 = 255,
}
}

View File

@ -120,39 +120,39 @@ VERSION_PATCH :: 0
VERSION :: "5.5"
PI :: 3.14159265358979323846
DEG2RAD :: PI / 180.0
RAD2DEG :: 180.0 / PI
DEG2RAD :: PI/180.0
RAD2DEG :: 180.0/PI
// Some Basic Colors
// NOTE: Custom raylib color palette for amazing visuals on WHITE background
LIGHTGRAY :: Color{200, 200, 200, 255} // Light Gray
GRAY :: Color{130, 130, 130, 255} // Gray
DARKGRAY :: Color{80, 80, 80, 255} // Dark Gray
YELLOW :: Color{253, 249, 0, 255} // Yellow
GOLD :: Color{255, 203, 0, 255} // Gold
ORANGE :: Color{255, 161, 0, 255} // Orange
PINK :: Color{255, 109, 194, 255} // Pink
RED :: Color{230, 41, 55, 255} // Red
MAROON :: Color{190, 33, 55, 255} // Maroon
GREEN :: Color{0, 228, 48, 255} // Green
LIME :: Color{0, 158, 47, 255} // Lime
DARKGREEN :: Color{0, 117, 44, 255} // Dark Green
SKYBLUE :: Color{102, 191, 255, 255} // Sky Blue
BLUE :: Color{0, 121, 241, 255} // Blue
DARKBLUE :: Color{0, 82, 172, 255} // Dark Blue
PURPLE :: Color{200, 122, 255, 255} // Purple
VIOLET :: Color{135, 60, 190, 255} // Violet
DARKPURPLE :: Color{112, 31, 126, 255} // Dark Purple
BEIGE :: Color{211, 176, 131, 255} // Beige
BROWN :: Color{127, 106, 79, 255} // Brown
DARKBROWN :: Color{76, 63, 47, 255} // Dark Brown
LIGHTGRAY :: Color{ 200, 200, 200, 255 } // Light Gray
GRAY :: Color{ 130, 130, 130, 255 } // Gray
DARKGRAY :: Color{ 80, 80, 80, 255 } // Dark Gray
YELLOW :: Color{ 253, 249, 0, 255 } // Yellow
GOLD :: Color{ 255, 203, 0, 255 } // Gold
ORANGE :: Color{ 255, 161, 0, 255 } // Orange
PINK :: Color{ 255, 109, 194, 255 } // Pink
RED :: Color{ 230, 41, 55, 255 } // Red
MAROON :: Color{ 190, 33, 55, 255 } // Maroon
GREEN :: Color{ 0, 228, 48, 255 } // Green
LIME :: Color{ 0, 158, 47, 255 } // Lime
DARKGREEN :: Color{ 0, 117, 44, 255 } // Dark Green
SKYBLUE :: Color{ 102, 191, 255, 255 } // Sky Blue
BLUE :: Color{ 0, 121, 241, 255 } // Blue
DARKBLUE :: Color{ 0, 82, 172, 255 } // Dark Blue
PURPLE :: Color{ 200, 122, 255, 255 } // Purple
VIOLET :: Color{ 135, 60, 190, 255 } // Violet
DARKPURPLE :: Color{ 112, 31, 126, 255 } // Dark Purple
BEIGE :: Color{ 211, 176, 131, 255 } // Beige
BROWN :: Color{ 127, 106, 79, 255 } // Brown
DARKBROWN :: Color{ 76, 63, 47, 255 } // Dark Brown
WHITE :: Color{255, 255, 255, 255} // White
BLACK :: Color{0, 0, 0, 255} // Black
BLANK :: Color{0, 0, 0, 0} // Blank (Transparent)
MAGENTA :: Color{255, 0, 255, 255} // Magenta
RAYWHITE :: Color{245, 245, 245, 255} // My own White (raylib logo)
WHITE :: Color{ 255, 255, 255, 255 } // White
BLACK :: Color{ 0, 0, 0, 255 } // Black
BLANK :: Color{ 0, 0, 0, 0 } // Blank (Transparent)
MAGENTA :: Color{ 255, 0, 255, 255 } // Magenta
RAYWHITE :: Color{ 245, 245, 245, 255 } // My own White (raylib logo)
// Vector2 type
Vector2 :: [2]f32
@ -165,7 +165,7 @@ Vector4 :: [4]f32
Quaternion :: quaternion128
// Matrix type (right handed, stored row major)
Matrix :: # row_major matrix[4, 4]f32
Matrix :: #row_major matrix[4, 4]f32
// Color, 4 components, R8G8B8A8 (32bit)
@ -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
@ -327,8 +327,9 @@ BoneInfo :: struct {
}
// Model type
Model :: struct #align (align_of(uintptr)) {
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
}
@ -419,7 +422,7 @@ VrDeviceInfo :: struct {
}
// VR Stereo rendering configuration for simulator
VrStereoConfig :: struct #align (4) {
VrStereoConfig :: struct #align(4) {
projection: [2]Matrix, // VR projection matrices (per eye)
viewOffset: [2]Matrix, // VR view offset matrices (per eye)
leftLensCenter: [2]f32, // VR left lens center
@ -475,7 +478,7 @@ ConfigFlag :: enum c.int {
MSAA_4X_HINT = 5, // Set to try enabling MSAA 4X
INTERLACED_HINT = 16, // Set to try enabling interlaced video format (for V3D)
}
ConfigFlags :: distinct bit_set[ConfigFlag;c.int]
ConfigFlags :: distinct bit_set[ConfigFlag; c.int]
// Trace log level
@ -823,7 +826,18 @@ Gesture :: enum c.uint {
PINCH_IN = 8, // Pinch in gesture
PINCH_OUT = 9, // Pinch out gesture
}
Gestures :: distinct bit_set[Gesture;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 {
@ -850,7 +864,7 @@ NPatchLayout :: enum c.int {
// Callbacks to hook some internal functions
// WARNING: This callbacks are intended for advanced users
TraceLogCallback :: #type proc "c" (logLevel: TraceLogLevel, text: cstring, args: ^c.va_list) // Logging: Redirect trace log messages
LoadFileDataCallback :: #type proc "c" (fileName: cstring, dataSize: ^c.int) -> [^]u8 // FileIO: Load binary data
LoadFileDataCallback :: #type proc "c"(fileName: cstring, dataSize: ^c.int) -> [^]u8 // FileIO: Load binary data
SaveFileDataCallback :: #type proc "c" (fileName: cstring, data: rawptr, dataSize: c.int) -> bool // FileIO: Save binary data
LoadFileTextCallback :: #type proc "c" (fileName: cstring) -> [^]u8 // FileIO: Load text data
SaveFileTextCallback :: #type proc "c" (fileName: cstring, text: cstring) -> bool // FileIO: Save text data
@ -858,7 +872,7 @@ SaveFileTextCallback :: #type proc "c" (fileName: cstring, text: cstring) -> boo
AudioCallback :: #type proc "c" (bufferData: rawptr, frames: c.uint)
@(default_calling_convention = "c")
@(default_calling_convention="c")
foreign lib {
//------------------------------------------------------------------------------------
// Global Variables Definition
@ -987,7 +1001,7 @@ foreign lib {
// Screen-space-related functions
GetScreenToWorldRay :: proc(position: Vector2, camera: Camera) -> Ray --- // Get a ray trace from screen position (i.e mouse)
GetScreenToWorldRayEx :: proc(position: Vector2, camera: Camera, width: c.int, height: c.int) -> Ray --- // Get a ray trace from screen position (i.e mouse) in a viewport
GetScreenToWorldRayEx :: proc(position: Vector2, camera: Camera, width: c.int, height: c.int) ->Ray --- // Get a ray trace from screen position (i.e mouse) in a viewport
GetWorldToScreen :: proc(position: Vector3, camera: Camera) -> Vector2 --- // Get the screen space position for a 3d world space position
GetWorldToScreenEx :: proc(position: Vector3, camera: Camera, width: c.int, height: c.int) -> Vector2 --- // Get size position for a 3d world space position
GetWorldToScreen2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Get the screen space position for a 2d camera world space position
@ -1056,7 +1070,7 @@ foreign lib {
MakeDirectory :: proc(dirPath: cstring) -> c.int --- // Create directories (including full path requested), returns 0 on success
ChangeDirectory :: proc(dir: cstring) -> bool --- // Change working directory, return true on success
IsPathFile :: proc(path: cstring) -> bool --- // Check if a given path is a file or a directory
IsFileNameValid :: proc(fileName: cstring) -> bool --- // Check if fileName is valid for the platform/OS
IsFileNameValid :: proc (fileName: cstring) -> bool --- // Check if fileName is valid for the platform/OS
LoadDirectoryFiles :: proc(dirPath: cstring) -> FilePathList --- // Load directory filepaths
LoadDirectoryFilesEx :: proc(basePath: cstring, filter: cstring, scanSubdirs: bool) -> FilePathList --- // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
UnloadDirectoryFiles :: proc(files: FilePathList) --- // Unload filepaths
@ -1072,7 +1086,7 @@ foreign lib {
EncodeDataBase64 :: proc(data: rawptr, dataSize: c.int, outputSize: ^c.int) -> [^]byte --- // Encode data to Base64 string, memory must be MemFree()
DecodeDataBase64 :: proc(data: rawptr, outputSize: ^c.int) -> [^]byte --- // Decode Base64 string data, memory must be MemFree()
ComputeCRC32 :: proc(data: rawptr, dataSize: c.int) -> c.uint --- // Compute CRC32 hash code
ComputeMD5 :: proc(data: rawptr, dataSize: c.int) -> [^]c.uint --- // Compute MD5 hash code, returns static int[4] (16 bytes)
ComputeMD5 :: proc (data: rawptr, dataSize: c.int) -> [^]c.uint --- // Compute MD5 hash code, returns static int[4] (16 bytes)
ComputeSHA1 :: proc(data: rawptr, dataSize: c.int) -> [^]c.uint --- // Compute SHA1 hash code, returns static int[5] (20 bytes)
@ -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
@ -1370,7 +1402,7 @@ foreign lib {
// Color/pixel related functions
@(deprecated = "Prefer col1 == col2")
@(deprecated="Prefer col1 == col2")
ColorIsEqual :: proc(col1, col2: Color) --- // Check if two colors are equal
Fade :: proc(color: Color, alpha: f32) -> Color --- // Get color with alpha applied, alpha goes from 0.0f to 1.0f
ColorToInt :: proc(color: Color) -> c.uint --- // Get hexadecimal value for a Color (0xRRGGBBAA)
@ -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)
//------------------------------------------------------------------------------------
@ -1663,7 +1697,7 @@ foreign lib {
// Check if a gesture have been detected
IsGestureDetected :: proc "c" (gesture: Gesture) -> bool {
@(default_calling_convention = "c")
@(default_calling_convention="c")
foreign lib {
IsGestureDetected :: proc "c" (gesture: Gestures) -> bool ---
}
@ -1673,15 +1707,15 @@ 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)
index = (index + 1) % MAX_TEXTFORMAT_BUFFERS
index = (index+1)%MAX_TEXTFORMAT_BUFFERS
str := fmt.bprintf(buffer[:len(buffer) - 1], string(text), ..args)
str := fmt.bprintf(buffer[:len(buffer)-1], string(text), ..args)
buffer[len(str)] = 0
return cstring(raw_data(buffer))
@ -1689,20 +1723,20 @@ TextFormat :: proc(text: cstring, args: ..any) -> cstring {
// Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree')
TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring {
return fmt.caprintf(string(text), ..args, allocator = MemAllocator())
return fmt.caprintf(string(text), ..args, allocator=MemAllocator())
}
// Internal memory free
MemFree :: proc {
MemFree :: proc{
MemFreePtr,
MemFreeCstring,
}
@(default_calling_convention = "c")
@(default_calling_convention="c")
foreign lib {
@(link_name = "MemFree")
@(link_name="MemFree")
MemFreePtr :: proc(ptr: rawptr) ---
}
@ -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))

View File

@ -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

View File

@ -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
@ -346,7 +348,7 @@ CullMode :: enum c.int {
Matrix :: rl.Matrix
@(default_calling_convention = "c", link_prefix = "rl")
@(default_calling_convention="c", link_prefix="rl")
foreign lib {
//------------------------------------------------------------------------------------
// Functions Declaration - Matrix operations
@ -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
@ -453,9 +457,9 @@ foreign lib {
// Functions Declaration - rlgl functionality
//------------------------------------------------------------------------------------
// rlgl initialization functions
@(link_prefix = "rlgl")
@(link_prefix="rlgl")
Init :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states)
@(link_prefix = "rlgl")
@(link_prefix="rlgl")
Close :: proc() --- // De-initialize rlgl (buffers, shaders, textures)
LoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function required)
GetVersion :: proc() -> GlVersion --- // Get current OpenGL version
@ -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)

Binary file not shown.

BIN
src_assets/test_level.blend1 (Stored with Git LFS)

Binary file not shown.