Web Build!!!

This commit is contained in:
sergeypdev 2025-05-23 17:42:10 +04:00
parent b855732e29
commit c31ebea3bd
26 changed files with 674 additions and 679 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ atlas.png
pdbs/ pdbs/
game_web/ game_web/
.venv .venv
build/

View File

@ -1,3 +0,0 @@
@echo off
odin build main_release -out:game_debug.exe -strict-style -vet -debug

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
odin build main_release -collection:common=./common -collection:game=./game -collection:libs=./libs -out:game_debug.bin -strict-style -vet -debug

View File

@ -1,78 +0,0 @@
@echo off
set GAME_RUNNING=false
set EXE=game_hot_reload.exe
:: Check if game is running
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF %%x == %EXE% set GAME_RUNNING=true
:: If game isn't running then:
:: - delete all game_XXX.dll files
:: - delete all PDBs in pdbs subdir
:: - optionally create the pdbs subdir
:: - write 0 into pdbs\pdb_number so game.dll PDBs start counting from zero
::
:: This makes sure we start over "fresh" at PDB number 0 when starting up the
:: game and it also makes sure we don't have so many PDBs laying around.
if %GAME_RUNNING% == false (
del /q game_*.dll 2> nul
if exist "pdbs" (
del /q pdbs\*.pdb
) else (
mkdir pdbs
)
echo 0 > pdbs\pdb_number
)
:: Load PDB number from file, increment and store back. For as long as the game
:: is running the pdb_number file won't be reset to 0, so we'll get a PDB of a
:: unique name on each hot reload.
set /p PDB_NUMBER=<pdbs\pdb_number
set /a PDB_NUMBER=%PDB_NUMBER%+1
echo %PDB_NUMBER% > pdbs\pdb_number
:: Build game dll, use pdbs\game_%PDB_NUMBER%.pdb as PDB name so each dll gets
:: its own PDB. This PDB stuff is done in order to make debugging work.
:: Debuggers tend to lock PDBs or just misbehave if you reuse the same PDB while
:: the debugger is attached. So each time we compile `game.dll` we give the
:: PDB a unique PDB.
::
:: Note that we could not just rename the PDB after creation; the DLL contains a
:: reference to where the PDB is.
::
:: Also note that we always write game.dll to the same file. game_hot_reload.exe
:: monitors this file and does the hot reload when it changes.
echo Building game.dll
odin build game -strict-style -vet -debug -define:RAYLIB_SHARED=true -build-mode:dll -out:game.dll -pdb-name:pdbs\game_%PDB_NUMBER%.pdb > nul
IF %ERRORLEVEL% NEQ 0 exit /b 1
:: If game.exe already running: Then only compile game.dll and exit cleanly
if %GAME_RUNNING% == true (
echo Game running, hot reloading... && exit /b 1
)
:: Build game.exe, which starts the program and loads game.dll och does the logic for hot reloading.
echo Building %EXE%
odin build main_hot_reload -strict-style -vet -debug -out:%EXE%
IF %ERRORLEVEL% NEQ 0 exit /b 1
:: Warning about raylib DLL not existing and where to find it.
if exist "raylib.dll" (
exit /b 0
)
:: Don't name this one ODIN_ROOT as the odin exe will start using that one then.
set ODIN_PATH=
for /f %%i in ('odin root') do set "ODIN_PATH=%%i"
if exist "%ODIN_PATH%\vendor\raylib\windows\raylib.dll" (
echo raylib.dll not found in current directory. Copying from %ODIN_PATH%\vendor\raylib\windows\raylib.dll
copy "%ODIN_PATH%\vendor\raylib\windows\raylib.dll" .
exit /b 0
)
echo "Please copy raylib.dll from <your_odin_compiler>/vendor/raylib/windows/raylib.dll to the same directory as game.exe"
exit /b 1

View File

@ -1,52 +0,0 @@
#!/usr/bin/env bash
# NOTE: this is a recent addition to the Odin compiler, if you don't have this command
# you can change this to the path to the Odin folder that contains vendor, eg: "~/Odin".
ROOT=$(odin root)
if [ ! $? -eq 0 ]; then
echo "Your Odin compiler does not have the 'odin root' command, please update or hardcode it in the script."
exit 1
fi
set -eu
# Figure out the mess that is dynamic libraries.
case $(uname) in
"Darwin")
case $(uname -m) in
"arm64") LIB_PATH="macos-arm64" ;;
*) LIB_PATH="macos" ;;
esac
DLL_EXT=".dylib"
EXTRA_LINKER_FLAGS="-Wl,-rpath $ROOT/vendor/raylib/$LIB_PATH"
;;
*)
DLL_EXT=".so"
EXTRA_LINKER_FLAGS="'-Wl,-rpath=\$ORIGIN/linux'"
# Copy the linux libraries into the project automatically.
if [ ! -d "linux" ]; then
mkdir linux
cp -r libs/raylib/src/libraylib*.so* linux
cp -r libs/physfs/libphysfs.so* linux
fi
;;
esac
# Build the game.
echo "Building game$DLL_EXT"
odin build game -extra-linker-flags:"$EXTRA_LINKER_FLAGS" -define:RAYLIB_SHARED=true -define:PHYSFS_SHARED=true -define:TRACY_ENABLE=true -collection:libs=./libs -collection:common=./common -collection:game=./game -build-mode:dll -out:game_tmp$DLL_EXT -strict-style -vet -debug -o:speed
# Need to use a temp file on Linux because it first writes an empty `game.so`, which the game will load before it is actually fully written.
mv game_tmp$DLL_EXT game$DLL_EXT
# Do not build the game_hot_reload.bin if it is already running.
# -f is there to make sure we match against full name, including .bin
if pgrep -f game_hot_reload.bin > /dev/null; then
echo "Game running, hot reloading..."
exit 1
else
echo "Building game_hot_reload.bin"
odin build main_hot_reload -define:TRACY_ENABLE=true -collection:libs=./libs -out:game_hot_reload.bin -strict-style -vet -debug
fi

View File

@ -1,3 +0,0 @@
@echo off
odin build main_release -out:game_release.exe -strict-style -vet -no-bounds-check -o:speed -subsystem:windows

View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
odin build main_release -collection:common=./common -collection:game=./game -out:game_release.bin -strict-style -vet -no-bounds-check -o:speed -debug

View File

@ -1,19 +0,0 @@
@echo off
set EMSCRIPTEN_SDK_DIR=c:\emsdk
set OUT_DIR=game_web
if not exist %OUT_DIR% mkdir %OUT_DIR%
set EMSDK_QUIET=1
call %EMSCRIPTEN_SDK_DIR%\emsdk_env.bat
odin build main_web -target:freestanding_wasm32 -build-mode:obj -define:RAYLIB_WASM_LIB=env.o -vet -strict-style -out:%OUT_DIR%/game
IF %ERRORLEVEL% NEQ 0 exit /b 1
for /f %%i in ('odin root') do set "ODIN_PATH=%%i"
set files=main_web/main_web.c %OUT_DIR%/game.wasm.o %ODIN_PATH%\vendor\raylib\wasm\libraylib.a
set flags=-sUSE_GLFW=3 -sASYNCIFY -sASSERTIONS -DPLATFORM_WEB
set custom=--shell-file main_web/index_template.html
emcc -o %OUT_DIR%/index.html %files% %flags% %custom% && del %OUT_DIR%\game.wasm.o

View File

@ -4,7 +4,9 @@ import "core:flags"
import "core:fmt" import "core:fmt"
import "core:log" import "core:log"
import os "core:os/os2" import os "core:os/os2"
import "core:path/filepath"
import "core:slice" import "core:slice"
import "core:strings"
Build_Variant :: enum { Build_Variant :: enum {
Hot_Reload, Hot_Reload,
@ -20,10 +22,9 @@ Options :: struct {
tracy: bool `usage:"Enable tracy profiler"`, tracy: bool `usage:"Enable tracy profiler"`,
} }
Error :: union #shared_nil { temp_concat :: proc(left, right: []string) -> []string {
Run_Error, result := slice.concatenate([][]string{left, right}, context.temp_allocator)
Copy_Error, return result
os.Error,
} }
build_deps :: proc(opts: Options) { build_deps :: proc(opts: Options) {
@ -39,7 +40,7 @@ build_deps :: proc(opts: Options) {
} }
target := opts.variant == .Web ? "wasm32-emscripten" : "native" target := opts.variant == .Web ? "wasm32-emscripten" : "native"
handle_error(
run_cmd( run_cmd(
{ {
"zig", "zig",
@ -50,7 +51,6 @@ build_deps :: proc(opts: Options) {
fmt.tprintf("-Dtarget=%s", target), fmt.tprintf("-Dtarget=%s", target),
}, },
cwd, cwd,
),
) )
} }
@ -59,16 +59,33 @@ build_deps :: proc(opts: Options) {
cwd := "./libs/physfs" cwd := "./libs/physfs"
file_name := shared ? "libphysfs.so" : "libphysfs.a" file_name := shared ? "libphysfs.so" : "libphysfs.a"
is_built := os.is_file(fmt.tprintf("./libs/physfs/%s", file_name)) is_built := os.is_file(fmt.tprintf("./libs/physfs/%s", file_name))
if is_built && force { if force {
handle_error(run_cmd({"make", "clean"}, cwd)) remove_all("./libs/physfs/build")
} }
prepare_cmd := []string {
"cmake",
"-B",
"build",
fmt.tprintf("-DPHYSFS_BUILD_SHARED=%v", shared),
"-DCMAKE_BUILD_TYPE=MinSizeRel",
"./physfs",
}
build_cmd := []string{"cmake", "--build", "build", "--config", "MinSizeRel"}
if !is_built || force { if !is_built || force {
handle_error(run_cmd({"make", file_name}, cwd)) if opts.variant == .Web {
run_cmd(temp_concat({"emcmake"}, prepare_cmd), cwd)
run_cmd(temp_concat({"emmake"}, build_cmd), cwd)
} else {
run_cmd(prepare_cmd, cwd)
run_cmd(build_cmd, cwd)
}
} }
} }
// Tracy // Tracy
if opts.tracy { if opts.tracy {
assert(opts.variant != .Web)
cwd := "./libs/tracy" cwd := "./libs/tracy"
when ODIN_OS == .Windows { when ODIN_OS == .Windows {
@ -90,7 +107,6 @@ build_deps :: proc(opts: Options) {
} }
if !is_built || force { if !is_built || force {
handle_error(
run_cmd( run_cmd(
slice.concatenate( slice.concatenate(
[][]string { [][]string {
@ -108,11 +124,10 @@ build_deps :: proc(opts: Options) {
context.temp_allocator, context.temp_allocator,
), ),
cwd, cwd,
),
) )
if !shared { if !shared {
handle_error(run_cmd({"zig", "ar", "rc", TRACY_NAME_STATIC, "tracy.o"}, cwd)) run_cmd({"zig", "ar", "rc", TRACY_NAME_STATIC, "tracy.o"}, cwd)
} }
} }
} }
@ -126,6 +141,35 @@ COMMON_FLAGS :: []string {
"-vet", "-vet",
} }
setup_emsdk_env :: proc() {
when ODIN_OS == .Windows {
PATH_ENV_DELIMITER :: ";"
} else {
PATH_ENV_DELIMITER :: ":"
}
PATHS :: [2]string{"./libs/emsdk/upstream/bin", "./libs/emsdk/upstream/emscripten"}
paths: [len(PATHS)]string
for path, i in PATHS {
paths[i] = absolute_path(path)
}
set_env(
"PATH",
strings.join(
{paths[0], paths[1], os.get_env("PATH", context.temp_allocator)},
":",
context.temp_allocator,
),
)
set_env("EMSDK", absolute_path("./libs/emsdk"))
}
archive_assets :: proc() {
}
main :: proc() { main :: proc() {
context.logger = log.create_console_logger() context.logger = log.create_console_logger()
opts := Options { opts := Options {
@ -136,6 +180,8 @@ main :: proc() {
if opts.variant == .Web { if opts.variant == .Web {
log.warnf("tracy is not supported on Web") log.warnf("tracy is not supported on Web")
opts.tracy = false opts.tracy = false
setup_emsdk_env()
} }
tracy_flag: []string = opts.tracy ? {"-define:TRACY_ENABLE=true"} : {} tracy_flag: []string = opts.tracy ? {"-define:TRACY_ENABLE=true"} : {}
@ -144,8 +190,10 @@ main :: proc() {
build_deps(opts) build_deps(opts)
#partial switch opts.variant { switch opts.variant {
case .Hot_Reload: case .Hot_Reload:
remove_all("./build/hotreload")
mkdir_all("./build/hotreload")
cmd := slice.concatenate( cmd := slice.concatenate(
[][]string { [][]string {
[]string { []string {
@ -155,7 +203,7 @@ main :: proc() {
"-define:RAYLIB_SHARED=true", "-define:RAYLIB_SHARED=true",
"-define:PHYSFS_SHARED=true", "-define:PHYSFS_SHARED=true",
"-build-mode:dll", "-build-mode:dll",
"-out:game_tmp.so", "-out:./build/hotreload/game_tmp.so",
}, },
tracy_flag, tracy_flag,
debug_flag, debug_flag,
@ -164,12 +212,14 @@ main :: proc() {
}, },
context.temp_allocator, context.temp_allocator,
) )
handle_error(run_cmd(cmd, ".")) run_cmd(cmd, "")
handle_error(os.rename("game_tmp.so", "game.so")) rename("./build/hotreload/game_tmp.so", "./build/hotreload/game.so")
case .Desktop: case .Desktop:
remove_all("./build/desktop")
mkdir_all("./build/desktop")
cmd := slice.concatenate( cmd := slice.concatenate(
[][]string { [][]string {
[]string{"odin", "build", "main_release", "-out:game.bin"}, []string{"odin", "build", "main_release", "-out:./build/desktop/game"},
tracy_flag, tracy_flag,
debug_flag, debug_flag,
optimize_flag, optimize_flag,
@ -177,17 +227,58 @@ main :: proc() {
}, },
context.temp_allocator, context.temp_allocator,
) )
handle_error(run_cmd(cmd, ".")) run_cmd(cmd, "")
case: case .Web:
remove_all("./build/web")
mkdir_all("./build/web")
odin_root := run_cmd({"odin", "root"}, "")
cmd := slice.concatenate( cmd := slice.concatenate(
[][]string { [][]string {
[]string{"odin", "build", "main_web", "-build-mode:obj", "-out:game_web/game"}, []string {
"odin",
"build",
"main_web",
"-target:js_wasm32",
"-build-mode:obj",
"-out:main_web/game",
},
COMMON_FLAGS, COMMON_FLAGS,
debug_flag, debug_flag,
optimize_flag, optimize_flag,
}, },
) )
run_cmd(cmd, "")
run_cmd(
{
"emcc",
"-g",
"-o",
"build/web/index.html",
"main_web/game.wasm.o",
"libs/raylib/zig-out-static/lib/libraylib.a",
"libs/physfs/build/libphysfs.a",
"-sUSE_GLFW=3",
"-sWASM_BIGINT",
"-sWARN_ON_UNDEFINED_SYMBOLS=0",
"-sALLOW_MEMORY_GROWTH",
"-sASSERTIONS",
"--shell-file",
"main_web/index_template.html",
"--preload-file",
"assets",
},
"",
)
handle_error(run_cmd(cmd, ".")) copy_file(
filepath.join(
{odin_root, "core", "sys", "wasm", "js", "odin.js"},
context.temp_allocator,
),
"./build/web/odin.js",
)
} }
} }

View File

@ -1,11 +1,14 @@
package builder package builder
import "base:intrinsics" import "base:intrinsics"
import "core:bufio"
import "core:fmt" import "core:fmt"
import "core:io"
import "core:log" import "core:log"
import os "core:os/os2" import os "core:os/os2"
import "core:path/filepath" import "core:path/filepath"
import "core:strings" import "core:strings"
import "core:thread"
Process_Error :: enum { Process_Error :: enum {
OK, OK,
@ -18,37 +21,104 @@ Run_Error :: union #shared_nil {
os.Error, os.Error,
} }
@(require_results) run_cmd_internal :: proc(
run_cmd :: proc(cmd: []string, cwd: string, loc := #caller_location) -> Run_Error { cmd: []string,
cwd: string,
loc := #caller_location,
) -> (
result: string,
err: Run_Error,
) {
if len(cwd) > 0 {
log.infof( log.infof(
"running [%s]: %s", "running [%s]: %s",
cwd, cwd,
strings.join(cmd, " ", context.temp_allocator), strings.join(cmd, " ", context.temp_allocator),
location = loc, location = loc,
) )
} else {
log.infof("running: %s", strings.join(cmd, " ", context.temp_allocator), location = loc)
}
r, w := os.pipe() or_return
defer os.close(r)
desc := os.Process_Desc { desc := os.Process_Desc {
command = cmd, command = cmd,
working_dir = cwd, working_dir = cwd,
stderr = os.stderr, stderr = os.stderr,
stdout = os.stdout, stdout = w,
} }
Read_Context :: struct {
r: ^os.File,
result: ^string,
}
read_ctx := Read_Context {
r = r,
result = &result,
}
thread_ctx := context
thread_ctx.user_ptr = &read_ctx
read_thread := thread.create_and_start(
proc() {
ctx := cast(^Read_Context)context.user_ptr
builder: strings.Builder
string_writer := strings.to_writer(&builder)
defer io.destroy(string_writer)
mw: io.Multi_Writer
w := io.multi_writer_init(&mw, string_writer, os.to_writer(os.stderr))
tee: io.Tee_Reader
reader := io.tee_reader_init(&tee, os.to_reader(ctx.r), w)
buf: [1024]u8
err: io.Error
for {
n: int
n, err = io.read(reader, buf[:], nil)
if err == .EOF {
break
}
handle_error(err)
}
// NOTE: mem leak, but who cares
ctx.result^ = strings.clone(strings.to_string(builder))
},
thread_ctx,
)
process := os.process_start(desc) or_return process := os.process_start(desc) or_return
os.close(w)
state := os.process_wait(process) or_return state := os.process_wait(process) or_return
thread.join(read_thread)
if !state.success { if !state.success {
return .Crash return "", .Crash
} }
if state.exit_code != 0 { if state.exit_code != 0 {
return .Invalid_Exit_Code return "", .Invalid_Exit_Code
} }
return nil return result, nil
} }
Copy_Error :: union #shared_nil { run_cmd :: proc(
os.Error, cmd: []string,
filepath.Match_Error, cwd: string,
expr := #caller_expression,
loc := #caller_location,
) -> string {
result, err := run_cmd_internal(cmd, cwd, loc)
handle_error(err, "", expr, loc)
return result
} }
handle_error :: proc( handle_error :: proc(
@ -78,3 +148,35 @@ remove_all :: proc(path: string, expr := #caller_expression, loc := #caller_loca
handle_error(err, fmt.tprintf("failed to remove %s", path), expr = expr, loc = loc) handle_error(err, fmt.tprintf("failed to remove %s", path), expr = expr, loc = loc)
} }
} }
copy_file :: proc(src, dst: string, expr := #caller_expression, loc := #caller_location) {
log.infof("cp %s %s", src, dst)
handle_error(os.copy_file(dst, src), "", expr, loc)
}
rename :: proc(src, dst: string, expr := #caller_expression, loc := #caller_location) {
log.infof("rename %s -> %s", src, dst)
handle_error(os.rename(src, dst), "", expr, loc)
}
mkdir_all :: proc(path: string, expr := #caller_expression, loc := #caller_location) {
log.infof("mkdir -p %s", path)
handle_error(os.mkdir_all(path), "", expr, loc)
}
absolute_path :: proc(
path: string,
allocator := context.temp_allocator,
expr := #caller_expression,
loc := #caller_location,
) -> string {
abs_path, err := os.get_absolute_path(path, allocator)
handle_error(err, "", expr, loc)
return abs_path
}
set_env :: proc(env, val: string) {
log.infof("set_env(%s, %s)", env, val)
handle_error(os.set_env(env, val))
}

View File

@ -1,6 +1,5 @@
package assets package assets
import "core:c"
import "core:log" import "core:log"
import "core:math" import "core:math"
import lg "core:math/linalg" import lg "core:math/linalg"
@ -21,7 +20,7 @@ Loaded_BVH :: struct {
aabb: bvh.AABB, aabb: bvh.AABB,
// BVH for each mesh in a model // BVH for each mesh in a model
bvhs: []bvh.BVH, bvhs: []bvh.BVH,
modtime: c.long, modtime: physfs.sint64,
} }
Loaded_Convex :: struct { Loaded_Convex :: struct {
@ -198,7 +197,7 @@ assetcache_fetch_or_load :: proc(
payload: Asset_Cache_Loader_Payload = nil, payload: Asset_Cache_Loader_Payload = nil,
) -> ( ) -> (
value: E, value: E,
modtime: i64, modtime: physfs.sint64,
result: Asset_Cache_Result, result: Asset_Cache_Result,
) { ) {
tracy.Zone() tracy.Zone()
@ -275,10 +274,10 @@ get_texture :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Texture2D {
get_model_ex :: proc( get_model_ex :: proc(
assetman: ^Asset_Manager, assetman: ^Asset_Manager,
path: cstring, path: cstring,
ref_modtime: c.long = 0, // will check reload status using reference load time. When 0 reloaded will be true only if this call triggered reload ref_modtime: physfs.sint64 = 0, // will check reload status using reference load time. When 0 reloaded will be true only if this call triggered reload
) -> ( ) -> (
model: rl.Model, model: rl.Model,
modtime: c.long, modtime: physfs.sint64,
reloaded: bool, reloaded: bool,
) { ) {
tracy.Zone() tracy.Zone()

View File

@ -1011,25 +1011,25 @@ draw_world :: proc(world: ^World) {
car_matrix = car_matrix =
(auto_cast linalg.matrix4_translate_f32(physics.body_get_shape_pos(car_body))) * car_matrix (auto_cast linalg.matrix4_translate_f32(physics.body_get_shape_pos(car_body))) * car_matrix
basic_shader := assets.get_shader( // basic_shader := assets.get_shader(
&g_mem.assetman, // &g_mem.assetman,
"assets/shaders/lit_vs.glsl", // "assets/shaders/lit_vs.glsl",
"assets/shaders/lit_ps.glsl", // "assets/shaders/lit_ps.glsl",
{.Ambient, .LightDir, .LightColor}, // {.Ambient, .LightDir, .LightColor},
) // )
light_dir := linalg.normalize(rl.Vector3{1, -1, 0}) // light_dir := linalg.normalize(rl.Vector3{1, -1, 0})
ambient := rl.Vector3{0.1, 0.1, 0.1} // ambient := rl.Vector3{0.1, 0.1, 0.1}
light_color := rl.Vector3{0.816, 0.855, 0.89} // light_color := rl.Vector3{0.816, 0.855, 0.89}
rl.SetShaderValue(basic_shader.shader, basic_shader.locations[.LightDir], &light_dir, .VEC3) // rl.SetShaderValue(basic_shader.shader, basic_shader.locations[.LightDir], &light_dir, .VEC3)
rl.SetShaderValue(basic_shader.shader, basic_shader.locations[.Ambient], &ambient, .VEC3) // rl.SetShaderValue(basic_shader.shader, basic_shader.locations[.Ambient], &ambient, .VEC3)
rl.SetShaderValue( // rl.SetShaderValue(
basic_shader.shader, // basic_shader.shader,
basic_shader.locations[.LightColor], // basic_shader.locations[.LightColor],
&light_color, // &light_color,
.VEC3, // .VEC3,
) // )
render.draw_model(car_model, basic_shader.shader, car_matrix) render.draw_model(car_model, {}, car_matrix)
render.draw_mesh_light( render.draw_mesh_light(
assets.get_model(&g_mem.assetman, "assets/ae86_lights.glb"), assets.get_model(&g_mem.assetman, "assets/ae86_lights.glb"),
@ -1262,7 +1262,14 @@ game_update :: proc() -> bool {
update() update()
draw() draw()
return !rl.WindowShouldClose()
when ODIN_OS != .JS {
// Never run this proc in browser. It contains a 16 ms sleep on web!
if rl.WindowShouldClose() {
return true
}
}
return true
} }
@(export) @(export)

View File

@ -138,7 +138,7 @@ draw_debug_scene :: proc(scene: ^Scene) {
} }
} }
if true { if false {
for &contact, contact_idx in sim_state.contact_container.contacts { for &contact, contact_idx in sim_state.contact_container.contacts {
points_a := contact.manifold.points_a points_a := contact.manifold.points_a
points_b := contact.manifold.points_b points_b := contact.manifold.points_b

View File

@ -30,9 +30,9 @@ draw_model :: proc(
color: rl.Color = rl.WHITE, color: rl.Color = rl.WHITE,
) { ) {
model := model model := model
for i in 0 ..< model.materialCount { // for i in 0 ..< model.materialCount {
model.materials[i].shader = shader // model.materials[i].shader = shader
} // }
model.transform = transform model.transform = transform
rl.DrawModel(model, rl.Vector3{}, 1, color) rl.DrawModel(model, rl.Vector3{}, 1, color)
@ -42,7 +42,7 @@ draw_mesh_light :: proc(model: rl.Model, transform: rl.Matrix, color: rl.Color)
model := model model := model
model.transform = transform model.transform = transform
light_shader := assets.get_shader(assetman, nil, "assets/shaders/light_ps.glsl", {}).shader // light_shader := assets.get_shader(assetman, nil, "assets/shaders/light_ps.glsl", {}).shader
rlgl.DrawRenderBatchActive() rlgl.DrawRenderBatchActive()
@ -56,7 +56,7 @@ draw_mesh_light :: proc(model: rl.Model, transform: rl.Matrix, color: rl.Color)
gl.ColorMask(false, false, false, false) gl.ColorMask(false, false, false, false)
rlgl.SetCullFace(.FRONT) rlgl.SetCullFace(.FRONT)
draw_model(model, light_shader, transform, color) draw_model(model, {}, transform, color)
rlgl.DrawRenderBatchActive() rlgl.DrawRenderBatchActive()
} }
@ -68,7 +68,7 @@ draw_mesh_light :: proc(model: rl.Model, transform: rl.Matrix, color: rl.Color)
gl.ColorMask(true, true, true, true) gl.ColorMask(true, true, true, true)
rlgl.SetCullFace(.BACK) rlgl.SetCullFace(.BACK)
draw_model(model, light_shader, transform, color) draw_model(model, {}, transform, color)
rlgl.DrawRenderBatchActive() rlgl.DrawRenderBatchActive()
} }
@ -78,7 +78,7 @@ draw_mesh_light :: proc(model: rl.Model, transform: rl.Matrix, color: rl.Color)
gl.DepthFunc(gl.GREATER) gl.DepthFunc(gl.GREATER)
rlgl.SetCullFace(.FRONT) rlgl.SetCullFace(.FRONT)
draw_model(model, light_shader, transform, color) draw_model(model, {}, transform, color)
rlgl.DrawRenderBatchActive() rlgl.DrawRenderBatchActive()
} }

View File

@ -29,7 +29,7 @@ rl_init :: proc() {
rl.SetTextureFilter(default_atlas_texture, .POINT) rl.SetTextureFilter(default_atlas_texture, .POINT)
gl.BindTexture(gl.TEXTURE_2D, default_atlas_texture.id) gl.BindTexture(gl.TEXTURE_2D, default_atlas_texture.id)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_SWIZZLE_A, gl.RED) // gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_SWIZZLE_A, gl.RED)
gl.BindTexture(gl.TEXTURE_2D, 0) gl.BindTexture(gl.TEXTURE_2D, 0)
} }

View File

@ -10,6 +10,8 @@ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
} else { } else {
foreign import lib "libphysfs.a" foreign import lib "libphysfs.a"
} }
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
foreign import lib "env.o"
} }
@(default_calling_convention = "c", link_prefix = "PHYSFS_") @(default_calling_convention = "c", link_prefix = "PHYSFS_")

View File

@ -109,7 +109,7 @@ when ODIN_OS == .Windows {
} else when ODIN_OS == .Darwin { } else when ODIN_OS == .Darwin {
foreign import lib {"zig-out-shared/lib/libraylib.dylib" when RAYLIB_SHARED else "zig-out-static/lib/libraylib.a", "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework"} foreign import lib {"zig-out-shared/lib/libraylib.dylib" when RAYLIB_SHARED else "zig-out-static/lib/libraylib.a", "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework"}
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { } else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
foreign import lib "zig-out-static/lib/libraylib.a" foreign import lib "env.o"
} else { } else {
foreign import lib "system:raylib" foreign import lib "system:raylib"
} }

View File

@ -126,6 +126,8 @@ when ODIN_OS == .Windows {
foreign import lib {"../zig-out-shared/lib/libraylib.so" when RAYLIB_SHARED else "../zig-out-static/lib/libraylib.a", "system:dl", "system:pthread"} // Note(bumbread): I'm not sure why in `linux/` folder there are// multiple copies of raylib.so, but since these bindings are for// particular version of the library, I better specify it. Ideally,// though, it's best specified in terms of major (.so.4) foreign import lib {"../zig-out-shared/lib/libraylib.so" when RAYLIB_SHARED else "../zig-out-static/lib/libraylib.a", "system:dl", "system:pthread"} // Note(bumbread): I'm not sure why in `linux/` folder there are// multiple copies of raylib.so, but since these bindings are for// particular version of the library, I better specify it. Ideally,// though, it's best specified in terms of major (.so.4)
} else when ODIN_OS == .Darwin { } else when ODIN_OS == .Darwin {
foreign import lib {"../zig-out-shared/lib/libraylib.dylib" when RAYLIB_SHARED else "../zig-out-static/lib/libraylib.a", "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework"} foreign import lib {"../zig-out-shared/lib/libraylib.dylib" when RAYLIB_SHARED else "../zig-out-static/lib/libraylib.a", "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework"}
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
foreign import lib "env.o"
} else { } else {
foreign import lib "system:raylib" foreign import lib "system:raylib"
} }

View File

@ -3,6 +3,8 @@ package tracy
import "core:c" import "core:c"
import "core:mem" import "core:mem"
_ :: c
ProfiledAllocatorData :: struct { ProfiledAllocatorData :: struct {
backing_allocator: mem.Allocator, backing_allocator: mem.Allocator,
profiled_allocator: mem.Allocator, profiled_allocator: mem.Allocator,
@ -14,16 +16,35 @@ MakeProfiledAllocator :: proc(
self: ^ProfiledAllocatorData, self: ^ProfiledAllocatorData,
callstack_size: i32 = TRACY_CALLSTACK, callstack_size: i32 = TRACY_CALLSTACK,
secure: b32 = false, secure: b32 = false,
backing_allocator := context.allocator) -> mem.Allocator { backing_allocator := context.allocator,
) -> mem.Allocator {
self.callstack_size = callstack_size self.callstack_size = callstack_size
self.secure = secure self.secure = secure
self.backing_allocator = backing_allocator self.backing_allocator = backing_allocator
self.profiled_allocator = mem.Allocator { self.profiled_allocator = mem.Allocator {
data = self, data = self,
procedure = proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, mem.Allocator_Error) { procedure = proc(
allocator_data: rawptr,
mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr,
old_size: int,
location := #caller_location,
) -> (
[]byte,
mem.Allocator_Error,
) {
using self := cast(^ProfiledAllocatorData)allocator_data using self := cast(^ProfiledAllocatorData)allocator_data
new_memory, error := self.backing_allocator.procedure(self.backing_allocator.data, mode, size, alignment, old_memory, old_size, location) new_memory, error := self.backing_allocator.procedure(
self.backing_allocator.data,
mode,
size,
alignment,
old_memory,
old_size,
location,
)
if error == .None { if error == .None {
switch mode { switch mode {
case .Alloc, .Alloc_Non_Zeroed: case .Alloc, .Alloc_Non_Zeroed:
@ -51,18 +72,27 @@ MakeProfiledAllocator :: proc(
EmitAlloc :: #force_inline proc(new_memory: []byte, size: int, callstack_size: i32, secure: b32) { EmitAlloc :: #force_inline proc(new_memory: []byte, size: int, callstack_size: i32, secure: b32) {
when TRACY_HAS_CALLSTACK { when TRACY_HAS_CALLSTACK {
if callstack_size > 0 { if callstack_size > 0 {
___tracy_emit_memory_alloc_callstack(raw_data(new_memory), c.size_t(size), callstack_size, secure) ___tracy_emit_memory_alloc_callstack(
raw_data(new_memory),
c.size_t(size),
callstack_size,
secure,
)
} else { } else {
___tracy_emit_memory_alloc(raw_data(new_memory), c.size_t(size), secure) ___tracy_emit_memory_alloc(raw_data(new_memory), c.size_t(size), secure)
} }
} else { } else {
when TRACY_ENABLE {
___tracy_emit_memory_alloc(raw_data(new_memory), c.size_t(size), secure) ___tracy_emit_memory_alloc(raw_data(new_memory), c.size_t(size), secure)
} }
} }
}
@(private = "file") @(private = "file")
EmitFree :: #force_inline proc(old_memory: rawptr, callstack_size: i32, secure: b32) { EmitFree :: #force_inline proc(old_memory: rawptr, callstack_size: i32, secure: b32) {
when TRACY_ENABLE {
if old_memory == nil {return} if old_memory == nil {return}
when TRACY_HAS_CALLSTACK { when TRACY_HAS_CALLSTACK {
if callstack_size > 0 { if callstack_size > 0 {
___tracy_emit_memory_free_callstack(old_memory, callstack_size, secure) ___tracy_emit_memory_free_callstack(old_memory, callstack_size, secure)
@ -73,5 +103,4 @@ EmitFree :: #force_inline proc(old_memory: rawptr, callstack_size: i32, secure:
___tracy_emit_memory_free(old_memory, secure) ___tracy_emit_memory_free(old_memory, secure)
} }
} }
}

View File

@ -2,9 +2,13 @@ package tracy
import "core:c" import "core:c"
_ :: c
when TRACY_ENABLE {
when ODIN_OS == .Darwin do foreign import tracy "tracy.dylib" when ODIN_OS == .Darwin do foreign import tracy "tracy.dylib"
when ODIN_OS == .Windows do foreign import tracy "tracy.lib" when ODIN_OS == .Windows do foreign import tracy "tracy.lib"
when ODIN_OS == .Linux do foreign import tracy "tracy.so" when ODIN_OS == .Linux do foreign import tracy "tracy.so"
}
TracyPlotFormatEnum :: enum i32 { TracyPlotFormatEnum :: enum i32 {
TracyPlotFormatNumber, TracyPlotFormatNumber,
@ -77,6 +81,7 @@ ___tracy_gpu_time_sync_data :: struct {
__tracy_lockable_context_data :: struct {} // NOTE(oskar): opaque __tracy_lockable_context_data :: struct {} // NOTE(oskar): opaque
when TRACY_ENABLE {
when #config(TRACY_MANUAL_LIFETIME, false) { when #config(TRACY_MANUAL_LIFETIME, false) {
@(default_calling_convention = "c") @(default_calling_convention = "c")
foreign tracy { foreign tracy {
@ -85,7 +90,6 @@ when #config(TRACY_MANUAL_LIFETIME, false) {
___tracy_profiler_started :: proc() -> b32 --- ___tracy_profiler_started :: proc() -> b32 ---
} }
} }
@(default_calling_convention = "c") @(default_calling_convention = "c")
foreign tracy { foreign tracy {
___tracy_set_thread_name :: proc(name: cstring) --- ___tracy_set_thread_name :: proc(name: cstring) ---
@ -169,3 +173,4 @@ when #config(TRACY_FIBERS, false) {
___tracy_fiber_leave :: proc() --- ___tracy_fiber_leave :: proc() ---
} }
} }
}

View File

@ -2,6 +2,8 @@ package tracy
import "core:c" import "core:c"
_ :: c
TRACY_ENABLE :: #config(TRACY_ENABLE, false) TRACY_ENABLE :: #config(TRACY_ENABLE, false)
TRACY_CALLSTACK :: #config(TRACY_CALLSTACK, 1) TRACY_CALLSTACK :: #config(TRACY_CALLSTACK, 1)
TRACY_HAS_CALLSTACK :: #config(TRACY_HAS_CALLSTACK, false) TRACY_HAS_CALLSTACK :: #config(TRACY_HAS_CALLSTACK, false)
@ -62,19 +64,25 @@ ZoneCS :: ZoneC
ZoneNCS :: ZoneNC ZoneNCS :: ZoneNC
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
ZoneText :: #force_inline proc(ctx: ZoneCtx, text: string) {___tracy_emit_zone_text( ZoneText :: #force_inline proc(
ctx, ctx: ZoneCtx,
_sl(text), text: string,
)} ) {when TRACY_ENABLE {___tracy_emit_zone_text(ctx, _sl(text))}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
ZoneName :: #force_inline proc(ctx: ZoneCtx, name: string) {___tracy_emit_zone_name( ZoneName :: #force_inline proc(
ctx, ctx: ZoneCtx,
_sl(name), name: string,
)} ) {when TRACY_ENABLE {___tracy_emit_zone_name(ctx, _sl(name))}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
ZoneColor :: #force_inline proc(ctx: ZoneCtx, color: u32) {___tracy_emit_zone_color(ctx, color)} ZoneColor :: #force_inline proc(
ctx: ZoneCtx,
color: u32,
) {when TRACY_ENABLE {___tracy_emit_zone_color(ctx, color)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
ZoneValue :: #force_inline proc(ctx: ZoneCtx, value: u64) {___tracy_emit_zone_value(ctx, value)} ZoneValue :: #force_inline proc(
ctx: ZoneCtx,
value: u64,
) {when TRACY_ENABLE {___tracy_emit_zone_value(ctx, value)}}
// NOTE: scoped Zone*() procs also exists, no need of calling this directly. // NOTE: scoped Zone*() procs also exists, no need of calling this directly.
ZoneBegin :: proc(active: bool, depth: i32, loc := #caller_location) -> (ctx: ZoneCtx) { ZoneBegin :: proc(active: bool, depth: i32, loc := #caller_location) -> (ctx: ZoneCtx) {
@ -98,7 +106,7 @@ ZoneBegin :: proc(active: bool, depth: i32, loc := #caller_location) -> (ctx: Zo
// NOTE: scoped Zone*() procs also exists, no need of calling this directly. // NOTE: scoped Zone*() procs also exists, no need of calling this directly.
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
ZoneEnd :: #force_inline proc(ctx: ZoneCtx) {___tracy_emit_zone_end(ctx)} ZoneEnd :: #force_inline proc(ctx: ZoneCtx) {when TRACY_ENABLE {___tracy_emit_zone_end(ctx)}}
// Memory profiling // Memory profiling
// (See allocator.odin for an implementation of an Odin custom allocator using memory profiling.) // (See allocator.odin for an implementation of an Odin custom allocator using memory profiling.)
@ -107,89 +115,91 @@ Alloc :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
size: c.size_t, size: c.size_t,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack(
ptr, ptr,
size, size,
depth, depth,
false, false,
)} else {___tracy_emit_memory_alloc(ptr, size, false)}} )} else {___tracy_emit_memory_alloc(ptr, size, false)}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
Free :: #force_inline proc( Free :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack(
ptr, ptr,
depth, depth,
false, false,
)} else {___tracy_emit_memory_free(ptr, false)}} )} else {___tracy_emit_memory_free(ptr, false)}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
SecureAlloc :: #force_inline proc( SecureAlloc :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
size: c.size_t, size: c.size_t,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack(
ptr, ptr,
size, size,
depth, depth,
true, true,
)} else {___tracy_emit_memory_alloc(ptr, size, true)}} )} else {___tracy_emit_memory_alloc(ptr, size, true)}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
SecureFree :: #force_inline proc( SecureFree :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack(
ptr, ptr,
depth, depth,
true, true,
)} else {___tracy_emit_memory_free(ptr, true)}} )} else {___tracy_emit_memory_free(ptr, true)}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
AllocN :: #force_inline proc( AllocN :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
size: c.size_t, size: c.size_t,
name: cstring, name: cstring,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack_named( ) {
ptr, when TRACY_ENABLE {
size, when TRACY_HAS_CALLSTACK {
depth, ___tracy_emit_memory_alloc_callstack_named(ptr, size, depth, false, name)
false, } else {
name, __tracy_emit_memory_alloc_named(ptr, size, false, name)
)} else {___tracy_emit_memory_alloc_named(ptr, size, false, name)}} }
}
}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
FreeN :: #force_inline proc( FreeN :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
name: cstring, name: cstring,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack_named( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack_named(
ptr, ptr,
depth, depth,
false, false,
name, name,
)} else {___tracy_emit_memory_free_named(ptr, false, name)}} )} else {___tracy_emit_memory_free_named(ptr, false, name)}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
SecureAllocN :: #force_inline proc( SecureAllocN :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
size: c.size_t, size: c.size_t,
name: cstring, name: cstring,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack_named( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_alloc_callstack_named(
ptr, ptr,
size, size,
depth, depth,
true, true,
name, name,
)} else {___tracy_emit_memory_alloc_named(ptr, size, true, name)}} )} else {when TRACY_ENABLE {___tracy_emit_memory_alloc_named(ptr, size, true, name)}}}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
SecureFreeN :: #force_inline proc( SecureFreeN :: #force_inline proc(
ptr: rawptr, ptr: rawptr,
name: cstring, name: cstring,
depth: i32 = TRACY_CALLSTACK, depth: i32 = TRACY_CALLSTACK,
) {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack_named( ) {when TRACY_ENABLE {when TRACY_HAS_CALLSTACK {___tracy_emit_memory_free_callstack_named(
ptr, ptr,
depth, depth,
true, true,
name, name,
)} else {___tracy_emit_memory_free_named(ptr, true, name)}} )} else {___tracy_emit_memory_free_named(ptr, true, name)}}}
// Dummy aliases to match C API (only difference is the `depth` parameter, // Dummy aliases to match C API (only difference is the `depth` parameter,
// which we declare as optional for the non-S procs.) // which we declare as optional for the non-S procs.)
@ -204,48 +214,67 @@ SecureFreeNS :: SecureFreeN
// Frame markup // Frame markup
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
FrameMark :: #force_inline proc(name: cstring = nil) {___tracy_emit_frame_mark(name)} FrameMark :: #force_inline proc(name: cstring = nil) {when TRACY_ENABLE {___tracy_emit_frame_mark(
name,
)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
FrameMarkStart :: #force_inline proc(name: cstring) {___tracy_emit_frame_mark_start(name)} FrameMarkStart :: #force_inline proc(
name: cstring,
) {when TRACY_ENABLE {___tracy_emit_frame_mark_start(name)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
FrameMarkEnd :: #force_inline proc(name: cstring) {___tracy_emit_frame_mark_end(name)} FrameMarkEnd :: #force_inline proc(
name: cstring,
) {when TRACY_ENABLE {___tracy_emit_frame_mark_end(name)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
FrameImage :: #force_inline proc( FrameImage :: #force_inline proc(
image: rawptr, image: rawptr,
w, h: u16, w, h: u16,
offset: u8, offset: u8,
flip: b32, flip: b32,
) {___tracy_emit_frame_image(image, w, h, offset, flip)} ) {when TRACY_ENABLE {___tracy_emit_frame_image(image, w, h, offset, flip)}}
// Plots and messages // Plots and messages
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
Plot :: #force_inline proc(name: cstring, value: f64) {___tracy_emit_plot(name, value)} Plot :: #force_inline proc(name: cstring, value: f64) {when TRACY_ENABLE {___tracy_emit_plot(
name,
value,
)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
PlotF :: #force_inline proc(name: cstring, value: f32) {___tracy_emit_plot_float(name, value)} PlotF :: #force_inline proc(
name: cstring,
value: f32,
) {when TRACY_ENABLE {___tracy_emit_plot_float(name, value)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
PlotI :: #force_inline proc(name: cstring, value: i64) {___tracy_emit_plot_int(name, value)} PlotI :: #force_inline proc(name: cstring, value: i64) {when TRACY_ENABLE {___tracy_emit_plot_int(
name,
value,
)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
PlotConfig :: #force_inline proc( PlotConfig :: #force_inline proc(
name: cstring, name: cstring,
type: TracyPlotFormatEnum, type: TracyPlotFormatEnum,
step, fill: b32, step, fill: b32,
color: u32, color: u32,
) {___tracy_emit_plot_config(name, type, step, fill, color)} ) {when TRACY_ENABLE {___tracy_emit_plot_config(name, type, step, fill, color)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
Message :: #force_inline proc(txt: string) {___tracy_emit_message( Message :: #force_inline proc(txt: string) {when TRACY_ENABLE {___tracy_emit_message(
_sl(txt), _sl(txt),
TRACY_CALLSTACK when TRACY_HAS_CALLSTACK else 0, TRACY_CALLSTACK when TRACY_HAS_CALLSTACK else 0,
)} )}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
MessageC :: #force_inline proc(txt: string, color: u32) {___tracy_emit_message( MessageC :: #force_inline proc(txt: string, color: u32) {when TRACY_ENABLE {___tracy_emit_message(
_sl(txt), _sl(txt),
TRACY_CALLSTACK when TRACY_HAS_CALLSTACK else 0, TRACY_CALLSTACK when TRACY_HAS_CALLSTACK else 0,
)} )}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
AppInfo :: #force_inline proc(name: string) {___tracy_emit_message_appinfo(_sl(name))} AppInfo :: #force_inline proc(name: string) {when TRACY_ENABLE {___tracy_emit_message_appinfo(
_sl(name),
)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
SetThreadName :: #force_inline proc(name: cstring) {___tracy_set_thread_name(name)} SetThreadName :: #force_inline proc(name: cstring) {when TRACY_ENABLE {___tracy_set_thread_name(
name,
)}}
// Connection status // Connection status
IsConnected :: #force_inline proc() -> bool {return( IsConnected :: #force_inline proc() -> bool {return(
@ -299,28 +328,38 @@ LockAnnounce :: #force_inline proc(loc := #caller_location) -> (ctx: LockCtx) {
return return
} }
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockTerminate :: #force_inline proc(lock: LockCtx) {___tracy_terminate_lockable_ctx(lock)} LockTerminate :: #force_inline proc(
lock: LockCtx,
) {when TRACY_ENABLE {___tracy_terminate_lockable_ctx(lock)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockBeforeLock :: #force_inline proc(lock: LockCtx) {___tracy_before_lock_lockable_ctx(lock)} LockBeforeLock :: #force_inline proc(
lock: LockCtx,
) {when TRACY_ENABLE {___tracy_before_lock_lockable_ctx(lock)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockAfterLock :: #force_inline proc(lock: LockCtx) {___tracy_after_lock_lockable_ctx(lock)} LockAfterLock :: #force_inline proc(
lock: LockCtx,
) {when TRACY_ENABLE {___tracy_after_lock_lockable_ctx(lock)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockAfterUnlock :: #force_inline proc(lock: LockCtx) {___tracy_after_unlock_lockable_ctx(lock)} LockAfterUnlock :: #force_inline proc(
lock: LockCtx,
) {when TRACY_ENABLE {___tracy_after_unlock_lockable_ctx(lock)}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockAfterTryLock :: #force_inline proc( LockAfterTryLock :: #force_inline proc(
lock: LockCtx, lock: LockCtx,
acquired: bool, acquired: bool,
) {___tracy_after_try_lock_lockable_ctx(lock, b32(acquired))} ) {when TRACY_ENABLE {___tracy_after_try_lock_lockable_ctx(lock, b32(acquired))}}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockMark :: #force_inline proc(lock: LockCtx, loc := #caller_location) { LockMark :: #force_inline proc(lock: LockCtx, loc := #caller_location) {
when TRACY_ENABLE {
id := ___tracy_alloc_srcloc(u32(loc.line), _sl(loc.file_path), _sl(loc.procedure)) id := ___tracy_alloc_srcloc(u32(loc.line), _sl(loc.file_path), _sl(loc.procedure))
___tracy_mark_lockable_ctx(lock, (^___tracy_source_location_data)(uintptr(id))) ___tracy_mark_lockable_ctx(lock, (^___tracy_source_location_data)(uintptr(id)))
} }
}
@(disabled = !TRACY_ENABLE) @(disabled = !TRACY_ENABLE)
LockCustomName :: #force_inline proc( LockCustomName :: #force_inline proc(
lock: LockCtx, lock: LockCtx,
name: string, name: string,
) {___tracy_custom_name_lockable_ctx(lock, _sl(name))} ) {when TRACY_ENABLE {___tracy_custom_name_lockable_ctx(lock, _sl(name))}}
// Helper for passing cstring+length to Tracy functions. // Helper for passing cstring+length to Tracy functions.
@(private = "file") @(private = "file")

BIN
main_web/game.wasm.o Normal file

Binary file not shown.

View File

@ -5,24 +5,19 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Odin + Raylib on the web</title> <title>Odin + Raylib on the web</title>
<meta name="title" content="Odin + Raylib on the web"> <meta name="title" content="Odin + Raylib on the web">
<meta name="description" content="Make games using Odin + Raylib that work in the browser"> <meta name="description" content="Make games using Odin + Raylib that work in the browser">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<!-- Favicon -->
<link rel="shortcut icon" href="https://www.raylib.com/favicon.ico">
<!-- Web Style -->
<style> <style>
body { body {
margin: 0px; margin: 0px;
overflow: hidden; overflow: hidden;
background-color: black; background-color: black;
} }
canvas.emscripten { canvas.game_canvas {
border: 0px none; border: 0px none;
background-color: black;} background-color: black;
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
margin-left: auto; margin-left: auto;
@ -30,33 +25,73 @@
display: block; display: block;
} }
</style> </style>
<script type='text/javascript' src="https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/dist/FileSaver.min.js"> </script>
<script type='text/javascript'>
function saveFileFromMEMFSToDisk(memoryFSname, localFSname) // This can be called by C/C++ code
{
var isSafari = false; // Not supported, navigator.userAgent access is being restricted
//var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var data = FS.readFile(memoryFSname);
var blob;
if (isSafari) blob = new Blob([data.buffer], { type: "application/octet-stream" });
else blob = new Blob([data.buffer], { type: "application/octet-binary" });
// NOTE: SaveAsDialog is a browser setting. For example, in Google Chrome,
// in Settings/Advanced/Downloads section you have a setting:
// 'Ask where to save each file before downloading' - which you can set true/false.
// If you enable this setting it would always ask you and bring the SaveAsDialog
saveAs(blob, localFSname);
}
</script>
</head> </head>
<body> <body>
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas> <canvas class="game_canvas" id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1" onmousedown="event.target.focus()" onkeydown="event.preventDefault()"></canvas>
<p id="output" /> <script type="text/javascript" src="odin.js"></script>
<script> <script>
var odinMemoryInterface = new odin.WasmMemoryInterface();
odinMemoryInterface.setIntSize(4);
var odinImports = odin.setupDefaultImports(odinMemoryInterface);
// The Module is used as configuration for emscripten.
var Module = { var Module = {
// This is called by emscripten when it starts up.
instantiateWasm: (imports, successCallback) => {
const newImports = {
...odinImports,
...imports
}
return WebAssembly.instantiateStreaming(fetch("index.wasm"), newImports).then(function(output) {
var e = output.instance.exports;
odinMemoryInterface.setExports(e);
odinMemoryInterface.setMemory(e.memory);
return successCallback(output.instance);
});
},
// This happens a bit after `instantiateWasm`, when everything is
// done setting up. At that point we can run code.
onRuntimeInitialized: () => {
var e = wasmExports;
// Calls any procedure marked with @init
e._start();
// See source/main_web/main_web.odin for main_start,
// main_update and main_end.
e.main_start();
function send_resize() {
var canvas = document.getElementById('canvas');
e.web_window_size_changed(canvas.width, canvas.height);
}
window.addEventListener('resize', function(event) {
send_resize();
}, true);
// This can probably be done better: Ideally we'd feed the
// initial size to `main_start`. But there seems to be a
// race condition. `canvas` doesn't have it's correct size yet.
send_resize();
// Runs the "main loop".
function do_main_update() {
if (!e.main_update()) {
e.main_end();
// Calls procedures marked with @fini
e._end();
return;
}
window.requestAnimationFrame(do_main_update);
}
window.requestAnimationFrame(do_main_update);
},
print: (function() { print: (function() {
var element = document.getElementById('output'); var element = document.getElementById("output");
if (element) element.value = ''; // clear browser cache if (element) element.value = ''; // clear browser cache
return function(text) { return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
@ -68,11 +103,12 @@
}; };
})(), })(),
canvas: (function() { canvas: (function() {
var canvas = document.getElementById('canvas'); return document.getElementById("canvas");
return canvas;
})() })()
}; };
</script> </script>
<!-- Emscripten injects its javascript here -->
{{{ SCRIPT }}} {{{ SCRIPT }}}
</body> </body>
</html> </html>

View File

@ -1,37 +0,0 @@
#include <stdlib.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
extern void web_init();
extern void web_update();
extern void web_window_size_changed(int w, int h);
void update_window_size() {
double w, h;
emscripten_get_element_css_size("#canvas", &w, &h);
web_window_size_changed((int)w, (int)h);
}
static EM_BOOL on_web_display_size_changed(
int event_type,
const EmscriptenUiEvent *event,
void *user_data
) {
update_window_size();
return 0;
}
int main(void) {
emscripten_set_resize_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW,
0, 0, on_web_display_size_changed
);
web_init();
update_window_size();
emscripten_set_main_loop(web_update, 0, 1);
// We don't really "shutdown" the game, since web browser tabs just close.
return 0;
}

View File

@ -29,7 +29,7 @@ main_start :: proc "c" () {
web_context = context web_context = context
game.game_init_window({}) game.game_init_window({"."})
game.game_init() game.game_init()
} }

View File

@ -1,120 +0,0 @@
/*
This file implements logger and temp allocator for the web build. The logger
is based on the one found here: https://github.com/Aronicu/Raylib-WASM/tree/main
*/
#+build wasm32, wasm64p32
package main_web
import "base:runtime"
import "core:c"
import "core:fmt"
import "core:log"
import "core:strings"
// WASM logger
WASM_Logger_Opts :: log.Options{.Level, .Short_File_Path, .Line}
create_wasm_logger :: proc (lowest := log.Level.Debug, opt := WASM_Logger_Opts) -> log.Logger {
return log.Logger{data = nil, procedure = wasm_logger_proc, lowest_level = lowest, options = opt}
}
@(private="file")
wasm_logger_proc :: proc(
logger_data: rawptr,
level: log.Level,
text: string,
options: log.Options,
location := #caller_location
) {
b := strings.builder_make(context.temp_allocator)
strings.write_string(&b, Wasm_Logger_Level_Headers[level])
do_location_header(options, &b, location)
fmt.sbprint(&b, text)
puts(strings.to_cstring(&b))
}
@(private="file")
Wasm_Logger_Level_Headers := [?]string {
0 ..< 10 = "[DEBUG] --- ",
10 ..< 20 = "[INFO ] --- ",
20 ..< 30 = "[WARN ] --- ",
30 ..< 40 = "[ERROR] --- ",
40 ..< 50 = "[FATAL] --- ",
}
@(private="file")
do_location_header :: proc(opts: log.Options, buf: ^strings.Builder, location := #caller_location) {
if log.Location_Header_Opts & opts == nil {
return
}
fmt.sbprint(buf, "[")
file := location.file_path
if .Short_File_Path in opts {
last := 0
for r, i in location.file_path {
if r == '/' {
last = i + 1
}
}
file = location.file_path[last:]
}
if log.Location_File_Opts & opts != nil {
fmt.sbprint(buf, file)
}
if .Line in opts {
if log.Location_File_Opts & opts != nil {
fmt.sbprint(buf, ":")
}
fmt.sbprint(buf, location.line)
}
if .Procedure in opts {
if (log.Location_File_Opts | {.Line}) & opts != nil {
fmt.sbprint(buf, ":")
}
fmt.sbprintf(buf, "%s()", location.procedure)
}
fmt.sbprint(buf, "] ")
}
@(default_calling_convention = "c")
foreign {
puts :: proc(buffer: cstring) -> c.int ---
}
// Temp Allocator
// More or less a copy from base:runtime (that one is disabled in freestanding
// build mode).
WASM_Temp_Allocator :: struct {
arena: runtime.Arena,
}
wasm_temp_allocator_init :: proc(s: ^WASM_Temp_Allocator, size: int, backing_allocator := context.allocator) {
_ = runtime.arena_init(&s.arena, uint(size), backing_allocator)
}
wasm_temp_allocator_proc :: proc(
allocator_data: rawptr,
mode: runtime.Allocator_Mode,
size, alignment: int,
old_memory: rawptr,
old_size: int,
loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
s := (^WASM_Temp_Allocator)(allocator_data)
return runtime.arena_allocator_proc(&s.arena, mode, size, alignment, old_memory, old_size, loc)
}
wasm_temp_allocator :: proc(allocator: ^WASM_Temp_Allocator) -> runtime.Allocator {
return runtime.Allocator{
procedure = wasm_temp_allocator_proc,
data = allocator,
}
}